changeset 1660:4a5c79e31b60 db-changes

integration mainline->db-changes
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 30 Sep 2015 13:23:31 +0200
parents 61ce8147f30d (current diff) 87a606265de8 (diff)
children 1a80777ad462
files Core/Compression/BufferCompressor.cpp Core/Compression/BufferCompressor.h Core/FileStorage/CompressedFileStorageAccessor.cpp Core/FileStorage/CompressedFileStorageAccessor.h Core/FileStorage/FileStorageAccessor.cpp Core/FileStorage/FileStorageAccessor.h Core/HttpServer/HttpHandler.cpp Core/HttpServer/HttpHandler.h Core/ImageFormats/ImageAccessor.cpp Core/ImageFormats/ImageAccessor.h Core/ImageFormats/ImageBuffer.cpp Core/ImageFormats/ImageBuffer.h Core/ImageFormats/ImageProcessing.cpp Core/ImageFormats/ImageProcessing.h Core/ImageFormats/PngReader.cpp Core/ImageFormats/PngReader.h Core/ImageFormats/PngWriter.cpp Core/ImageFormats/PngWriter.h Core/Lua/LuaException.h Core/MultiThreading/ArrayFilledByThreads.cpp Core/MultiThreading/ArrayFilledByThreads.h Core/MultiThreading/ThreadedCommandProcessor.cpp Core/MultiThreading/ThreadedCommandProcessor.h Core/OrthancException.cpp OrthancCppClient/Instance.cpp OrthancCppClient/Instance.h OrthancCppClient/OrthancClientException.h OrthancCppClient/OrthancConnection.cpp OrthancCppClient/OrthancConnection.h OrthancCppClient/OrthancCppClient.cpp OrthancCppClient/Patient.cpp OrthancCppClient/Patient.h OrthancCppClient/Series.cpp OrthancCppClient/Series.h OrthancCppClient/SharedLibrary/AUTOGENERATED/ExternC.cpp OrthancCppClient/SharedLibrary/AUTOGENERATED/OrthancCppClient.h OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows32.def OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows32.rc OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows64.def OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows64.rc OrthancCppClient/SharedLibrary/ConfigurationCpp.json OrthancCppClient/SharedLibrary/Generate.sh OrthancCppClient/SharedLibrary/Laaw/VersionScript.map OrthancCppClient/SharedLibrary/Laaw/laaw/laaw-exports.h OrthancCppClient/SharedLibrary/Laaw/laaw/laaw.h OrthancCppClient/SharedLibrary/Product.json OrthancCppClient/SharedLibrary/SharedLibrary.cpp OrthancCppClient/Study.cpp OrthancCppClient/Study.h OrthancExplorer/libs/images/ajax-loader.gif OrthancExplorer/libs/images/ajax-loader.png OrthancExplorer/libs/images/ajax-loader2.gif OrthancExplorer/libs/jquery-1.7.2.min.js OrthancExplorer/libs/jquery.mobile-1.1.0.min.css OrthancExplorer/libs/jquery.mobile-1.1.0.min.js OrthancExplorer/libs/jquery.mobile.structure-1.1.0.min.css OrthancExplorer/libs/jquery.mobile.theme-1.1.0.min.css OrthancServer/IServerIndexListener.h Plugins/Include/OrthancCDatabasePlugin.h Plugins/Include/OrthancCPlugin.h Plugins/Include/OrthancCppDatabasePlugin.h Resources/MinGW64Toolchain.cmake Resources/OrthancClient.doxygen Resources/Patches/boost-1.55.0-clang-atomic.patch Resources/Samples/OrthancClient/Basic/CMakeLists.txt Resources/Samples/OrthancClient/Basic/main.cpp Resources/Samples/OrthancClient/Vtk/CMakeLists.txt Resources/Samples/OrthancClient/Vtk/main.cpp UnitTestsSources/PngTests.cpp
diffstat 380 files changed, 28495 insertions(+), 21374 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Wed Feb 11 10:40:08 2015 +0100
+++ b/CMakeLists.txt	Wed Sep 30 13:23:31 2015 +0200
@@ -5,6 +5,14 @@
 # Version of the build, should always be "mainline" except in release branches
 set(ORTHANC_VERSION "mainline")
 
+# Version of the database schema. History:
+#   * Orthanc 0.1.0 -> Orthanc 0.3.0 = no versioning
+#   * Orthanc 0.3.1                  = version 2
+#   * Orthanc 0.4.0 -> Orthanc 0.7.2 = version 3
+#   * Orthanc 0.7.3 -> Orthanc 0.8.4 = version 4
+#   * Orthanc 0.8.5 -> mainline      = version 5
+set(ORTHANC_DATABASE_VERSION 5)
+
 
 #####################################################################
 ## CMake parameters tunable at the command line
@@ -14,12 +22,14 @@
 SET(STATIC_BUILD OFF CACHE BOOL "Static build of the third-party libraries (necessary for Windows)")
 SET(STANDALONE_BUILD ON CACHE BOOL "Standalone build (all the resources are embedded, necessary for releases)")
 SET(ENABLE_SSL ON CACHE BOOL "Include support for SSL")
-SET(BUILD_CLIENT_LIBRARY ON CACHE BOOL "Build the client library")
 SET(DCMTK_DICTIONARY_DIR "" CACHE PATH "Directory containing the DCMTK dictionaries \"dicom.dic\" and \"private.dic\" (only when using system version of DCMTK)") 
 SET(ALLOW_DOWNLOADS OFF CACHE BOOL "Allow CMake to download packages")
 SET(UNIT_TESTS_WITH_HTTP_CONNEXIONS ON CACHE BOOL "Allow unit tests to make HTTP requests")
+SET(ENABLE_GOOGLE_LOG OFF CACHE BOOL "Enable Google Log (otherwise, an internal logger is used)")
 SET(ENABLE_JPEG ON CACHE BOOL "Enable JPEG decompression")
 SET(ENABLE_JPEG_LOSSLESS ON CACHE BOOL "Enable JPEG-LS (Lossless) decompression")
+SET(ENABLE_PLUGINS ON CACHE BOOL "Enable plugins")
+SET(BUILD_SERVE_FOLDERS ON CACHE BOOL "Build the ServeFolders plugin")
 
 # Advanced parameters to fine-tune linking against system libraries
 SET(USE_SYSTEM_JSONCPP ON CACHE BOOL "Use the system version of JsonCpp")
@@ -30,7 +40,8 @@
 SET(USE_SYSTEM_LUA ON CACHE BOOL "Use the system version of Lua")
 SET(USE_SYSTEM_DCMTK ON CACHE BOOL "Use the system version of DCMTK")
 SET(USE_SYSTEM_BOOST ON CACHE BOOL "Use the system version of Boost")
-SET(USE_SYSTEM_LIBPNG ON CACHE BOOL "Use the system version of LibPng")
+SET(USE_SYSTEM_LIBPNG ON CACHE BOOL "Use the system version of libpng")
+SET(USE_SYSTEM_LIBJPEG ON CACHE BOOL "Use the system version of libjpeg")
 SET(USE_SYSTEM_CURL ON CACHE BOOL "Use the system version of LibCurl")
 SET(USE_SYSTEM_OPENSSL ON CACHE BOOL "Use the system version of OpenSSL")
 SET(USE_SYSTEM_ZLIB ON CACHE BOOL "Use the system version of ZLib")
@@ -41,9 +52,13 @@
 
 # Distribution-specific settings
 SET(USE_GTEST_DEBIAN_SOURCE_PACKAGE OFF CACHE BOOL "Use the sources of Google Test shipped with libgtest-dev (Debian only)")
+SET(SYSTEM_MONGOOSE_USE_CALLBACKS ON CACHE BOOL "The system version of Mongoose uses callbacks (version >= 3.7)")
+SET(USE_BOOST_ICONV ON CACHE BOOL "Use iconv instead of wconv (Windows only)")
+
 mark_as_advanced(USE_GTEST_DEBIAN_SOURCE_PACKAGE)
-SET(SYSTEM_MONGOOSE_USE_CALLBACKS ON CACHE BOOL "The system version of Mongoose uses callbacks (version >= 3.7)")
 mark_as_advanced(SYSTEM_MONGOOSE_USE_CALLBACKS)
+mark_as_advanced(USE_BOOST_ICONV)
+mark_as_advanced(USE_PUGIXML)
 
 # Path to the root folder of the Orthanc distribution
 set(ORTHANC_ROOT ${CMAKE_SOURCE_DIR})
@@ -52,6 +67,7 @@
 include(CheckIncludeFiles)
 include(CheckIncludeFileCXX)
 include(CheckLibraryExists)
+include(FindPythonInterp)
 include(${CMAKE_SOURCE_DIR}/Resources/CMake/AutoGeneratedCode.cmake)
 include(${CMAKE_SOURCE_DIR}/Resources/CMake/DownloadPackage.cmake)
 include(${CMAKE_SOURCE_DIR}/Resources/CMake/Compiler.cmake)
@@ -66,12 +82,13 @@
 
 set(ORTHANC_CORE_SOURCES
   Core/Cache/MemoryCache.cpp
+  Core/Cache/SharedArchive.cpp
   Core/ChunkedBuffer.cpp
-  Core/Compression/BufferCompressor.cpp
+  Core/Compression/DeflateBaseCompressor.cpp
+  Core/Compression/GzipCompressor.cpp
+  Core/Compression/HierarchicalZipWriter.cpp
+  Core/Compression/ZipWriter.cpp
   Core/Compression/ZlibCompressor.cpp
-  Core/Compression/ZipWriter.cpp
-  Core/Compression/HierarchicalZipWriter.cpp
-  Core/OrthancException.cpp
   Core/DicomFormat/DicomArray.cpp
   Core/DicomFormat/DicomMap.cpp
   Core/DicomFormat/DicomTag.cpp
@@ -81,34 +98,39 @@
   Core/Enumerations.cpp
   Core/FileStorage/FilesystemStorage.cpp
   Core/FileStorage/StorageAccessor.cpp
-  Core/FileStorage/CompressedFileStorageAccessor.cpp
-  Core/FileStorage/FileStorageAccessor.cpp
   Core/HttpClient.cpp
+  Core/HttpServer/BufferHttpSender.cpp
   Core/HttpServer/EmbeddedResourceHttpHandler.cpp
   Core/HttpServer/FilesystemHttpHandler.cpp
-  Core/HttpServer/HttpHandler.cpp
+  Core/HttpServer/HttpToolbox.cpp
   Core/HttpServer/HttpOutput.cpp
+  Core/HttpServer/StringHttpOutput.cpp
   Core/HttpServer/MongooseServer.cpp
   Core/HttpServer/HttpFileSender.cpp
   Core/HttpServer/FilesystemHttpSender.cpp
+  Core/HttpServer/HttpStreamTranscoder.cpp
+  Core/Logging.cpp
   Core/RestApi/RestApiCall.cpp
   Core/RestApi/RestApiGetCall.cpp
   Core/RestApi/RestApiHierarchy.cpp
   Core/RestApi/RestApiPath.cpp
   Core/RestApi/RestApiOutput.cpp
   Core/RestApi/RestApi.cpp
-  Core/MultiThreading/ArrayFilledByThreads.cpp
   Core/MultiThreading/BagOfRunnablesBySteps.cpp
   Core/MultiThreading/Mutex.cpp
   Core/MultiThreading/ReaderWriterLock.cpp
   Core/MultiThreading/Semaphore.cpp
   Core/MultiThreading/SharedMessageQueue.cpp
-  Core/MultiThreading/ThreadedCommandProcessor.cpp
-  Core/ImageFormats/ImageAccessor.cpp
-  Core/ImageFormats/ImageBuffer.cpp
-  Core/ImageFormats/ImageProcessing.cpp
-  Core/ImageFormats/PngReader.cpp
-  Core/ImageFormats/PngWriter.cpp
+  Core/Images/Font.cpp
+  Core/Images/FontRegistry.cpp
+  Core/Images/ImageAccessor.cpp
+  Core/Images/ImageBuffer.cpp
+  Core/Images/ImageProcessing.cpp
+  Core/Images/JpegErrorManager.cpp
+  Core/Images/JpegReader.cpp
+  Core/Images/JpegWriter.cpp
+  Core/Images/PngReader.cpp
+  Core/Images/PngWriter.cpp
   Core/SQLite/Connection.cpp
   Core/SQLite/FunctionContext.cpp
   Core/SQLite/Statement.cpp
@@ -119,17 +141,6 @@
   Core/Uuid.cpp
   Core/Lua/LuaContext.cpp
   Core/Lua/LuaFunctionCall.cpp
-
-  OrthancCppClient/OrthancConnection.cpp
-  OrthancCppClient/Study.cpp
-  OrthancCppClient/Series.cpp
-  OrthancCppClient/Instance.cpp
-  OrthancCppClient/Patient.cpp
-
-  Plugins/Engine/SharedLibrary.cpp
-  Plugins/Engine/PluginsManager.cpp
-  Plugins/Engine/OrthancPlugins.cpp
-  Plugins/Engine/OrthancPluginDatabase.cpp
   )
 
 
@@ -166,6 +177,11 @@
   OrthancServer/OrthancFindRequestHandler.cpp
   OrthancServer/OrthancMoveRequestHandler.cpp
   OrthancServer/ExportedResource.cpp
+  OrthancServer/ResourceFinder.cpp
+  OrthancServer/DicomFindQuery.cpp
+  OrthancServer/QueryRetrieveHandler.cpp
+  OrthancServer/LuaScripting.cpp
+  OrthancServer/OrthancHttpHandler.cpp
 
   # From "lua-scripting" branch
   OrthancServer/DicomInstanceToStore.cpp
@@ -185,7 +201,7 @@
   UnitTestsSources/FileStorageTests.cpp
   UnitTestsSources/FromDcmtkTests.cpp
   UnitTestsSources/MemoryCacheTests.cpp
-  UnitTestsSources/PngTests.cpp
+  UnitTestsSources/ImageTests.cpp
   UnitTestsSources/RestApiTests.cpp
   UnitTestsSources/SQLiteTests.cpp
   UnitTestsSources/SQLiteChromiumTests.cpp
@@ -197,10 +213,26 @@
   UnitTestsSources/UnitTestsMain.cpp
   UnitTestsSources/ImageProcessingTests.cpp
   UnitTestsSources/JpegLosslessTests.cpp
-  UnitTestsSources/PluginsTests.cpp
+  UnitTestsSources/StreamTests.cpp
   )
 
 
+if (ENABLE_PLUGINS)
+  list(APPEND ORTHANC_SERVER_SOURCES
+    Plugins/Engine/OrthancPluginDatabase.cpp
+    Plugins/Engine/OrthancPlugins.cpp
+    Plugins/Engine/PluginsEnumerations.cpp
+    Plugins/Engine/PluginsErrorDictionary.cpp
+    Plugins/Engine/PluginsManager.cpp
+    Plugins/Engine/SharedLibrary.cpp
+    )
+
+  list(APPEND ORTHANC_UNIT_TESTS_SOURCES
+    UnitTestsSources/PluginsTests.cpp
+    )
+endif()
+
+
 set(ORTHANC_EMBEDDED_FILES
   PREPARE_DATABASE            ${CMAKE_CURRENT_SOURCE_DIR}/OrthancServer/PrepareDatabase.sql
   UPGRADE_DATABASE_3_TO_4     ${CMAKE_CURRENT_SOURCE_DIR}/OrthancServer/Upgrade3To4.sql
@@ -208,6 +240,7 @@
   CONFIGURATION_SAMPLE        ${CMAKE_CURRENT_SOURCE_DIR}/Resources/Configuration.json
   DICOM_CONFORMANCE_STATEMENT ${CMAKE_CURRENT_SOURCE_DIR}/Resources/DicomConformanceStatement.txt
   LUA_TOOLBOX                 ${CMAKE_CURRENT_SOURCE_DIR}/Resources/Toolbox.lua
+  FONT_UBUNTU_MONO_BOLD_16    ${CMAKE_CURRENT_SOURCE_DIR}/Resources/Fonts/UbuntuMonoBold-16.json
   )
 
 
@@ -216,32 +249,28 @@
 ## Inclusion of third-party dependencies
 #####################################################################
 
-# Configuration of the standalone builds
-if (CMAKE_CROSSCOMPILING)
-  # Cross-compilation implies the standalone build
-  SET(STANDALONE_BUILD ON)
+if (ENABLE_GOOGLE_LOG)
+  include(${CMAKE_SOURCE_DIR}/Resources/CMake/GoogleLogConfiguration.cmake)
 endif()
 
-# Prepare the third-party dependencies
-SET(THIRD_PARTY_SOURCES
-  ${CMAKE_SOURCE_DIR}/Resources/ThirdParty/md5/md5.c
-  ${CMAKE_SOURCE_DIR}/Resources/ThirdParty/base64/base64.cpp
-  )
+include(${CMAKE_SOURCE_DIR}/Resources/CMake/JsonCppConfiguration.cmake)
+include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibCurlConfiguration.cmake)
+include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibPngConfiguration.cmake)
+include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibJpegConfiguration.cmake)
+include(${CMAKE_SOURCE_DIR}/Resources/CMake/LuaConfiguration.cmake)
+include(${CMAKE_SOURCE_DIR}/Resources/CMake/MongooseConfiguration.cmake)
+include(${CMAKE_SOURCE_DIR}/Resources/CMake/PugixmlConfiguration.cmake)
+include(${CMAKE_SOURCE_DIR}/Resources/CMake/SQLiteConfiguration.cmake)
+include(${CMAKE_SOURCE_DIR}/Resources/CMake/ZlibConfiguration.cmake)
 
-include(${CMAKE_SOURCE_DIR}/Resources/CMake/GoogleLogConfiguration.cmake)
+# These are the two most heavyweight dependencies. We put them as the
+# last includes to quickly spot problems when configuring static
+# builds.
 include(${CMAKE_SOURCE_DIR}/Resources/CMake/BoostConfiguration.cmake)
 include(${CMAKE_SOURCE_DIR}/Resources/CMake/DcmtkConfiguration.cmake)
-include(${CMAKE_SOURCE_DIR}/Resources/CMake/MongooseConfiguration.cmake)
-include(${CMAKE_SOURCE_DIR}/Resources/CMake/ZlibConfiguration.cmake)
-include(${CMAKE_SOURCE_DIR}/Resources/CMake/SQLiteConfiguration.cmake)
-include(${CMAKE_SOURCE_DIR}/Resources/CMake/JsonCppConfiguration.cmake)
-include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibPngConfiguration.cmake)
-include(${CMAKE_SOURCE_DIR}/Resources/CMake/LuaConfiguration.cmake)
-include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibCurlConfiguration.cmake)
-include(${CMAKE_SOURCE_DIR}/Resources/CMake/PugixmlConfiguration.cmake)
 
 
-if (${ENABLE_SSL})
+if (ENABLE_SSL)
   add_definitions(-DORTHANC_SSL_ENABLED=1)
   include(${CMAKE_SOURCE_DIR}/Resources/CMake/OpenSslConfiguration.cmake)
 else()
@@ -263,12 +292,19 @@
 endif()
 
 
+if (ENABLE_PLUGINS)
+  add_definitions(-DORTHANC_PLUGINS_ENABLED=1)
+else()
+  add_definitions(-DORTHANC_PLUGINS_ENABLED=0)
+endif()
+
+
 
 #####################################################################
 ## Autogeneration of files
 #####################################################################
 
-if (${STANDALONE_BUILD})
+if (STANDALONE_BUILD)
   # We embed all the resources in the binaries for standalone builds
   add_definitions(-DORTHANC_STANDALONE=1)
   EmbedResources(
@@ -286,6 +322,22 @@
     )
 endif()
 
+if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
+  execute_process(
+    COMMAND 
+    ${PYTHON_EXECUTABLE} ${ORTHANC_ROOT}/Resources/WindowsResources.py
+    ${ORTHANC_VERSION} Orthanc Orthanc.exe "Lightweight, RESTful DICOM server for medical imaging"
+    ERROR_VARIABLE Failure
+    OUTPUT_FILE ${AUTOGENERATED_DIR}/Orthanc.rc
+    )
+
+  if (Failure)
+    message(FATAL_ERROR "Error while computing the version information: ${Failure}")
+  endif()
+
+  list(APPEND ORTHANC_RESOURCES ${AUTOGENERATED_DIR}/Orthanc.rc)
+endif()
+
 
 
 #####################################################################
@@ -293,7 +345,7 @@
 #####################################################################
 
 # Setup precompiled headers for Microsoft Visual Studio
-if (${MSVC})
+if (MSVC)
   add_definitions(-DORTHANC_USE_PRECOMPILED_HEADERS=1)
 
   ADD_VISUAL_STUDIO_PRECOMPILED_HEADERS(
@@ -309,6 +361,8 @@
 
 add_definitions(
   -DORTHANC_VERSION="${ORTHANC_VERSION}"
+  -DORTHANC_DATABASE_VERSION=${ORTHANC_DATABASE_VERSION}
+  -DORTHANC_ENABLE_LOGGING=1
   )
 
 list(LENGTH OPENSSL_SOURCES OPENSSL_SOURCES_LENGTH)
@@ -318,13 +372,31 @@
 
 add_library(CoreLibrary
   STATIC
+  ${ORTHANC_CORE_SOURCES}
   ${AUTOGENERATED_SOURCES}
-  ${THIRD_PARTY_SOURCES}
+
+  ${BOOST_SOURCES}
   ${CURL_SOURCES}
-  ${ORTHANC_CORE_SOURCES}
+  ${GOOGLE_LOG_SOURCES}
+  ${JSONCPP_SOURCES}
+  ${LIBPNG_SOURCES}
+  ${LIBJPEG_SOURCES}
+  ${LUA_SOURCES}
+  ${MONGOOSE_SOURCES}
+  ${PUGIXML_SOURCES}
+  ${SQLITE_SOURCES}
+  ${ZLIB_SOURCES}
+
+  ${CMAKE_SOURCE_DIR}/Resources/ThirdParty/md5/md5.c
+  ${CMAKE_SOURCE_DIR}/Resources/ThirdParty/base64/base64.cpp
+
+  # This is the minizip distribution to create ZIP files using zlib
+  ${ORTHANC_ROOT}/Resources/ThirdParty/minizip/ioapi.c
+  ${ORTHANC_ROOT}/Resources/ThirdParty/minizip/zip.c
   )  
 
 
+
 #####################################################################
 ## Build the Orthanc server
 #####################################################################
@@ -340,9 +412,10 @@
 
 add_executable(Orthanc
   OrthancServer/main.cpp
+  ${ORTHANC_RESOURCES}
   )
 
-target_link_libraries(Orthanc ServerLibrary CoreLibrary ${STATIC_LUA} ${STATIC_GOOGLE_LOG})
+target_link_libraries(Orthanc ServerLibrary CoreLibrary ${DCMTK_LIBRARIES})
 
 if (${OPENSSL_SOURCES_LENGTH} GREATER 0)
   target_link_libraries(Orthanc OpenSSL)
@@ -365,13 +438,16 @@
   add_definitions(-DUNIT_TESTS_WITH_HTTP_CONNEXIONS=0)
 endif()
 
-add_definitions(-DORTHANC_BUILD_UNIT_TESTS=1)
+add_definitions(
+  -DORTHANC_BUILD_UNIT_TESTS=1
+  )
+
 include(${CMAKE_SOURCE_DIR}/Resources/CMake/GoogleTestConfiguration.cmake)
 add_executable(UnitTests
   ${GTEST_SOURCES}
   ${ORTHANC_UNIT_TESTS_SOURCES}
   )
-target_link_libraries(UnitTests ServerLibrary CoreLibrary ${STATIC_LUA} ${STATIC_GOOGLE_LOG})
+target_link_libraries(UnitTests ServerLibrary CoreLibrary ${DCMTK_LIBRARIES})
 
 if (${OPENSSL_SOURCES_LENGTH} GREATER 0)
   target_link_libraries(UnitTests OpenSSL)
@@ -380,112 +456,47 @@
 
 
 #####################################################################
-## Create the standalone DLL containing the Orthanc Client API
+## Build the "ServeFolders" plugin
 #####################################################################
 
-if (BUILD_CLIENT_LIBRARY)
-  include_directories(${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/Laaw)
-
-  if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
-    if (CMAKE_CROSSCOMPILING)
-      # Remove the default "lib" prefix from "libOrthancClient.dll" if cross-compiling
-      set(CMAKE_SHARED_LIBRARY_PREFIX "")
+if (ENABLE_PLUGINS AND BUILD_SERVE_FOLDERS)
+  execute_process(
+    COMMAND 
+    ${PYTHON_EXECUTABLE} ${ORTHANC_ROOT}/Resources/WindowsResources.py
+    ${ORTHANC_VERSION} ServeFolders ServeFolders.dll "Orthanc plugin to serve additional folders"
+    ERROR_VARIABLE Failure
+    OUTPUT_FILE ${AUTOGENERATED_DIR}/ServeFolders.rc
+    )
 
-      if (${CMAKE_SIZEOF_VOID_P} EQUAL 4)
-        set(ORTHANC_CPP_CLIENT_AUX ${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows32.def)
-      elseif (${CMAKE_SIZEOF_VOID_P} EQUAL 8)
-        set(ORTHANC_CPP_CLIENT_AUX ${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows64.def)
-      else()
-        message(FATAL_ERROR "Support your platform here")
-      endif()
-    else()
-      # Nothing to do if using Visual Studio
-    endif()
-
-    if (${CMAKE_SIZEOF_VOID_P} EQUAL 4)
-      set(CMAKE_SHARED_LIBRARY_SUFFIX "_Windows32.dll")
-      list(APPEND ORTHANC_CPP_CLIENT_AUX ${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows32.rc)
-    elseif (${CMAKE_SIZEOF_VOID_P} EQUAL 8)
-      set(CMAKE_SHARED_LIBRARY_SUFFIX "_Windows64.dll")
-      list(APPEND ORTHANC_CPP_CLIENT_AUX ${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows64.rc)
-    else()
-      message(FATAL_ERROR "Support your platform here")
-    endif()    
-
-  else()
-    set(ORTHANC_CPP_CLIENT_AUX ${OPENSSL_SOURCES})
+  if (Failure)
+    message(FATAL_ERROR "Error while computing the version information: ${Failure}")
   endif()
 
-  add_library(OrthancClient SHARED
-    ${ORTHANC_ROOT}/OrthancCppClient/OrthancCppClient.cpp
-    ${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/SharedLibrary.cpp
-    ${ORTHANC_ROOT}/Resources/ThirdParty/md5/md5.c
-    ${ORTHANC_ROOT}/Resources/ThirdParty/base64/base64.cpp
-    ${ORTHANC_CPP_CLIENT_AUX}
-    ${THIRD_PARTY_SOURCES}
-    ${CURL_SOURCES}
-    ${GOOGLE_LOG_SOURCES}
+  add_definitions(-DSERVE_FOLDERS_VERSION="${ORTHANC_VERSION}")
+
+  include_directories(${CMAKE_SOURCE_DIR}/Plugins/Include)
+
+  add_library(ServeFolders SHARED 
+    ${BOOST_SOURCES}
+    ${JSONCPP_SOURCES}
+    Plugins/Samples/ServeFolders/Plugin.cpp
+    ${AUTOGENERATED_DIR}/ServeFolders.rc
     )
 
-  if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
-      ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
-    set_target_properties(OrthancClient
-      PROPERTIES LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined -Wl,--as-needed -Wl,--version-script=${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/Laaw/VersionScript.map"
-      )
-    target_link_libraries(OrthancClient pthread)
-
-  elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
-    target_link_libraries(OrthancClient OpenSSL ws2_32)
-
-    if (CMAKE_CROSSCOMPILING)
-      set_target_properties(OrthancClient
-        PROPERTIES LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++"
-        )
-    endif()
-
-  elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
-    # TODO
-    target_link_libraries(OrthancClient pthread)
-
-  else()
-    message(FATAL_ERROR "Support your platform here")
-  endif()
-
-
-  # Set the version of the "Orthanc Client" shared library
-  file(STRINGS
-    ${CMAKE_SOURCE_DIR}/OrthancCppClient/SharedLibrary/Product.json
-    ORTHANC_CLIENT_VERSION_TMP
-    REGEX "^[ \t]*\"Version\"[ \t]*")
-
-  string(REGEX REPLACE "^.*\"([0-9]+)\\.([0-9]+)\\.([0-9]+)\"" "\\1.\\2" 
-    ORTHANC_CLIENT_VERSION ${ORTHANC_CLIENT_VERSION_TMP})
-
-  message("Setting the version of the library to ${ORTHANC_CLIENT_VERSION}")
-
-  set_target_properties(OrthancClient PROPERTIES 
-    VERSION ${ORTHANC_CLIENT_VERSION} 
-    SOVERSION ${ORTHANC_CLIENT_VERSION})
-
-
-  install(
-    TARGETS OrthancClient
-    RUNTIME DESTINATION lib    # Destination for Windows
-    LIBRARY DESTINATION lib    # Destination for Linux
+  set_target_properties(
+    ServeFolders PROPERTIES 
+    VERSION ${ORTHANC_VERSION} 
+    SOVERSION ${ORTHANC_VERSION}
     )
 
   install(
-    FILES
-    ${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/AUTOGENERATED/OrthancCppClient.h 
-    ${ORTHANC_ROOT}/Plugins/Include/OrthancCPlugin.h 
-    ${ORTHANC_ROOT}/Plugins/Include/OrthancCDatabasePlugin.h 
-    ${ORTHANC_ROOT}/Plugins/Include/OrthancCppDatabasePlugin.h 
-    DESTINATION include/orthanc
+    TARGETS ServeFolders
+    RUNTIME DESTINATION lib    # Destination for Windows
+    LIBRARY DESTINATION share/orthanc/plugins    # Destination for Linux
     )
 endif()
 
 
-        
 
 #####################################################################
 ## Generate the documentation if Doxygen is present
@@ -519,31 +530,28 @@
     DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/OrthancPluginDocumentation/doc/
     DESTINATION share/doc/orthanc/OrthancPlugin
     )
-
-  if (BUILD_CLIENT_LIBRARY)
-    configure_file(
-      ${CMAKE_SOURCE_DIR}/Resources/OrthancClient.doxygen
-      ${CMAKE_CURRENT_BINARY_DIR}/OrthancClient.doxygen
-      @ONLY)
-
-    add_custom_command(TARGET OrthancClient 
-      POST_BUILD
-      COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/OrthancClient.doxygen
-      WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
-      COMMENT "Generating client documentation with Doxygen" VERBATIM
-      )
-
-    install(
-      DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/OrthancClientDocumentation/doc/
-      DESTINATION share/doc/orthanc/OrthancClient
-      )
-  endif()
-
 else()
   message("Doxygen not found. The documentation will not be built.")
 endif()
 
 
+
+#####################################################################
+## Install the plugin SDK
+#####################################################################
+
+if (ENABLE_PLUGINS)
+  install(
+    FILES
+    Plugins/Include/orthanc/OrthancCPlugin.h 
+    Plugins/Include/orthanc/OrthancCDatabasePlugin.h 
+    Plugins/Include/orthanc/OrthancCppDatabasePlugin.h 
+    DESTINATION include/orthanc
+    )
+endif()
+
+
+
 #####################################################################
 ## Prepare the "uninstall" target
 ## http://www.cmake.org/Wiki/CMake_FAQ#Can_I_do_.22make_uninstall.22_with_CMake.3F
--- a/Core/Cache/MemoryCache.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/Cache/MemoryCache.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,9 +33,7 @@
 #include "../PrecompiledHeaders.h"
 #include "MemoryCache.h"
 
-#include <stdlib.h>  // This fixes a problem in glog for recent
-                     // releases of MinGW
-#include <glog/logging.h>
+#include "../Logging.h"
 
 namespace Orthanc
 {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Cache/SharedArchive.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,134 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "SharedArchive.h"
+
+
+#include "../Uuid.h"
+
+
+namespace Orthanc
+{
+  void SharedArchive::RemoveInternal(const std::string& id)
+  {
+    Archive::iterator it = archive_.find(id);
+
+    if (it != archive_.end())
+    {
+      delete it->second;
+      archive_.erase(it);
+    }
+  }
+
+
+  SharedArchive::Accessor::Accessor(SharedArchive& that,
+                                    const std::string& id) :
+    lock_(that.mutex_)
+  {
+    Archive::iterator it = that.archive_.find(id);
+
+    if (it == that.archive_.end())
+    {
+      throw OrthancException(ErrorCode_InexistentItem);
+    }
+    else
+    {
+      that.lru_.MakeMostRecent(id);
+      item_ = it->second;
+    }
+  }
+
+
+  SharedArchive::SharedArchive(size_t maxSize) : 
+    maxSize_(maxSize)
+  {
+    if (maxSize == 0)
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
+  SharedArchive::~SharedArchive()
+  {
+    for (Archive::iterator it = archive_.begin();
+         it != archive_.end(); ++it)
+    {
+      delete it->second;
+    }
+  }
+
+
+  std::string SharedArchive::Add(IDynamicObject* obj)
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+
+    if (archive_.size() == maxSize_)
+    {
+      // The quota has been reached, remove the oldest element
+      std::string oldest = lru_.RemoveOldest();
+      RemoveInternal(oldest);
+    }
+
+    std::string id = Toolbox::GenerateUuid();
+    RemoveInternal(id);  // Should never be useful because of UUID
+    archive_[id] = obj;
+    lru_.Add(id);
+
+    return id;
+  }
+
+
+  void SharedArchive::Remove(const std::string& id)
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+    RemoveInternal(id);      
+    lru_.Invalidate(id);
+  }
+
+
+  void SharedArchive::List(std::list<std::string>& items)
+  {
+    items.clear();
+
+    boost::mutex::scoped_lock lock(mutex_);
+
+    for (Archive::const_iterator it = archive_.begin();
+         it != archive_.end(); ++it)
+    {
+      items.push_back(it->first);
+    }
+  }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Cache/SharedArchive.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,85 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "LeastRecentlyUsedIndex.h"
+#include "../IDynamicObject.h"
+
+#include <map>
+#include <boost/thread.hpp>
+
+namespace Orthanc
+{
+  class SharedArchive : public boost::noncopyable
+  {
+  private:
+    typedef std::map<std::string, IDynamicObject*>  Archive;
+
+    size_t         maxSize_;
+    boost::mutex   mutex_;
+    Archive        archive_;
+    Orthanc::LeastRecentlyUsedIndex<std::string> lru_;
+
+    void RemoveInternal(const std::string& id);
+
+  public:
+    class Accessor : public boost::noncopyable
+    {
+    private:
+      boost::mutex::scoped_lock  lock_;
+      IDynamicObject*            item_;
+
+    public:
+      Accessor(SharedArchive& that,
+               const std::string& id);
+
+      IDynamicObject& GetItem() const
+      {
+        return *item_;
+      }      
+    };
+
+
+    SharedArchive(size_t maxSize);
+
+    ~SharedArchive();
+
+    std::string Add(IDynamicObject* obj);  // Takes the ownership
+
+    void Remove(const std::string& id);
+
+    void List(std::list<std::string>& items);
+  };
+}
+
+
--- a/Core/Compression/BufferCompressor.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "../PrecompiledHeaders.h"
-#include "BufferCompressor.h"
-
-namespace Orthanc
-{
-  void BufferCompressor::Compress(std::string& output,
-                                  const std::vector<uint8_t>& input)
-  {
-    if (input.size() > 0)
-      Compress(output, &input[0], input.size());
-    else
-      Compress(output, NULL, 0);
-  }
-
-  void BufferCompressor::Uncompress(std::string& output,
-                                    const std::vector<uint8_t>& input)
-  {
-    if (input.size() > 0)
-      Uncompress(output, &input[0], input.size());
-    else
-      Uncompress(output, NULL, 0);
-  }
-
-  void BufferCompressor::Compress(std::string& output,
-                                  const std::string& input)
-  {
-    if (input.size() > 0)
-      Compress(output, &input[0], input.size());
-    else
-      Compress(output, NULL, 0);
-  }
-
-  void BufferCompressor::Uncompress(std::string& output,
-                                    const std::string& input)
-  {
-    if (input.size() > 0)
-      Uncompress(output, &input[0], input.size());
-    else
-      Uncompress(output, NULL, 0);
-  }
-}
--- a/Core/Compression/BufferCompressor.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include <string>
-#include <cstddef>
-#include <stdint.h>
-#include <vector>
-
-namespace Orthanc
-{
-  class BufferCompressor
-  {
-  public:
-    virtual ~BufferCompressor()
-    {
-    }
-
-    virtual void Compress(std::string& compressed,
-                          const void* uncompressed,
-                          size_t uncompressedSize) = 0;
-
-    virtual void Uncompress(std::string& uncompressed,
-                            const void* compressed,
-                            size_t compressedSize) = 0;
-
-    virtual void Compress(std::string& compressed,
-                          const std::vector<uint8_t>& uncompressed);
-
-    virtual void Uncompress(std::string& uncompressed,
-                            const std::vector<uint8_t>& compressed);
-
-    virtual void Compress(std::string& compressed,
-                          const std::string& uncompressed);
-
-    virtual void Uncompress(std::string& uncompressed,
-                            const std::string& compressed);
-  };
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Compression/DeflateBaseCompressor.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,75 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "DeflateBaseCompressor.h"
+
+#include "../OrthancException.h"
+#include "../Logging.h"
+
+#include <string.h>
+
+namespace Orthanc
+{
+  void DeflateBaseCompressor::SetCompressionLevel(uint8_t level)
+  {
+    if (level >= 10)
+    {
+      LOG(ERROR) << "Zlib compression level must be between 0 (no compression) and 9 (highest compression)";
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    compressionLevel_ = level;
+  }
+
+
+  uint64_t DeflateBaseCompressor::ReadUncompressedSizePrefix(const void* compressed,
+                                                             size_t compressedSize)
+  {
+    if (compressedSize == 0)
+    {
+      return 0;
+    }
+
+    if (compressedSize < sizeof(uint64_t))
+    {
+      LOG(ERROR) << "The compressed buffer is ill-formed";
+      throw OrthancException(ErrorCode_CorruptedFile);
+    }
+
+    uint64_t size;
+    memcpy(&size, compressed, sizeof(uint64_t));
+
+    return size;
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Compression/DeflateBaseCompressor.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,75 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IBufferCompressor.h"
+
+#include <stdint.h>
+
+namespace Orthanc
+{
+  class DeflateBaseCompressor : public IBufferCompressor
+  {
+  private:
+    uint8_t compressionLevel_;
+    bool    prefixWithUncompressedSize_;
+
+  protected:
+    uint64_t ReadUncompressedSizePrefix(const void* compressed,
+                                        size_t compressedSize);
+
+  public:
+    DeflateBaseCompressor() : 
+      compressionLevel_(6),
+      prefixWithUncompressedSize_(false)
+    {
+    }
+
+    void SetCompressionLevel(uint8_t level);
+    
+    void SetPrefixWithUncompressedSize(bool prefix)
+    {
+      prefixWithUncompressedSize_ = prefix;
+    }
+
+    bool HasPrefixWithUncompressedSize() const
+    {
+      return prefixWithUncompressedSize_;
+    }
+
+    uint8_t GetCompressionLevel() const
+    {
+      return compressionLevel_;
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Compression/GzipCompressor.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,277 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "GzipCompressor.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <zlib.h>
+
+#include "../OrthancException.h"
+#include "../Logging.h"
+
+namespace Orthanc
+{
+  uint64_t GzipCompressor::GuessUncompressedSize(const void* compressed,
+                                                 size_t compressedSize)
+  {
+    /**
+     * "Is there a way to find out the size of the original file which
+     * is inside a GZIP file? [...] There is no truly reliable way,
+     * other than gunzipping the stream. You do not need to save the
+     * result of the decompression, so you can determine the size by
+     * simply reading and decoding the entire file without taking up
+     * space with the decompressed result.
+     *
+     * There is an unreliable way to determine the uncompressed size,
+     * which is to look at the last four bytes of the gzip file, which
+     * is the uncompressed length of that entry modulo 232 in little
+     * endian order.
+     * 
+     * It is unreliable because a) the uncompressed data may be longer
+     * than 2^32 bytes, and b) the gzip file may consist of multiple
+     * gzip streams, in which case you would find the length of only
+     * the last of those streams.
+     * 
+     * If you are in control of the source of the gzip files, you know
+     * that they consist of single gzip streams, and you know that
+     * they are less than 2^32 bytes uncompressed, then and only then
+     * can you use those last four bytes with confidence."
+     *
+     * http://stackoverflow.com/a/9727599/881731
+     **/
+
+    if (compressedSize < 4)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    const uint8_t* p = reinterpret_cast<const uint8_t*>(compressed) + compressedSize - 4;
+
+    return ((static_cast<uint32_t>(p[0]) << 0) +
+            (static_cast<uint32_t>(p[1]) << 8) +
+            (static_cast<uint32_t>(p[2]) << 16) +
+            (static_cast<uint32_t>(p[3]) << 24));            
+  }
+
+
+
+  void GzipCompressor::Compress(std::string& compressed,
+                                const void* uncompressed,
+                                size_t uncompressedSize)
+  {
+    uLongf compressedSize = compressBound(uncompressedSize) + 1024 /* security margin */;
+    if (compressedSize == 0)
+    {
+      compressedSize = 1;
+    }
+
+    uint8_t* target;
+    if (HasPrefixWithUncompressedSize())
+    {
+      compressed.resize(compressedSize + sizeof(uint64_t));
+      target = reinterpret_cast<uint8_t*>(&compressed[0]) + sizeof(uint64_t);
+    }
+    else
+    {
+      compressed.resize(compressedSize);
+      target = reinterpret_cast<uint8_t*>(&compressed[0]);
+    }
+
+    z_stream stream;
+    memset(&stream, 0, sizeof(stream));
+
+    stream.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(uncompressed));
+    stream.next_out = reinterpret_cast<Bytef*>(target);
+
+    stream.avail_in = static_cast<uInt>(uncompressedSize);
+    stream.avail_out = static_cast<uInt>(compressedSize);
+
+    // Ensure no overflow (if the buffer is too large for the current archicture)
+    if (static_cast<size_t>(stream.avail_in) != uncompressedSize ||
+        static_cast<size_t>(stream.avail_out) != compressedSize)
+    {
+      throw OrthancException(ErrorCode_NotEnoughMemory);
+    }
+    
+    // Initialize the compression engine
+    int error = deflateInit2(&stream, 
+                             GetCompressionLevel(), 
+                             Z_DEFLATED,
+                             MAX_WBITS + 16,      // ask for gzip output
+                             8,                   // default memory level
+                             Z_DEFAULT_STRATEGY);
+
+    if (error != Z_OK)
+    {
+      // Cannot initialize zlib
+      compressed.clear();
+      throw OrthancException(ErrorCode_InternalError);
+    }
+
+    // Compress the input buffer
+    error = deflate(&stream, Z_FINISH);
+
+    if (error != Z_STREAM_END)
+    {
+      deflateEnd(&stream);
+      compressed.clear();
+
+      switch (error)
+      {
+      case Z_MEM_ERROR:
+        throw OrthancException(ErrorCode_NotEnoughMemory);
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+      }  
+    }
+
+    size_t size = stream.total_out;
+
+    if (deflateEnd(&stream) != Z_OK)
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+
+    // The compression was successful
+    if (HasPrefixWithUncompressedSize())
+    {
+      uint64_t s = static_cast<uint64_t>(uncompressedSize);
+      memcpy(&compressed[0], &s, sizeof(uint64_t));
+      compressed.resize(size + sizeof(uint64_t));
+    }
+    else
+    {
+      compressed.resize(size);
+    }
+  }
+
+
+  void GzipCompressor::Uncompress(std::string& uncompressed,
+                                  const void* compressed,
+                                  size_t compressedSize)
+  {
+    uint64_t uncompressedSize;
+    const uint8_t* source = reinterpret_cast<const uint8_t*>(compressed);
+
+    if (HasPrefixWithUncompressedSize())
+    {
+      uncompressedSize = ReadUncompressedSizePrefix(compressed, compressedSize);
+      source += sizeof(uint64_t);
+      compressedSize -= sizeof(uint64_t);
+    }
+    else
+    {
+      uncompressedSize = GuessUncompressedSize(compressed, compressedSize);
+    }
+
+    try
+    {
+      uncompressed.resize(static_cast<size_t>(uncompressedSize));
+    }
+    catch (...)
+    {
+      throw OrthancException(ErrorCode_NotEnoughMemory);
+    }
+
+    z_stream stream;
+    memset(&stream, 0, sizeof(stream));
+
+    char dummy = '\0';  // zlib does not like NULL output buffers (even if the uncompressed data is empty)
+    stream.next_in = const_cast<Bytef*>(source);
+    stream.next_out = reinterpret_cast<Bytef*>(uncompressedSize == 0 ? &dummy : &uncompressed[0]);
+
+    stream.avail_in = static_cast<uInt>(compressedSize);
+    stream.avail_out = static_cast<uInt>(uncompressedSize);
+
+    // Ensure no overflow (if the buffer is too large for the current archicture)
+    if (static_cast<size_t>(stream.avail_in) != compressedSize ||
+        static_cast<size_t>(stream.avail_out) != uncompressedSize)
+    {
+      throw OrthancException(ErrorCode_NotEnoughMemory);
+    }
+
+    // Initialize the compression engine
+    int error = inflateInit2(&stream, 
+                             MAX_WBITS + 16);  // this is a gzip input
+
+    if (error != Z_OK)
+    {
+      // Cannot initialize zlib
+      uncompressed.clear();
+      throw OrthancException(ErrorCode_InternalError);
+    }
+
+    // Uncompress the input buffer
+    error = inflate(&stream, Z_FINISH);
+
+    if (error != Z_STREAM_END)
+    {
+      inflateEnd(&stream);
+      uncompressed.clear();
+
+      switch (error)
+      {
+        case Z_MEM_ERROR:
+          throw OrthancException(ErrorCode_NotEnoughMemory);
+          
+        case Z_BUF_ERROR:
+        case Z_NEED_DICT:
+          throw OrthancException(ErrorCode_BadFileFormat);
+          
+        default:
+          throw OrthancException(ErrorCode_InternalError);
+      }
+    }
+
+    size_t size = stream.total_out;
+
+    if (inflateEnd(&stream) != Z_OK)
+    {
+      uncompressed.clear();
+      throw OrthancException(ErrorCode_InternalError);
+    }
+
+    if (size != uncompressedSize)
+    {
+      uncompressed.clear();
+
+      // The uncompressed size was not that properly guess, presumably
+      // because of a file size over 4GB. Should fallback to
+      // stream-based decompression.
+      LOG(ERROR) << "The uncompressed size of a gzip-encoded buffer was not properly guessed";
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Compression/GzipCompressor.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,59 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "DeflateBaseCompressor.h"
+
+namespace Orthanc
+{
+  class GzipCompressor : public DeflateBaseCompressor
+  {
+  private:
+    uint64_t GuessUncompressedSize(const void* compressed,
+                                   size_t compressedSize);
+
+  public:
+    GzipCompressor()
+    {
+      SetPrefixWithUncompressedSize(false);
+    }
+
+    virtual void Compress(std::string& compressed,
+                          const void* uncompressed,
+                          size_t uncompressedSize);
+
+    virtual void Uncompress(std::string& uncompressed,
+                            const void* compressed,
+                            size_t compressedSize);
+  };
+}
--- a/Core/Compression/HierarchicalZipWriter.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/Compression/HierarchicalZipWriter.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -53,7 +53,7 @@
       if (c == '^')
         c = ' ';
 
-      if (c < 128 && 
+      if (c <= 127 && 
           c >= 0)
       {
         if (isspace(c)) 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Compression/IBufferCompressor.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,73 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include <string>
+#include <boost/noncopyable.hpp>
+
+namespace Orthanc
+{
+  class IBufferCompressor : public boost::noncopyable
+  {
+  public:
+    virtual ~IBufferCompressor()
+    {
+    }
+
+    virtual void Compress(std::string& compressed,
+                          const void* uncompressed,
+                          size_t uncompressedSize) = 0;
+
+    virtual void Uncompress(std::string& uncompressed,
+                            const void* compressed,
+                            size_t compressedSize) = 0;
+
+    static void Compress(std::string& compressed,
+                         IBufferCompressor& compressor,
+                         const std::string& uncompressed)
+    {
+      compressor.Compress(compressed, 
+                          uncompressed.size() == 0 ? NULL : uncompressed.c_str(), 
+                          uncompressed.size());
+    }
+
+    static void Uncompress(std::string& uncompressed,
+                           IBufferCompressor& compressor,
+                           const std::string& compressed)
+    {
+      compressor.Uncompress(uncompressed, 
+                            compressed.size() == 0 ? NULL : compressed.c_str(), 
+                            compressed.size());
+    }
+  };
+}
--- a/Core/Compression/ZipWriter.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/Compression/ZipWriter.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -44,6 +44,7 @@
 
 #include "../../Resources/ThirdParty/minizip/zip.h"
 #include "../OrthancException.h"
+#include "../Logging.h"
 
 
 static void PrepareFileInfo(zip_fileinfo& zfi)
@@ -122,7 +123,8 @@
 
     if (path_.size() == 0)
     {
-      throw OrthancException("Please call SetOutputPath() before creating the file");
+      LOG(ERROR) << "Please call SetOutputPath() before creating the file";
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
     }
 
     hasFileInZip_ = false;
@@ -165,7 +167,8 @@
   {
     if (level >= 10)
     {
-      throw OrthancException("ZIP compression level must be between 0 (no compression) and 9 (highest compression");
+      LOG(ERROR) << "ZIP compression level must be between 0 (no compression) and 9 (highest compression)";
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
 
     Close();
@@ -224,7 +227,8 @@
   {
     if (!hasFileInZip_)
     {
-      throw OrthancException("Call first OpenFile()");
+      LOG(ERROR) << "Call first OpenFile()";
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
     }
 
     const size_t maxBytesInAStep = std::numeric_limits<int32_t>::max();
--- a/Core/Compression/ZlibCompressor.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/Compression/ZlibCompressor.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,24 +33,15 @@
 #include "../PrecompiledHeaders.h"
 #include "ZlibCompressor.h"
 
+#include "../OrthancException.h"
+#include "../Logging.h"
+
 #include <stdio.h>
 #include <string.h>
 #include <zlib.h>
-#include "../OrthancException.h"
 
 namespace Orthanc
 {
-  void ZlibCompressor::SetCompressionLevel(uint8_t level)
-  {
-    if (level >= 10)
-    {
-      throw OrthancException("Zlib compression level must be between 0 (no compression) and 9 (highest compression");
-    }
-
-    compressionLevel_ = level;
-  }
-
-
   void ZlibCompressor::Compress(std::string& compressed,
                                 const void* uncompressed,
                                 size_t uncompressedSize)
@@ -61,25 +52,32 @@
       return;
     }
 
-    uLongf compressedSize = compressBound(uncompressedSize);
-    compressed.resize(compressedSize + sizeof(size_t));
+    uLongf compressedSize = compressBound(uncompressedSize) + 1024 /* security margin */;
+    if (compressedSize == 0)
+    {
+      compressedSize = 1;
+    }
 
-    int error = compress2
-      (reinterpret_cast<uint8_t*>(&compressed[0]) + sizeof(size_t),
-       &compressedSize,
-       const_cast<Bytef *>(static_cast<const Bytef *>(uncompressed)), 
-       uncompressedSize,
-       compressionLevel_);
-
-    memcpy(&compressed[0], &uncompressedSize, sizeof(size_t));
-  
-    if (error == Z_OK)
+    uint8_t* target;
+    if (HasPrefixWithUncompressedSize())
     {
-      compressed.resize(compressedSize + sizeof(size_t));
-      return;
+      compressed.resize(compressedSize + sizeof(uint64_t));
+      target = reinterpret_cast<uint8_t*>(&compressed[0]) + sizeof(uint64_t);
     }
     else
     {
+      compressed.resize(compressedSize);
+      target = reinterpret_cast<uint8_t*>(&compressed[0]);
+    }
+
+    int error = compress2(target,
+                          &compressedSize,
+                          const_cast<Bytef *>(static_cast<const Bytef *>(uncompressed)), 
+                          uncompressedSize,
+                          GetCompressionLevel());
+
+    if (error != Z_OK)
+    {
       compressed.clear();
 
       switch (error)
@@ -91,6 +89,18 @@
         throw OrthancException(ErrorCode_InternalError);
       }  
     }
+
+    // The compression was successful
+    if (HasPrefixWithUncompressedSize())
+    {
+      uint64_t s = static_cast<uint64_t>(uncompressedSize);
+      memcpy(&compressed[0], &s, sizeof(uint64_t));
+      compressed.resize(compressedSize + sizeof(uint64_t));
+    }
+    else
+    {
+      compressed.resize(compressedSize);
+    }
   }
 
 
@@ -104,29 +114,29 @@
       return;
     }
 
-    if (compressedSize < sizeof(size_t))
+    if (!HasPrefixWithUncompressedSize())
     {
-      throw OrthancException("Zlib: The compressed buffer is ill-formed");
+      LOG(ERROR) << "Cannot guess the uncompressed size of a zlib-encoded buffer";
+      throw OrthancException(ErrorCode_InternalError);
     }
 
-    size_t uncompressedLength;
-    memcpy(&uncompressedLength, compressed, sizeof(size_t));
+    uint64_t uncompressedSize = ReadUncompressedSizePrefix(compressed, compressedSize);
     
     try
     {
-      uncompressed.resize(uncompressedLength);
+      uncompressed.resize(static_cast<size_t>(uncompressedSize));
     }
     catch (...)
     {
-      throw OrthancException("Zlib: Corrupted compressed buffer");
+      throw OrthancException(ErrorCode_NotEnoughMemory);
     }
 
-    uLongf tmp = uncompressedLength;
+    uLongf tmp = static_cast<uLongf>(uncompressedSize);
     int error = uncompress
       (reinterpret_cast<uint8_t*>(&uncompressed[0]), 
        &tmp,
-       reinterpret_cast<const uint8_t*>(compressed) + sizeof(size_t),
-       compressedSize - sizeof(size_t));
+       reinterpret_cast<const uint8_t*>(compressed) + sizeof(uint64_t),
+       compressedSize - sizeof(uint64_t));
 
     if (error != Z_OK)
     {
@@ -135,7 +145,7 @@
       switch (error)
       {
       case Z_DATA_ERROR:
-        throw OrthancException("Zlib: Corrupted or incomplete compressed buffer");
+        throw OrthancException(ErrorCode_CorruptedFile);
 
       case Z_MEM_ERROR:
         throw OrthancException(ErrorCode_NotEnoughMemory);
--- a/Core/Compression/ZlibCompressor.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/Compression/ZlibCompressor.h	Wed Sep 30 13:23:31 2015 +0200
@@ -32,29 +32,16 @@
 
 #pragma once
 
-#include "BufferCompressor.h"
+#include "DeflateBaseCompressor.h"
 
 namespace Orthanc
 {
-  class ZlibCompressor : public BufferCompressor
+  class ZlibCompressor : public DeflateBaseCompressor
   {
-  private:
-    uint8_t compressionLevel_;
-
   public:
-    using BufferCompressor::Compress;
-    using BufferCompressor::Uncompress;
-
     ZlibCompressor()
     {
-      compressionLevel_ = 6;
-    }
-
-    void SetCompressionLevel(uint8_t level);
-
-    uint8_t GetCompressionLevel() const
-    {
-      return compressionLevel_;
+      SetPrefixWithUncompressedSize(true);
     }
 
     virtual void Compress(std::string& compressed,
--- a/Core/DicomFormat/DicomMap.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/DicomFormat/DicomMap.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -273,6 +273,16 @@
     result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "");
     result.SetValue(DICOM_TAG_PATIENT_ID, "");
     result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "");
+
+    // These tags are considered as "main" by Orthanc, but are not in the Series module
+    result.Remove(DicomTag(0x0008, 0x0070));  // Manufacturer
+    result.Remove(DicomTag(0x0008, 0x1010));  // Station name
+    result.Remove(DicomTag(0x0018, 0x0024));  // Sequence name
+    result.Remove(DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES);
+    result.Remove(DICOM_TAG_IMAGES_IN_ACQUISITION);
+    result.Remove(DICOM_TAG_NUMBER_OF_SLICES);
+    result.Remove(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS);
+    result.Remove(DICOM_TAG_NUMBER_OF_TIME_SLICES);
   }
 
   void DicomMap::SetupFindInstanceTemplate(DicomMap& result)
@@ -406,4 +416,16 @@
     DicomArray a(*this);
     a.Print(fp);
   }
+
+
+  void DicomMap::GetTags(std::set<DicomTag>& tags) const
+  {
+    tags.clear();
+
+    for (Map::const_iterator it = map_.begin();
+         it != map_.end(); ++it)
+    {
+      tags.insert(it->first);
+    }
+  }
 }
--- a/Core/DicomFormat/DicomMap.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/DicomFormat/DicomMap.h	Wed Sep 30 13:23:31 2015 +0200
@@ -77,6 +77,11 @@
     {
       Clear();
     }
+
+    size_t GetSize() const
+    {
+      return map_.size();
+    }
     
     DicomMap* Clone() const;
 
@@ -166,5 +171,7 @@
     static void GetMainDicomTags(std::set<DicomTag>& result);
 
     void Print(FILE* fp) const;
+
+    void GetTags(std::set<DicomTag>& tags) const;
   };
 }
--- a/Core/DicomFormat/DicomTag.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/DicomFormat/DicomTag.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -118,11 +118,10 @@
   }
 
 
-  void DicomTag::GetTagsForModule(std::set<DicomTag>& target,
+  void DicomTag::AddTagsForModule(std::set<DicomTag>& target,
                                   DicomModule module)
   {
     // REFERENCE: 11_03pu.pdf, DICOM PS 3.3 2011 - Information Object Definitions
-    target.clear();
 
     switch (module)
     {
--- a/Core/DicomFormat/DicomTag.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/DicomFormat/DicomTag.h	Wed Sep 30 13:23:31 2015 +0200
@@ -84,7 +84,7 @@
 
     friend std::ostream& operator<< (std::ostream& o, const DicomTag& tag);
 
-    static void GetTagsForModule(std::set<DicomTag>& target,
+    static void AddTagsForModule(std::set<DicomTag>& target,
                                  DicomModule module);
 
     bool IsIdentifier() const;
@@ -106,8 +106,8 @@
   static const DicomTag DICOM_TAG_NUMBER_OF_FRAMES(0x0028, 0x0008);
   static const DicomTag DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES(0x0018, 0x1090);
   static const DicomTag DICOM_TAG_IMAGES_IN_ACQUISITION(0x0020, 0x1002);
-
   static const DicomTag DICOM_TAG_PATIENT_NAME(0x0010, 0x0010);
+  static const DicomTag DICOM_TAG_ENCAPSULATED_DOCUMENT(0x0042, 0x0011);
 
   // The following is used for "modify/anonymize" operations
   static const DicomTag DICOM_TAG_SOP_CLASS_UID(0x0008, 0x0016);
@@ -134,4 +134,18 @@
   static const DicomTag DICOM_TAG_PIXEL_REPRESENTATION(0x0028, 0x0103);
   static const DicomTag DICOM_TAG_PLANAR_CONFIGURATION(0x0028, 0x0006);
   static const DicomTag DICOM_TAG_PHOTOMETRIC_INTERPRETATION(0x0028, 0x0004);
+
+  // Tags related to date and time
+  static const DicomTag DICOM_TAG_ACQUISITION_DATE(0x0008, 0x0022);
+  static const DicomTag DICOM_TAG_ACQUISITION_TIME(0x0008, 0x0032);
+  static const DicomTag DICOM_TAG_CONTENT_DATE(0x0008, 0x0023);
+  static const DicomTag DICOM_TAG_CONTENT_TIME(0x0008, 0x0033);
+  static const DicomTag DICOM_TAG_INSTANCE_CREATION_DATE(0x0008, 0x0012);
+  static const DicomTag DICOM_TAG_INSTANCE_CREATION_TIME(0x0008, 0x0013);
+  static const DicomTag DICOM_TAG_PATIENT_BIRTH_DATE(0x0010, 0x0030);
+  static const DicomTag DICOM_TAG_PATIENT_BIRTH_TIME(0x0010, 0x0032);
+  static const DicomTag DICOM_TAG_SERIES_DATE(0x0008, 0x0021);
+  static const DicomTag DICOM_TAG_SERIES_TIME(0x0008, 0x0031);
+  static const DicomTag DICOM_TAG_STUDY_DATE(0x0008, 0x0020);
+  static const DicomTag DICOM_TAG_STUDY_TIME(0x0008, 0x0030);
 }
--- a/Core/Enumerations.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/Enumerations.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -36,8 +36,294 @@
 #include "OrthancException.h"
 #include "Toolbox.h"
 
+#include <string.h>
+
 namespace Orthanc
 {
+  // This function is autogenerated by the script
+  // "Resources/GenerateErrorCodes.py"
+  const char* EnumerationToString(ErrorCode error)
+  {
+    if (error >= ErrorCode_START_PLUGINS)
+    {
+      return "Error encountered within some plugin";
+    }
+
+    switch (error)
+    {
+      case ErrorCode_InternalError:
+        return "Internal error";
+
+      case ErrorCode_Success:
+        return "Success";
+
+      case ErrorCode_Plugin:
+        return "Error encountered within the plugin engine";
+
+      case ErrorCode_NotImplemented:
+        return "Not implemented yet";
+
+      case ErrorCode_ParameterOutOfRange:
+        return "Parameter out of range";
+
+      case ErrorCode_NotEnoughMemory:
+        return "Not enough memory";
+
+      case ErrorCode_BadParameterType:
+        return "Bad type for a parameter";
+
+      case ErrorCode_BadSequenceOfCalls:
+        return "Bad sequence of calls";
+
+      case ErrorCode_InexistentItem:
+        return "Accessing an inexistent item";
+
+      case ErrorCode_BadRequest:
+        return "Bad request";
+
+      case ErrorCode_NetworkProtocol:
+        return "Error in the network protocol";
+
+      case ErrorCode_SystemCommand:
+        return "Error while calling a system command";
+
+      case ErrorCode_Database:
+        return "Error with the database engine";
+
+      case ErrorCode_UriSyntax:
+        return "Badly formatted URI";
+
+      case ErrorCode_InexistentFile:
+        return "Inexistent file";
+
+      case ErrorCode_CannotWriteFile:
+        return "Cannot write to file";
+
+      case ErrorCode_BadFileFormat:
+        return "Bad file format";
+
+      case ErrorCode_Timeout:
+        return "Timeout";
+
+      case ErrorCode_UnknownResource:
+        return "Unknown resource";
+
+      case ErrorCode_IncompatibleDatabaseVersion:
+        return "Incompatible version of the database";
+
+      case ErrorCode_FullStorage:
+        return "The file storage is full";
+
+      case ErrorCode_CorruptedFile:
+        return "Corrupted file (e.g. inconsistent MD5 hash)";
+
+      case ErrorCode_InexistentTag:
+        return "Inexistent tag";
+
+      case ErrorCode_ReadOnly:
+        return "Cannot modify a read-only data structure";
+
+      case ErrorCode_IncompatibleImageFormat:
+        return "Incompatible format of the images";
+
+      case ErrorCode_IncompatibleImageSize:
+        return "Incompatible size of the images";
+
+      case ErrorCode_SharedLibrary:
+        return "Error while using a shared library (plugin)";
+
+      case ErrorCode_UnknownPluginService:
+        return "Plugin invoking an unknown service";
+
+      case ErrorCode_UnknownDicomTag:
+        return "Unknown DICOM tag";
+
+      case ErrorCode_BadJson:
+        return "Cannot parse a JSON document";
+
+      case ErrorCode_Unauthorized:
+        return "Bad credentials were provided to an HTTP request";
+
+      case ErrorCode_BadFont:
+        return "Badly formatted font file";
+
+      case ErrorCode_DatabasePlugin:
+        return "The plugin implementing a custom database back-end does not fulfill the proper interface";
+
+      case ErrorCode_StorageAreaPlugin:
+        return "Error in the plugin implementing a custom storage area";
+
+      case ErrorCode_SQLiteNotOpened:
+        return "SQLite: The database is not opened";
+
+      case ErrorCode_SQLiteAlreadyOpened:
+        return "SQLite: Connection is already open";
+
+      case ErrorCode_SQLiteCannotOpen:
+        return "SQLite: Unable to open the database";
+
+      case ErrorCode_SQLiteStatementAlreadyUsed:
+        return "SQLite: This cached statement is already being referred to";
+
+      case ErrorCode_SQLiteExecute:
+        return "SQLite: Cannot execute a command";
+
+      case ErrorCode_SQLiteRollbackWithoutTransaction:
+        return "SQLite: Rolling back a nonexistent transaction (have you called Begin()?)";
+
+      case ErrorCode_SQLiteCommitWithoutTransaction:
+        return "SQLite: Committing a nonexistent transaction";
+
+      case ErrorCode_SQLiteRegisterFunction:
+        return "SQLite: Unable to register a function";
+
+      case ErrorCode_SQLiteFlush:
+        return "SQLite: Unable to flush the database";
+
+      case ErrorCode_SQLiteCannotRun:
+        return "SQLite: Cannot run a cached statement";
+
+      case ErrorCode_SQLiteCannotStep:
+        return "SQLite: Cannot step over a cached statement";
+
+      case ErrorCode_SQLiteBindOutOfRange:
+        return "SQLite: Bing a value while out of range (serious error)";
+
+      case ErrorCode_SQLitePrepareStatement:
+        return "SQLite: Cannot prepare a cached statement";
+
+      case ErrorCode_SQLiteTransactionAlreadyStarted:
+        return "SQLite: Beginning the same transaction twice";
+
+      case ErrorCode_SQLiteTransactionCommit:
+        return "SQLite: Failure when committing the transaction";
+
+      case ErrorCode_SQLiteTransactionBegin:
+        return "SQLite: Cannot start a transaction";
+
+      case ErrorCode_DirectoryOverFile:
+        return "The directory to be created is already occupied by a regular file";
+
+      case ErrorCode_FileStorageCannotWrite:
+        return "Unable to create a subdirectory or a file in the file storage";
+
+      case ErrorCode_DirectoryExpected:
+        return "The specified path does not point to a directory";
+
+      case ErrorCode_HttpPortInUse:
+        return "The TCP port of the HTTP server is already in use";
+
+      case ErrorCode_DicomPortInUse:
+        return "The TCP port of the DICOM server is already in use";
+
+      case ErrorCode_BadHttpStatusInRest:
+        return "This HTTP status is not allowed in a REST API";
+
+      case ErrorCode_RegularFileExpected:
+        return "The specified path does not point to a regular file";
+
+      case ErrorCode_PathToExecutable:
+        return "Unable to get the path to the executable";
+
+      case ErrorCode_MakeDirectory:
+        return "Cannot create a directory";
+
+      case ErrorCode_BadApplicationEntityTitle:
+        return "An application entity title (AET) cannot be empty or be longer than 16 characters";
+
+      case ErrorCode_NoCFindHandler:
+        return "No request handler factory for DICOM C-FIND SCP";
+
+      case ErrorCode_NoCMoveHandler:
+        return "No request handler factory for DICOM C-MOVE SCP";
+
+      case ErrorCode_NoCStoreHandler:
+        return "No request handler factory for DICOM C-STORE SCP";
+
+      case ErrorCode_NoApplicationEntityFilter:
+        return "No application entity filter";
+
+      case ErrorCode_NoSopClassOrInstance:
+        return "DicomUserConnection: Unable to find the SOP class and instance";
+
+      case ErrorCode_NoPresentationContext:
+        return "DicomUserConnection: No acceptable presentation context for modality";
+
+      case ErrorCode_DicomFindUnavailable:
+        return "DicomUserConnection: The C-FIND command is not supported by the remote SCP";
+
+      case ErrorCode_DicomMoveUnavailable:
+        return "DicomUserConnection: The C-MOVE command is not supported by the remote SCP";
+
+      case ErrorCode_CannotStoreInstance:
+        return "Cannot store an instance";
+
+      case ErrorCode_CreateDicomNotString:
+        return "Only string values are supported when creating DICOM instances";
+
+      case ErrorCode_CreateDicomOverrideTag:
+        return "Trying to override a value inherited from a parent module";
+
+      case ErrorCode_CreateDicomUseContent:
+        return "Use \"Content\" to inject an image into a new DICOM instance";
+
+      case ErrorCode_CreateDicomNoPayload:
+        return "No payload is present for one instance in the series";
+
+      case ErrorCode_CreateDicomUseDataUriScheme:
+        return "The payload of the DICOM instance must be specified according to Data URI scheme";
+
+      case ErrorCode_CreateDicomBadParent:
+        return "Trying to attach a new DICOM instance to an inexistent resource";
+
+      case ErrorCode_CreateDicomParentIsInstance:
+        return "Trying to attach a new DICOM instance to an instance (must be a series, study or patient)";
+
+      case ErrorCode_CreateDicomParentEncoding:
+        return "Unable to get the encoding of the parent resource";
+
+      case ErrorCode_UnknownModality:
+        return "Unknown modality";
+
+      case ErrorCode_BadJobOrdering:
+        return "Bad ordering of filters in a job";
+
+      case ErrorCode_JsonToLuaTable:
+        return "Cannot convert the given JSON object to a Lua table";
+
+      case ErrorCode_CannotCreateLua:
+        return "Cannot create the Lua context";
+
+      case ErrorCode_CannotExecuteLua:
+        return "Cannot execute a Lua command";
+
+      case ErrorCode_LuaAlreadyExecuted:
+        return "Arguments cannot be pushed after the Lua function is executed";
+
+      case ErrorCode_LuaBadOutput:
+        return "The Lua function does not give the expected number of outputs";
+
+      case ErrorCode_NotLuaPredicate:
+        return "The Lua function is not a predicate (only true/false outputs allowed)";
+
+      case ErrorCode_LuaReturnsNoString:
+        return "The Lua function does not return a string";
+
+      case ErrorCode_StorageAreaAlreadyRegistered:
+        return "Another plugin has already registered a custom storage area";
+
+      case ErrorCode_DatabaseBackendAlreadyRegistered:
+        return "Another plugin has already registered a custom database back-end";
+
+      case ErrorCode_DatabaseNotInitialized:
+        return "Plugin trying to call the database during its initialization";
+
+      default:
+        return "Unknown error code";
+    }
+  }
+
+
   const char* EnumerationToString(HttpMethod method)
   {
     switch (method)
@@ -289,6 +575,9 @@
       case Encoding_Cyrillic:
         return "Cyrillic";
 
+      case Encoding_Windows1251:
+        return "Windows1251";
+
       case Encoding_Arabic:
         return "Arabic";
 
@@ -365,6 +654,53 @@
   }
 
 
+  const char* EnumerationToString(RequestOrigin origin)
+  {
+    switch (origin)
+    {
+      case RequestOrigin_Unknown:
+        return "Unknown";
+
+      case RequestOrigin_DicomProtocol:
+        return "DicomProtocol";
+
+      case RequestOrigin_Http:
+        return "Http";
+
+      case RequestOrigin_Plugins:
+        return "Plugins";
+
+      case RequestOrigin_Lua:
+        return "Lua";
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
+  const char* EnumerationToString(LogLevel level)
+  {
+    switch (level)
+    {
+      case LogLevel_Error:
+        return "ERROR";
+
+      case LogLevel_Warning:
+        return "WARNING";
+
+      case LogLevel_Info:
+        return "INFO";
+
+      case LogLevel_Trace:
+        return "TRACE";
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
   Encoding StringToEncoding(const char* encoding)
   {
     std::string s(encoding);
@@ -410,6 +746,11 @@
       return Encoding_Cyrillic;
     }
 
+    if (s == "WINDOWS1251")
+    {
+      return Encoding_Windows1251;
+    }
+
     if (s == "ARABIC")
     {
       return Encoding_Arabic;
@@ -485,6 +826,31 @@
   }
 
 
+  LogLevel StringToLogLevel(const char *level)
+  {
+    if (strcmp(level, "ERROR") == 0)
+    {
+      return LogLevel_Error;
+    }
+    else if (strcmp(level, "WARNING") == 0)
+    {
+      return LogLevel_Warning;
+    }
+    else if (strcmp(level, "INFO") == 0)
+    {
+      return LogLevel_Info;
+    }
+    else if (strcmp(level, "TRACE") == 0)
+    {
+      return LogLevel_Trace;
+    }
+    else 
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+  }
+
+
   unsigned int GetBytesPerPixel(PixelFormat format)
   {
     switch (format)
@@ -617,4 +983,178 @@
         return "application/octet-stream";
     }
   }
+
+
+  const char* GetFileExtension(FileContentType type)
+  {
+    switch (type)
+    {
+      case FileContentType_Dicom:
+        return ".dcm";
+
+      case FileContentType_DicomAsJson:
+        return ".json";
+
+      default:
+        // Unknown file type
+        return "";
+    }
+  }
+
+
+  ResourceType GetChildResourceType(ResourceType type)
+  {
+    switch (type)
+    {
+      case ResourceType_Patient:
+        return ResourceType_Study;
+
+      case ResourceType_Study:
+        return ResourceType_Series;
+        
+      case ResourceType_Series:
+        return ResourceType_Instance;
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
+  ResourceType GetParentResourceType(ResourceType type)
+  {
+    switch (type)
+    {
+      case ResourceType_Study:
+        return ResourceType_Patient;
+        
+      case ResourceType_Series:
+        return ResourceType_Study;
+
+      case ResourceType_Instance:
+        return ResourceType_Series;
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
+  DicomModule GetModule(ResourceType type)
+  {
+    switch (type)
+    {
+      case ResourceType_Patient:
+        return DicomModule_Patient;
+
+      case ResourceType_Study:
+        return DicomModule_Study;
+        
+      case ResourceType_Series:
+        return DicomModule_Series;
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
+
+  const char* GetDicomSpecificCharacterSet(Encoding encoding)
+  {
+    // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/
+    switch (encoding)
+    {
+      case Encoding_Utf8:
+      case Encoding_Ascii:
+        return "ISO_IR 192";
+
+      case Encoding_Latin1:
+        return "ISO_IR 100";
+
+      case Encoding_Latin2:
+        return "ISO_IR 101";
+
+      case Encoding_Latin3:
+        return "ISO_IR 109";
+
+      case Encoding_Latin4:
+        return "ISO_IR 110";
+
+      case Encoding_Latin5:
+        return "ISO_IR 148";
+
+      case Encoding_Cyrillic:
+        return "ISO_IR 144";
+
+      case Encoding_Arabic:
+        return "ISO_IR 127";
+
+      case Encoding_Greek:
+        return "ISO_IR 126";
+
+      case Encoding_Hebrew:
+        return "ISO_IR 138";
+
+      case Encoding_Japanese:
+        return "ISO_IR 13";
+
+      case Encoding_Chinese:
+        return "GB18030";
+
+      case Encoding_Thai:
+        return "ISO_IR 166";
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
+  // This function is autogenerated by the script
+  // "Resources/GenerateErrorCodes.py"
+  HttpStatus ConvertErrorCodeToHttpStatus(ErrorCode error)
+  {
+    switch (error)
+    {
+      case ErrorCode_Success:
+        return HttpStatus_200_Ok;
+
+      case ErrorCode_ParameterOutOfRange:
+        return HttpStatus_400_BadRequest;
+
+      case ErrorCode_BadParameterType:
+        return HttpStatus_400_BadRequest;
+
+      case ErrorCode_InexistentItem:
+        return HttpStatus_404_NotFound;
+
+      case ErrorCode_BadRequest:
+        return HttpStatus_400_BadRequest;
+
+      case ErrorCode_UriSyntax:
+        return HttpStatus_400_BadRequest;
+
+      case ErrorCode_InexistentFile:
+        return HttpStatus_404_NotFound;
+
+      case ErrorCode_BadFileFormat:
+        return HttpStatus_400_BadRequest;
+
+      case ErrorCode_UnknownResource:
+        return HttpStatus_404_NotFound;
+
+      case ErrorCode_InexistentTag:
+        return HttpStatus_404_NotFound;
+
+      case ErrorCode_BadJson:
+        return HttpStatus_400_BadRequest;
+
+      case ErrorCode_Unauthorized:
+        return HttpStatus_401_Unauthorized;
+
+      default:
+        return HttpStatus_500_InternalServerError;
+    }
+  }
 }
--- a/Core/Enumerations.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/Enumerations.h	Wed Sep 30 13:23:31 2015 +0200
@@ -32,8 +32,6 @@
 
 #pragma once
 
-#include <laaw/laaw.h>
-
 namespace Orthanc
 {
   enum Endianness
@@ -43,45 +41,115 @@
     Endianness_Little
   };
 
+  // This enumeration is autogenerated by the script
+  // "Resources/GenerateErrorCodes.py"
   enum ErrorCode
   {
-    // Generic error codes
-    ErrorCode_Success,
-    ErrorCode_Custom,
-    ErrorCode_InternalError,
-    ErrorCode_NotImplemented,
-    ErrorCode_ParameterOutOfRange,
-    ErrorCode_NotEnoughMemory,
-    ErrorCode_BadParameterType,
-    ErrorCode_BadSequenceOfCalls,
-    ErrorCode_InexistentItem,
-    ErrorCode_BadRequest,
-    ErrorCode_NetworkProtocol,
-    ErrorCode_SystemCommand,
-    ErrorCode_Database,
+    ErrorCode_InternalError = -1    /*!< Internal error */,
+    ErrorCode_Success = 0    /*!< Success */,
+    ErrorCode_Plugin = 1    /*!< Error encountered within the plugin engine */,
+    ErrorCode_NotImplemented = 2    /*!< Not implemented yet */,
+    ErrorCode_ParameterOutOfRange = 3    /*!< Parameter out of range */,
+    ErrorCode_NotEnoughMemory = 4    /*!< Not enough memory */,
+    ErrorCode_BadParameterType = 5    /*!< Bad type for a parameter */,
+    ErrorCode_BadSequenceOfCalls = 6    /*!< Bad sequence of calls */,
+    ErrorCode_InexistentItem = 7    /*!< Accessing an inexistent item */,
+    ErrorCode_BadRequest = 8    /*!< Bad request */,
+    ErrorCode_NetworkProtocol = 9    /*!< Error in the network protocol */,
+    ErrorCode_SystemCommand = 10    /*!< Error while calling a system command */,
+    ErrorCode_Database = 11    /*!< Error with the database engine */,
+    ErrorCode_UriSyntax = 12    /*!< Badly formatted URI */,
+    ErrorCode_InexistentFile = 13    /*!< Inexistent file */,
+    ErrorCode_CannotWriteFile = 14    /*!< Cannot write to file */,
+    ErrorCode_BadFileFormat = 15    /*!< Bad file format */,
+    ErrorCode_Timeout = 16    /*!< Timeout */,
+    ErrorCode_UnknownResource = 17    /*!< Unknown resource */,
+    ErrorCode_IncompatibleDatabaseVersion = 18    /*!< Incompatible version of the database */,
+    ErrorCode_FullStorage = 19    /*!< The file storage is full */,
+    ErrorCode_CorruptedFile = 20    /*!< Corrupted file (e.g. inconsistent MD5 hash) */,
+    ErrorCode_InexistentTag = 21    /*!< Inexistent tag */,
+    ErrorCode_ReadOnly = 22    /*!< Cannot modify a read-only data structure */,
+    ErrorCode_IncompatibleImageFormat = 23    /*!< Incompatible format of the images */,
+    ErrorCode_IncompatibleImageSize = 24    /*!< Incompatible size of the images */,
+    ErrorCode_SharedLibrary = 25    /*!< Error while using a shared library (plugin) */,
+    ErrorCode_UnknownPluginService = 26    /*!< Plugin invoking an unknown service */,
+    ErrorCode_UnknownDicomTag = 27    /*!< Unknown DICOM tag */,
+    ErrorCode_BadJson = 28    /*!< Cannot parse a JSON document */,
+    ErrorCode_Unauthorized = 29    /*!< Bad credentials were provided to an HTTP request */,
+    ErrorCode_BadFont = 30    /*!< Badly formatted font file */,
+    ErrorCode_DatabasePlugin = 31    /*!< The plugin implementing a custom database back-end does not fulfill the proper interface */,
+    ErrorCode_StorageAreaPlugin = 32    /*!< Error in the plugin implementing a custom storage area */,
+    ErrorCode_SQLiteNotOpened = 1000    /*!< SQLite: The database is not opened */,
+    ErrorCode_SQLiteAlreadyOpened = 1001    /*!< SQLite: Connection is already open */,
+    ErrorCode_SQLiteCannotOpen = 1002    /*!< SQLite: Unable to open the database */,
+    ErrorCode_SQLiteStatementAlreadyUsed = 1003    /*!< SQLite: This cached statement is already being referred to */,
+    ErrorCode_SQLiteExecute = 1004    /*!< SQLite: Cannot execute a command */,
+    ErrorCode_SQLiteRollbackWithoutTransaction = 1005    /*!< SQLite: Rolling back a nonexistent transaction (have you called Begin()?) */,
+    ErrorCode_SQLiteCommitWithoutTransaction = 1006    /*!< SQLite: Committing a nonexistent transaction */,
+    ErrorCode_SQLiteRegisterFunction = 1007    /*!< SQLite: Unable to register a function */,
+    ErrorCode_SQLiteFlush = 1008    /*!< SQLite: Unable to flush the database */,
+    ErrorCode_SQLiteCannotRun = 1009    /*!< SQLite: Cannot run a cached statement */,
+    ErrorCode_SQLiteCannotStep = 1010    /*!< SQLite: Cannot step over a cached statement */,
+    ErrorCode_SQLiteBindOutOfRange = 1011    /*!< SQLite: Bing a value while out of range (serious error) */,
+    ErrorCode_SQLitePrepareStatement = 1012    /*!< SQLite: Cannot prepare a cached statement */,
+    ErrorCode_SQLiteTransactionAlreadyStarted = 1013    /*!< SQLite: Beginning the same transaction twice */,
+    ErrorCode_SQLiteTransactionCommit = 1014    /*!< SQLite: Failure when committing the transaction */,
+    ErrorCode_SQLiteTransactionBegin = 1015    /*!< SQLite: Cannot start a transaction */,
+    ErrorCode_DirectoryOverFile = 2000    /*!< The directory to be created is already occupied by a regular file */,
+    ErrorCode_FileStorageCannotWrite = 2001    /*!< Unable to create a subdirectory or a file in the file storage */,
+    ErrorCode_DirectoryExpected = 2002    /*!< The specified path does not point to a directory */,
+    ErrorCode_HttpPortInUse = 2003    /*!< The TCP port of the HTTP server is already in use */,
+    ErrorCode_DicomPortInUse = 2004    /*!< The TCP port of the DICOM server is already in use */,
+    ErrorCode_BadHttpStatusInRest = 2005    /*!< This HTTP status is not allowed in a REST API */,
+    ErrorCode_RegularFileExpected = 2006    /*!< The specified path does not point to a regular file */,
+    ErrorCode_PathToExecutable = 2007    /*!< Unable to get the path to the executable */,
+    ErrorCode_MakeDirectory = 2008    /*!< Cannot create a directory */,
+    ErrorCode_BadApplicationEntityTitle = 2009    /*!< An application entity title (AET) cannot be empty or be longer than 16 characters */,
+    ErrorCode_NoCFindHandler = 2010    /*!< No request handler factory for DICOM C-FIND SCP */,
+    ErrorCode_NoCMoveHandler = 2011    /*!< No request handler factory for DICOM C-MOVE SCP */,
+    ErrorCode_NoCStoreHandler = 2012    /*!< No request handler factory for DICOM C-STORE SCP */,
+    ErrorCode_NoApplicationEntityFilter = 2013    /*!< No application entity filter */,
+    ErrorCode_NoSopClassOrInstance = 2014    /*!< DicomUserConnection: Unable to find the SOP class and instance */,
+    ErrorCode_NoPresentationContext = 2015    /*!< DicomUserConnection: No acceptable presentation context for modality */,
+    ErrorCode_DicomFindUnavailable = 2016    /*!< DicomUserConnection: The C-FIND command is not supported by the remote SCP */,
+    ErrorCode_DicomMoveUnavailable = 2017    /*!< DicomUserConnection: The C-MOVE command is not supported by the remote SCP */,
+    ErrorCode_CannotStoreInstance = 2018    /*!< Cannot store an instance */,
+    ErrorCode_CreateDicomNotString = 2019    /*!< Only string values are supported when creating DICOM instances */,
+    ErrorCode_CreateDicomOverrideTag = 2020    /*!< Trying to override a value inherited from a parent module */,
+    ErrorCode_CreateDicomUseContent = 2021    /*!< Use \"Content\" to inject an image into a new DICOM instance */,
+    ErrorCode_CreateDicomNoPayload = 2022    /*!< No payload is present for one instance in the series */,
+    ErrorCode_CreateDicomUseDataUriScheme = 2023    /*!< The payload of the DICOM instance must be specified according to Data URI scheme */,
+    ErrorCode_CreateDicomBadParent = 2024    /*!< Trying to attach a new DICOM instance to an inexistent resource */,
+    ErrorCode_CreateDicomParentIsInstance = 2025    /*!< Trying to attach a new DICOM instance to an instance (must be a series, study or patient) */,
+    ErrorCode_CreateDicomParentEncoding = 2026    /*!< Unable to get the encoding of the parent resource */,
+    ErrorCode_UnknownModality = 2027    /*!< Unknown modality */,
+    ErrorCode_BadJobOrdering = 2028    /*!< Bad ordering of filters in a job */,
+    ErrorCode_JsonToLuaTable = 2029    /*!< Cannot convert the given JSON object to a Lua table */,
+    ErrorCode_CannotCreateLua = 2030    /*!< Cannot create the Lua context */,
+    ErrorCode_CannotExecuteLua = 2031    /*!< Cannot execute a Lua command */,
+    ErrorCode_LuaAlreadyExecuted = 2032    /*!< Arguments cannot be pushed after the Lua function is executed */,
+    ErrorCode_LuaBadOutput = 2033    /*!< The Lua function does not give the expected number of outputs */,
+    ErrorCode_NotLuaPredicate = 2034    /*!< The Lua function is not a predicate (only true/false outputs allowed) */,
+    ErrorCode_LuaReturnsNoString = 2035    /*!< The Lua function does not return a string */,
+    ErrorCode_StorageAreaAlreadyRegistered = 2036    /*!< Another plugin has already registered a custom storage area */,
+    ErrorCode_DatabaseBackendAlreadyRegistered = 2037    /*!< Another plugin has already registered a custom database back-end */,
+    ErrorCode_DatabaseNotInitialized = 2038    /*!< Plugin trying to call the database during its initialization */,
+    ErrorCode_START_PLUGINS = 1000000
+  };
 
-    // Specific error codes
-    ErrorCode_UriSyntax,
-    ErrorCode_InexistentFile,
-    ErrorCode_CannotWriteFile,
-    ErrorCode_BadFileFormat,
-    ErrorCode_Timeout,
-    ErrorCode_UnknownResource,
-    ErrorCode_IncompatibleDatabaseVersion,
-    ErrorCode_FullStorage,
-    ErrorCode_CorruptedFile,
-    ErrorCode_InexistentTag,
-    ErrorCode_ReadOnly,
-    ErrorCode_IncompatibleImageFormat,
-    ErrorCode_IncompatibleImageSize,
-    ErrorCode_SharedLibrary,
-    ErrorCode_Plugin
+  enum LogLevel
+  {
+    LogLevel_Error,
+    LogLevel_Warning,
+    LogLevel_Info,
+    LogLevel_Trace
   };
 
+
   /**
    * {summary}{The memory layout of the pixels (resp. voxels) of a 2D (resp. 3D) image.}
    **/
-  enum LAAW_API PixelFormat
+  enum PixelFormat
   {
     /**
      * {summary}{Color image in RGB24 format.}
@@ -120,7 +188,7 @@
   /**
    * {summary}{The extraction mode specifies the way the values of the pixels are scaled when downloading a 2D image.}
    **/
-  enum LAAW_API ImageExtractionMode
+  enum ImageExtractionMode
   {
     /**
      * {summary}{Rescaled to 8bpp.}
@@ -232,6 +300,15 @@
   };
 
 
+  // https://en.wikipedia.org/wiki/HTTP_compression
+  enum HttpCompression
+  {
+    HttpCompression_None,
+    HttpCompression_Deflate,
+    HttpCompression_Gzip
+  };
+
+
   // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/
   enum Encoding
   {
@@ -243,6 +320,7 @@
     Encoding_Latin4,
     Encoding_Latin5,                        // Turkish
     Encoding_Cyrillic,
+    Encoding_Windows1251,                   // Windows-1251 (commonly used for Cyrillic)
     Encoding_Arabic,
     Encoding_Greek,
     Encoding_Hebrew,
@@ -283,6 +361,15 @@
     DicomModule_Image
   };
 
+  enum RequestOrigin
+  {
+    RequestOrigin_Unknown,
+    RequestOrigin_DicomProtocol,
+    RequestOrigin_Http,
+    RequestOrigin_Plugins,
+    RequestOrigin_Lua
+  };
+
 
   /**
    * WARNING: Do not change the explicit values in the enumerations
@@ -292,8 +379,23 @@
 
   enum CompressionType
   {
+    /**
+     * Buffer/file that is stored as-is, in a raw fashion, without
+     * compression.
+     **/
     CompressionType_None = 1,
-    CompressionType_Zlib = 2
+
+    /**
+     * Buffer that is compressed using the "deflate" algorithm (RFC
+     * 1951), wrapped inside the zlib data format (RFC 1950), prefixed
+     * with a "uint64_t" (8 bytes) that encodes the size of the
+     * uncompressed buffer. If the compressed buffer is empty, its
+     * represents an empty uncompressed buffer. This format is
+     * internal to Orthanc. If the 8 first bytes are skipped AND the
+     * buffer is non-empty, the buffer is compatible with the
+     * "deflate" HTTP compression.
+     **/
+    CompressionType_ZlibWithSize = 2
   };
 
   enum FileContentType
@@ -318,6 +420,8 @@
   };
 
 
+  const char* EnumerationToString(ErrorCode code);
+
   const char* EnumerationToString(HttpMethod method);
 
   const char* EnumerationToString(HttpStatus status);
@@ -330,16 +434,34 @@
 
   const char* EnumerationToString(PhotometricInterpretation photometric);
 
+  const char* EnumerationToString(LogLevel level);
+
+  const char* EnumerationToString(RequestOrigin origin);
+
   Encoding StringToEncoding(const char* encoding);
 
   ResourceType StringToResourceType(const char* type);
 
   ImageFormat StringToImageFormat(const char* format);
 
+  LogLevel StringToLogLevel(const char* format);
+
   unsigned int GetBytesPerPixel(PixelFormat format);
 
   bool GetDicomEncoding(Encoding& encoding,
                         const char* specificCharacterSet);
 
   const char* GetMimeType(FileContentType type);
+
+  const char* GetFileExtension(FileContentType type);
+
+  ResourceType GetChildResourceType(ResourceType type);
+
+  ResourceType GetParentResourceType(ResourceType type);
+
+  DicomModule GetModule(ResourceType type);
+
+  const char* GetDicomSpecificCharacterSet(Encoding encoding);
+
+  HttpStatus ConvertErrorCodeToHttpStatus(ErrorCode error);
 }
--- a/Core/FileStorage/CompressedFileStorageAccessor.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,180 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "../PrecompiledHeaders.h"
-#include "CompressedFileStorageAccessor.h"
-
-#include "../OrthancException.h"
-#include "FileStorageAccessor.h"
-#include "../HttpServer/BufferHttpSender.h"
-#include "../Uuid.h"
-
-#include <memory>
-#include <glog/logging.h>
-
-namespace Orthanc
-{
-  FileInfo CompressedFileStorageAccessor::WriteInternal(const void* data,
-                                                        size_t size,
-                                                        FileContentType type)
-  {
-    std::string uuid = Toolbox::GenerateUuid();
-
-    std::string md5;
-
-    if (storeMD5_)
-    {
-      Toolbox::ComputeMD5(md5, data, size);
-    }
-
-    switch (compressionType_)
-    {
-    case CompressionType_None:
-    {
-      GetStorageArea().Create(uuid.c_str(), data, size, type);
-      return FileInfo(uuid, type, size, md5);
-    }
-
-    case CompressionType_Zlib:
-    {
-      std::string compressed;
-      zlib_.Compress(compressed, data, size);
-
-      std::string compressedMD5;
-      
-      if (storeMD5_)
-      {
-        Toolbox::ComputeMD5(compressedMD5, compressed);
-      }
-
-      if (compressed.size() > 0)
-      {
-        GetStorageArea().Create(uuid.c_str(), &compressed[0], compressed.size(), type);
-      }
-      else
-      {
-        GetStorageArea().Create(uuid.c_str(), NULL, 0, type);
-      }
-
-      return FileInfo(uuid, type, size, md5,
-                      CompressionType_Zlib, compressed.size(), compressedMD5);
-    }
-
-    default:
-      throw OrthancException(ErrorCode_NotImplemented);
-    }
-  }
-
-
-  CompressedFileStorageAccessor::CompressedFileStorageAccessor() : 
-    storage_(NULL),
-    compressionType_(CompressionType_None)
-  {
-  }
-
-
-  CompressedFileStorageAccessor::CompressedFileStorageAccessor(IStorageArea& storage) : 
-    storage_(&storage),
-    compressionType_(CompressionType_None)
-  {
-  }
-
-
-  IStorageArea& CompressedFileStorageAccessor::GetStorageArea()
-  {
-    if (storage_ == NULL)
-    {
-      LOG(ERROR) << "No storage area is currently available";
-      throw OrthancException(ErrorCode_BadSequenceOfCalls);
-    }
-
-    return *storage_;
-  }
-
-
-  void CompressedFileStorageAccessor::Read(std::string& content,
-                                           const std::string& uuid,
-                                           FileContentType type)
-  {
-    switch (compressionType_)
-    {
-    case CompressionType_None:
-      GetStorageArea().Read(content, uuid, type);
-      break;
-
-    case CompressionType_Zlib:
-    {
-      std::string compressed;
-      GetStorageArea().Read(compressed, uuid, type);
-      zlib_.Uncompress(content, compressed);
-      break;
-    }
-
-    default:
-      throw OrthancException(ErrorCode_NotImplemented);
-    }
-  }
-
-  HttpFileSender* CompressedFileStorageAccessor::ConstructHttpFileSender(const std::string& uuid,
-                                                                         FileContentType type)
-  {
-    switch (compressionType_)
-    {
-    case CompressionType_None:
-    {
-      FileStorageAccessor uncompressedAccessor(GetStorageArea());
-      return uncompressedAccessor.ConstructHttpFileSender(uuid, type);
-    }
-
-    case CompressionType_Zlib:
-    {
-      std::string compressed;
-      GetStorageArea().Read(compressed, uuid, type);
-
-      std::auto_ptr<BufferHttpSender> sender(new BufferHttpSender);
-      zlib_.Uncompress(sender->GetBuffer(), compressed);
-
-      return sender.release();
-    }        
-
-    default:
-      throw OrthancException(ErrorCode_NotImplemented);
-    }
-  }
-
-
-  void  CompressedFileStorageAccessor::Remove(const std::string& uuid,
-                                              FileContentType type)
-  {
-    GetStorageArea().Remove(uuid, type);
-  }
-}
--- a/Core/FileStorage/CompressedFileStorageAccessor.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "IStorageArea.h"
-#include "StorageAccessor.h"
-#include "../Compression/ZlibCompressor.h"
-
-namespace Orthanc
-{
-  class CompressedFileStorageAccessor : public StorageAccessor
-  {
-  private:
-    IStorageArea* storage_;
-    ZlibCompressor zlib_;
-    CompressionType compressionType_;
-
-  protected:
-    virtual FileInfo WriteInternal(const void* data,
-                                   size_t size,
-                                   FileContentType type);
-
-  public: 
-    CompressedFileStorageAccessor();
-
-    CompressedFileStorageAccessor(IStorageArea& storage);
-
-    void SetStorageArea(IStorageArea& storage)
-    {
-      storage_ = &storage;
-    }
-
-    bool HasStorageArea() const
-    {
-      return storage_ != NULL;
-    }
-
-    IStorageArea& GetStorageArea();
-
-    void SetCompressionForNextOperations(CompressionType compression)
-    {
-      compressionType_ = compression;
-    }
-    
-    CompressionType GetCompressionForNextOperations()
-    {
-      return compressionType_;
-    }
-
-    virtual void Read(std::string& content,
-                      const std::string& uuid,
-                      FileContentType type);
-
-    virtual HttpFileSender* ConstructHttpFileSender(const std::string& uuid,
-                                                    FileContentType type);
-
-    virtual void Remove(const std::string& uuid,
-                        FileContentType type);
-  };
-}
--- a/Core/FileStorage/FileStorageAccessor.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "../PrecompiledHeaders.h"
-#include "FileStorageAccessor.h"
-
-#include "../HttpServer/BufferHttpSender.h"
-#include "../Uuid.h"
-
-#include <memory>
-#include <stdio.h>
-
-namespace Orthanc
-{
-  FileInfo FileStorageAccessor::WriteInternal(const void* data,
-                                              size_t size,
-                                              FileContentType type)
-  {
-    std::string md5;
-
-    if (storeMD5_)
-    {
-      Toolbox::ComputeMD5(md5, data, size);
-    }
-
-    std::string uuid = Toolbox::GenerateUuid();
-    storage_.Create(uuid.c_str(), data, size, type);
-
-    return FileInfo(uuid, type, size, md5);
-  }
-
-
-  HttpFileSender* FileStorageAccessor::ConstructHttpFileSender(const std::string& uuid,
-                                                               FileContentType type)
-  {
-    std::auto_ptr<BufferHttpSender> sender(new BufferHttpSender);
-
-    storage_.Read(sender->GetBuffer(), uuid, type);
-      
-    return sender.release();
-  }
-
-}
--- a/Core/FileStorage/FileStorageAccessor.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "StorageAccessor.h"
-#include "IStorageArea.h"
-
-namespace Orthanc
-{
-  class FileStorageAccessor : public StorageAccessor
-  {
-  private:
-    IStorageArea& storage_;
-    
-  protected:
-    virtual FileInfo WriteInternal(const void* data,
-                                   size_t size,
-                                   FileContentType type);
-
-  public:
-    FileStorageAccessor(IStorageArea& storage) : storage_(storage)
-    {
-    }
-
-    virtual void Read(std::string& content,
-                      const std::string& uuid,
-                      FileContentType type)
-    {
-      storage_.Read(content, uuid, type);
-    }
-
-    virtual HttpFileSender* ConstructHttpFileSender(const std::string& uuid,
-                                                    FileContentType type);
-
-    virtual void Remove(const std::string& uuid,
-                        FileContentType type)
-    {
-      storage_.Remove(uuid, type);
-    }
-  };
-}
--- a/Core/FileStorage/FilesystemStorage.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/FileStorage/FilesystemStorage.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -36,12 +36,13 @@
 // http://stackoverflow.com/questions/1576272/storing-large-number-of-files-in-file-system
 // http://stackoverflow.com/questions/446358/storing-a-large-number-of-images
 
+#include "../Logging.h"
 #include "../OrthancException.h"
 #include "../Toolbox.h"
 #include "../Uuid.h"
 
 #include <boost/filesystem/fstream.hpp>
-#include <glog/logging.h>
+
 
 static std::string ToString(const boost::filesystem::path& p)
 {
@@ -82,7 +83,7 @@
     //root_ = boost::filesystem::absolute(root).string();
     root_ = root;
 
-    Toolbox::CreateDirectory(root);
+    Toolbox::MakeDirectory(root);
   }
 
   void FilesystemStorage::Create(const std::string& uuid,
@@ -105,14 +106,14 @@
     {
       if (!boost::filesystem::is_directory(path.parent_path()))
       {
-        throw OrthancException("The subdirectory to be created is already occupied by a regular file");        
+        throw OrthancException(ErrorCode_DirectoryOverFile);
       }
     }
     else
     {
       if (!boost::filesystem::create_directories(path.parent_path()))
       {
-        throw OrthancException("Unable to create a subdirectory in the file storage");        
+        throw OrthancException(ErrorCode_FileStorageCannotWrite);
       }
     }
 
@@ -120,7 +121,7 @@
     f.open(path, std::ofstream::out | std::ios::binary);
     if (!f.good())
     {
-      throw OrthancException("Unable to create a new file in the file storage");
+      throw OrthancException(ErrorCode_FileStorageCannotWrite);
     }
 
     if (size != 0)
@@ -129,7 +130,7 @@
       if (!f.good())
       {
         f.close();
-        throw OrthancException("Unable to write to the new file in the file storage");
+        throw OrthancException(ErrorCode_FileStorageCannotWrite);
       }
     }
 
@@ -212,7 +213,10 @@
   void FilesystemStorage::Remove(const std::string& uuid,
                                  FileContentType /*type*/)
   {
+#if ORTHANC_ENABLE_GOOGLE_LOG == 1
     LOG(INFO) << "Deleting file " << uuid;
+#endif
+
     namespace fs = boost::filesystem;
 
     fs::path p = GetPath(uuid);
--- a/Core/FileStorage/FilesystemStorage.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/FileStorage/FilesystemStorage.h	Wed Sep 30 13:23:31 2015 +0200
@@ -34,6 +34,7 @@
 
 #include "IStorageArea.h"
 
+#include <stdint.h>
 #include <boost/filesystem.hpp>
 #include <set>
 
--- a/Core/FileStorage/IStorageArea.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/FileStorage/IStorageArea.h	Wed Sep 30 13:23:31 2015 +0200
@@ -34,6 +34,7 @@
 
 #include "../Enumerations.h"
 
+#include <string>
 #include <boost/noncopyable.hpp>
 
 namespace Orthanc
--- a/Core/FileStorage/StorageAccessor.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/FileStorage/StorageAccessor.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,31 +33,126 @@
 #include "../PrecompiledHeaders.h"
 #include "StorageAccessor.h"
 
+#include "../Compression/ZlibCompressor.h"
+#include "../OrthancException.h"
+#include "../HttpServer/HttpStreamTranscoder.h"
+
 namespace Orthanc
 {
-  FileInfo StorageAccessor::Write(const std::vector<uint8_t>& content,
-                                  FileContentType type)
+  FileInfo StorageAccessor::Write(const void* data,
+                                  size_t size,
+                                  FileContentType type,
+                                  CompressionType compression,
+                                  bool storeMd5)
   {
-    if (content.size() == 0)
+    std::string uuid = Toolbox::GenerateUuid();
+
+    std::string md5;
+
+    if (storeMd5)
+    {
+      Toolbox::ComputeMD5(md5, data, size);
+    }
+
+    switch (compression)
     {
-      return WriteInternal(NULL, 0, type);
-    }
-    else
-    {
-      return WriteInternal(&content[0], content.size(), type);
+      case CompressionType_None:
+      {
+        area_.Create(uuid, data, size, type);
+        return FileInfo(uuid, type, size, md5);
+      }
+
+      case CompressionType_ZlibWithSize:
+      {
+        ZlibCompressor zlib;
+
+        std::string compressed;
+        zlib.Compress(compressed, data, size);
+
+        std::string compressedMD5;
+      
+        if (storeMd5)
+        {
+          Toolbox::ComputeMD5(compressedMD5, compressed);
+        }
+
+        if (compressed.size() > 0)
+        {
+          area_.Create(uuid, &compressed[0], compressed.size(), type);
+        }
+        else
+        {
+          area_.Create(uuid, NULL, 0, type);
+        }
+
+        return FileInfo(uuid, type, size, md5,
+                        CompressionType_ZlibWithSize, compressed.size(), compressedMD5);
+      }
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
     }
   }
 
-  FileInfo StorageAccessor::Write(const std::string& content,
-                                  FileContentType type)
+
+  void StorageAccessor::Read(std::string& content,
+                             const FileInfo& info)
   {
-    if (content.size() == 0)
+    switch (info.GetCompressionType())
     {
-      return WriteInternal(NULL, 0, type);
+      case CompressionType_None:
+      {
+        area_.Read(content, info.GetUuid(), info.GetContentType());
+        break;
+      }
+
+      case CompressionType_ZlibWithSize:
+      {
+        ZlibCompressor zlib;
+
+        std::string compressed;
+        area_.Read(compressed, info.GetUuid(), info.GetContentType());
+        IBufferCompressor::Uncompress(content, zlib, compressed);
+        break;
+      }
+
+      default:
+      {
+        throw OrthancException(ErrorCode_NotImplemented);
+      }
     }
-    else
-    {
-      return WriteInternal(&content[0], content.size(), type);
-    }
+
+    // TODO Check the validity of the uncompressed MD5?
+  }
+
+
+  void StorageAccessor::SetupSender(BufferHttpSender& sender,
+                                    const FileInfo& info)
+  {
+    area_.Read(sender.GetBuffer(), info.GetUuid(), info.GetContentType());
+    sender.SetContentType(GetMimeType(info.GetContentType()));
+    sender.SetContentFilename(info.GetUuid() + std::string(GetFileExtension(info.GetContentType())));
+  }
+
+
+  void StorageAccessor::AnswerFile(HttpOutput& output,
+                                   const FileInfo& info)
+  {
+    BufferHttpSender sender;
+    SetupSender(sender, info);
+  
+    HttpStreamTranscoder transcoder(sender, info.GetCompressionType());
+    output.Answer(transcoder);
+  }
+
+
+  void StorageAccessor::AnswerFile(RestApiOutput& output,
+                                   const FileInfo& info)
+  {
+    BufferHttpSender sender;
+    SetupSender(sender, info);
+  
+    HttpStreamTranscoder transcoder(sender, info.GetCompressionType());
+    output.AnswerStream(transcoder);
   }
 }
--- a/Core/FileStorage/StorageAccessor.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/FileStorage/StorageAccessor.h	Wed Sep 30 13:23:31 2015 +0200
@@ -32,8 +32,10 @@
 
 #pragma once
 
+#include "IStorageArea.h"
 #include "FileInfo.h"
-#include "../HttpServer/HttpFileSender.h"
+#include "../HttpServer/BufferHttpSender.h"
+#include "../RestApi/RestApiOutput.h"
 
 #include <vector>
 #include <string>
@@ -44,54 +46,44 @@
 {
   class StorageAccessor : boost::noncopyable
   {
-  protected:
-    bool storeMD5_;
+  private:
+    IStorageArea&  area_;
 
-    virtual FileInfo WriteInternal(const void* data,
-                                   size_t size,
-                                   FileContentType type) = 0;
+    void SetupSender(BufferHttpSender& sender,
+                     const FileInfo& info);
 
   public:
-    StorageAccessor()
-    {
-      storeMD5_ = true;
-    }
-
-    virtual ~StorageAccessor()
+    StorageAccessor(IStorageArea& area) : area_(area)
     {
     }
 
-    void SetStoreMD5(bool storeMD5)
-    {
-      storeMD5_ = storeMD5;
-    }
-
-    bool IsStoreMD5() const
-    {
-      return storeMD5_;
-    }
-
     FileInfo Write(const void* data,
                    size_t size,
-                   FileContentType type)
+                   FileContentType type,
+                   CompressionType compression,
+                   bool storeMd5);
+
+    FileInfo Write(const std::string& data, 
+                   FileContentType type,
+                   CompressionType compression,
+                   bool storeMd5)
     {
-      return WriteInternal(data, size, type);
+      return Write((data.size() == 0 ? NULL : data.c_str()),
+                   data.size(), type, compression, storeMd5);
     }
 
-    FileInfo Write(const std::vector<uint8_t>& content,
-                   FileContentType type);
-
-    FileInfo Write(const std::string& content,
-                   FileContentType type);
+    void Read(std::string& content,
+              const FileInfo& info);
 
-    virtual void Read(std::string& content,
-                      const std::string& uuid,
-                      FileContentType type) = 0;
+    void Remove(const FileInfo& info)
+    {
+      area_.Remove(info.GetUuid(), info.GetContentType());
+    }
 
-    virtual void Remove(const std::string& uuid,
-                        FileContentType type) = 0;
+    void AnswerFile(HttpOutput& output,
+                    const FileInfo& info);
 
-    virtual HttpFileSender* ConstructHttpFileSender(const std::string& uuid,
-                                                    FileContentType type) = 0;
+    void AnswerFile(RestApiOutput& output,
+                    const FileInfo& info);
   };
 }
--- a/Core/HttpClient.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/HttpClient.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,11 +33,46 @@
 #include "PrecompiledHeaders.h"
 #include "HttpClient.h"
 
-#include "../Core/Toolbox.h"
-#include "../Core/OrthancException.h"
+#include "Toolbox.h"
+#include "OrthancException.h"
+#include "Logging.h"
 
 #include <string.h>
 #include <curl/curl.h>
+#include <boost/algorithm/string/predicate.hpp>
+
+
+static std::string globalCACertificates_;
+static bool globalVerifyPeers_ = true;
+static long globalTimeout_ = 0;
+
+extern "C"
+{
+  static CURLcode GetHttpStatus(CURLcode code, CURL* curl, long* status)
+  {
+    if (code == CURLE_OK)
+    {
+      code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status);
+      return code;
+    }
+    else
+    {
+      *status = 0;
+      return code;
+    }
+  }
+
+  // This is a dummy wrapper function to suppress any OpenSSL-related
+  // problem in valgrind. Inlining is prevented.
+#if defined(__GNUC__) || defined(__clang__)
+    __attribute__((noinline)) 
+#endif
+    static CURLcode OrthancHttpClientPerformSSL(CURL* curl, long* status)
+  {
+    return GetHttpStatus(curl_easy_perform(curl), curl, status);
+  }
+}
+
 
 
 namespace Orthanc
@@ -49,11 +84,32 @@
   };
 
 
+  static void ThrowException(HttpStatus status)
+  {
+    switch (status)
+    {
+      case HttpStatus_400_BadRequest:
+        throw OrthancException(ErrorCode_BadRequest);
+
+      case HttpStatus_401_Unauthorized:
+        throw OrthancException(ErrorCode_Unauthorized);
+
+      case HttpStatus_404_NotFound:
+        throw OrthancException(ErrorCode_InexistentItem);
+
+      default:
+        throw OrthancException(ErrorCode_NetworkProtocol);
+    }
+  }
+
+
+
   static CURLcode CheckCode(CURLcode code)
   {
     if (code != CURLE_OK)
     {
-      throw OrthancException("libCURL error: " + std::string(curl_easy_strerror(code)));
+      LOG(ERROR) << "libCURL error: " + std::string(curl_easy_strerror(code));
+      throw OrthancException(ErrorCode_NetworkProtocol);
     }
 
     return code;
@@ -96,10 +152,6 @@
     CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADER, 0));
     CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_FOLLOWLOCATION, 1));
 
-#if ORTHANC_SSL_ENABLED == 1
-    CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 0)); 
-#endif
-
     // This fixes the "longjmp causes uninitialized stack frame" crash
     // that happens on modern Linux versions.
     // http://stackoverflow.com/questions/9191668/error-longjmp-causes-uninitialized-stack-frame
@@ -109,7 +161,8 @@
     method_ = HttpMethod_Get;
     lastStatus_ = HttpStatus_200_Ok;
     isVerbose_ = false;
-    timeout_ = 0;
+    timeout_ = globalTimeout_;
+    verifyPeers_ = globalVerifyPeers_;
   }
 
 
@@ -163,6 +216,19 @@
     CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_URL, url_.c_str()));
     CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEDATA, &answer));
 
+    // Setup HTTPS-related options
+#if ORTHANC_SSL_ENABLED == 1
+    if (IsHttpsVerifyPeers())
+    {
+      CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CAINFO, GetHttpsCACertificates().c_str()));
+      CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 1)); 
+    }
+    else
+    {
+      CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 0)); 
+    }
+#endif
+
     // Reset the parameters from previous calls to Apply()
     CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, NULL));
     CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPGET, 0L));
@@ -229,10 +295,10 @@
     if (method_ == HttpMethod_Post ||
         method_ == HttpMethod_Put)
     {
-      if (postData_.size() > 0)
+      if (body_.size() > 0)
       {
-        CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, postData_.c_str()));
-        CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, postData_.size()));
+        CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, body_.c_str()));
+        CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, body_.size()));
       }
       else
       {
@@ -243,10 +309,19 @@
 
 
     // Do the actual request
-    CheckCode(curl_easy_perform(pimpl_->curl_));
+    CURLcode code;
+    long status = 0;
 
-    long status;
-    CheckCode(curl_easy_getinfo(pimpl_->curl_, CURLINFO_RESPONSE_CODE, &status));
+    if (boost::starts_with(url_, "https://"))
+    {
+      code = OrthancHttpClientPerformSSL(pimpl_->curl_, &status);
+    }
+    else
+    {
+      code = GetHttpStatus(curl_easy_perform(pimpl_->curl_), pimpl_->curl_, &status);
+    }
+
+    CheckCode(code);
 
     if (status == 0)
     {
@@ -284,13 +359,74 @@
   }
 
   
-  void HttpClient::GlobalInitialize()
+  const std::string& HttpClient::GetHttpsCACertificates() const
+  {
+    if (caCertificates_.empty())
+    {
+      return globalCACertificates_;
+    }
+    else
+    {
+      return caCertificates_;
+    }
+  }
+
+
+  void HttpClient::GlobalInitialize(bool httpsVerifyPeers,
+                                    const std::string& httpsVerifyCertificates)
   {
+    globalVerifyPeers_ = httpsVerifyPeers;
+    globalCACertificates_ = httpsVerifyCertificates;
+
+#if ORTHANC_SSL_ENABLED == 1
+    if (httpsVerifyPeers)
+    {
+      if (globalCACertificates_.empty())
+      {
+        LOG(WARNING) << "No certificates are provided to validate peers, "
+                     << "set \"HttpsCACertificates\" if you need to do HTTPS requests";
+      }
+      else
+      {
+        LOG(WARNING) << "HTTPS will use the CA certificates from this file: " << globalCACertificates_;
+      }
+    }
+    else
+    {
+      LOG(WARNING) << "The verification of the peers in HTTPS requests is disabled!";
+    }
+#endif
+
     CheckCode(curl_global_init(CURL_GLOBAL_DEFAULT));
   }
+
   
   void HttpClient::GlobalFinalize()
   {
     curl_global_cleanup();
   }
+
+  
+  void HttpClient::SetDefaultTimeout(long timeout)
+  {
+    LOG(INFO) << "Setting the default timeout for HTTP client connections: " << timeout << " seconds";
+    globalTimeout_ = timeout;
+  }
+
+
+  void HttpClient::ApplyAndThrowException(std::string& answer)
+  {
+    if (!Apply(answer))
+    {
+      ThrowException(GetLastStatus());
+    }
+  }
+  
+  void HttpClient::ApplyAndThrowException(Json::Value& answer)
+  {
+    if (!Apply(answer))
+    {
+      ThrowException(GetLastStatus());
+    }
+  }
 }
--- a/Core/HttpClient.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/HttpClient.h	Wed Sep 30 13:23:31 2015 +0200
@@ -32,7 +32,7 @@
 
 #pragma once
 
-#include "../Core/Enumerations.h"
+#include "Enumerations.h"
 
 #include <string>
 #include <boost/shared_ptr.hpp>
@@ -50,10 +50,12 @@
     std::string credentials_;
     HttpMethod method_;
     HttpStatus lastStatus_;
-    std::string postData_;
+    std::string body_;  // This only makes sense for POST and PUT requests
     bool isVerbose_;
     long timeout_;
     std::string proxy_;
+    bool verifyPeers_;
+    std::string caCertificates_;
 
     void Setup();
 
@@ -101,19 +103,19 @@
       return timeout_;
     }
 
-    void SetPostData(const std::string& data)
+    void SetBody(const std::string& data)
     {
-      postData_ = data;
+      body_ = data;
     }
 
-    std::string& AccessPostData()
+    std::string& GetBody()
     {
-      return postData_;
+      return body_;
     }
 
-    const std::string& AccessPostData() const
+    const std::string& GetBody() const
     {
-      return postData_;
+      return body_;
     }
 
     void SetVerbose(bool isVerbose);
@@ -140,8 +142,32 @@
       proxy_ = proxy;
     }
 
-    static void GlobalInitialize();
+    void SetHttpsVerifyPeers(bool verify)
+    {
+      verifyPeers_ = verify;
+    }
+
+    bool IsHttpsVerifyPeers() const
+    {
+      return verifyPeers_;
+    }
+
+    void SetHttpsCACertificates(const std::string& certificates)
+    {
+      caCertificates_ = certificates;
+    }
+
+    const std::string& GetHttpsCACertificates() const;
+
+    static void GlobalInitialize(bool httpsVerifyPeers,
+                                 const std::string& httpsCACertificates);
   
     static void GlobalFinalize();
+
+    static void SetDefaultTimeout(long timeout);
+
+    void ApplyAndThrowException(std::string& answer);
+
+    void ApplyAndThrowException(Json::Value& answer);
   };
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/HttpServer/BufferHttpSender.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,84 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "../PrecompiledHeaders.h"
+#include "BufferHttpSender.h"
+
+#include "../OrthancException.h"
+
+#include <cassert>
+
+namespace Orthanc
+{
+  BufferHttpSender::BufferHttpSender() :
+    position_(0), 
+    chunkSize_(0),
+    currentChunkSize_(0)
+  {
+  }
+
+
+  bool BufferHttpSender::ReadNextChunk()
+  {
+    assert(position_ + currentChunkSize_ <= buffer_.size());
+
+    position_ += currentChunkSize_;
+
+    if (position_ == buffer_.size())
+    {
+      return false;
+    }
+    else
+    {
+      currentChunkSize_ = buffer_.size() - position_;
+
+      if (chunkSize_ != 0 &&
+          currentChunkSize_ > chunkSize_)
+      {
+        currentChunkSize_ = chunkSize_;
+      }
+
+      return true;
+    }
+  }
+
+
+  const char* BufferHttpSender::GetChunkContent()
+  {
+    return buffer_.c_str() + position_;
+  }
+
+
+  size_t BufferHttpSender::GetChunkSize()
+  {
+    return currentChunkSize_;
+  }
+}
--- a/Core/HttpServer/BufferHttpSender.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/HttpServer/BufferHttpSender.h	Wed Sep 30 13:23:31 2015 +0200
@@ -38,25 +38,14 @@
   class BufferHttpSender : public HttpFileSender
   {
   private:
-    std::string buffer_;
-
-  protected:
-    virtual uint64_t GetFileSize()
-    {
-      return buffer_.size();
-    }
-
-    virtual bool SendData(HttpOutput& output)
-    {
-      if (buffer_.size())
-      {
-        output.SendBody(&buffer_[0], buffer_.size());
-      }
-
-      return true;
-    }
+    std::string  buffer_;
+    size_t       position_;
+    size_t       chunkSize_;
+    size_t       currentChunkSize_;
 
   public:
+    BufferHttpSender();
+
     std::string& GetBuffer() 
     {
       return buffer_;
@@ -66,5 +55,28 @@
     {
       return buffer_;
     }
+
+    // This is for test purpose. If "chunkSize" is set to "0" (the
+    // default), the entire buffer is consumed at once.
+    void SetChunkSize(size_t chunkSize)
+    {
+      chunkSize_ = chunkSize;
+    }
+
+
+    /**
+     * Implementation of the IHttpStreamAnswer interface.
+     **/
+
+    virtual uint64_t GetContentLength()
+    {
+      return buffer_.size();
+    }
+
+    virtual bool ReadNextChunk();
+
+    virtual const char* GetChunkContent();
+
+    virtual size_t GetChunkSize();
   };
 }
--- a/Core/HttpServer/EmbeddedResourceHttpHandler.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/HttpServer/EmbeddedResourceHttpHandler.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,11 +33,11 @@
 #include "../PrecompiledHeaders.h"
 #include "EmbeddedResourceHttpHandler.h"
 
+#include "../Logging.h"
 #include "../OrthancException.h"
 #include "HttpOutput.h"
 
 #include <stdio.h>
-#include <glog/logging.h>
 
 
 namespace Orthanc
@@ -53,11 +53,15 @@
 
   bool EmbeddedResourceHttpHandler::Handle(
     HttpOutput& output,
+    RequestOrigin /*origin*/,
+    const char* /*remoteIp*/,
+    const char* /*username*/,
     HttpMethod method,
     const UriComponents& uri,
     const Arguments& headers,
-    const Arguments& arguments,
-    const std::string&)
+    const GetArguments& arguments,
+    const char* /*bodyData*/,
+    size_t /*bodySize*/)
   {
     if (!Toolbox::IsChildUri(baseUri_, uri))
     {
@@ -80,7 +84,7 @@
       size_t size = EmbeddedResources::GetDirectoryResourceSize(resourceId_, resourcePath.c_str());
 
       output.SetContentType(contentType.c_str());
-      output.SendBody(buffer, size);
+      output.Answer(buffer, size);
     }
     catch (OrthancException&)
     {
--- a/Core/HttpServer/EmbeddedResourceHttpHandler.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/HttpServer/EmbeddedResourceHttpHandler.h	Wed Sep 30 13:23:31 2015 +0200
@@ -32,14 +32,14 @@
 
 #pragma once
 
-#include "HttpHandler.h"
+#include "IHttpHandler.h"
 
 #include <EmbeddedResources.h>   // Autogenerated file
 #include <boost/shared_ptr.hpp>
 
 namespace Orthanc
 {
-  class EmbeddedResourceHttpHandler : public HttpHandler
+  class EmbeddedResourceHttpHandler : public IHttpHandler
   {
   private:
     UriComponents baseUri_;
@@ -52,10 +52,14 @@
 
     virtual bool Handle(
       HttpOutput& output,
+      RequestOrigin origin,
+      const char* remoteIp,
+      const char* username,
       HttpMethod method,
       const UriComponents& uri,
       const Arguments& headers,
-      const Arguments& arguments,
-      const std::string&);
+      const GetArguments& arguments,
+      const char* /*bodyData*/,
+      size_t /*bodySize*/);
   };
 }
--- a/Core/HttpServer/FilesystemHttpHandler.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/HttpServer/FilesystemHttpHandler.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -50,13 +50,12 @@
 
 
   static void OutputDirectoryContent(HttpOutput& output,
+                                     const IHttpHandler::Arguments& headers,
                                      const UriComponents& uri,
                                      const boost::filesystem::path& p)
   {
     namespace fs = boost::filesystem;
 
-    output.SetContentType("text/html");
-
     std::string s;
     s += "<html>";
     s += "  <body>";
@@ -104,7 +103,8 @@
     s += "  </body>";
     s += "</html>";
 
-    output.SendBody(s);
+    output.SetContentType("text/html");
+    output.Answer(s);
   }
 
 
@@ -119,18 +119,22 @@
     if (!fs::exists(pimpl_->root_) || 
         !fs::is_directory(pimpl_->root_))
     {
-      throw OrthancException("The path does not point to a directory");
+      throw OrthancException(ErrorCode_DirectoryExpected);
     }
   }
 
 
   bool FilesystemHttpHandler::Handle(
     HttpOutput& output,
+    RequestOrigin /*origin*/,
+    const char* /*remoteIp*/,
+    const char* /*username*/,
     HttpMethod method,
     const UriComponents& uri,
     const Arguments& headers,
-    const Arguments& arguments,
-    const std::string&)
+    const GetArguments& arguments,
+    const char* /*bodyData*/,
+    size_t /*bodySize*/)
   {
     if (!Toolbox::IsChildUri(pimpl_->baseUri_, uri))
     {
@@ -154,15 +158,14 @@
 
     if (fs::exists(p) && fs::is_regular_file(p))
     {
-      FilesystemHttpSender(p).Send(output);
-
-      //output.AnswerFileAutodetectContentType(p.string());
+      FilesystemHttpSender sender(p);
+      output.Answer(sender);   // TODO COMPRESSION
     }
     else if (listDirectoryContent_ &&
              fs::exists(p) && 
              fs::is_directory(p))
     {
-      OutputDirectoryContent(output, uri, p);
+      OutputDirectoryContent(output, headers, uri, p);
     }
     else
     {
--- a/Core/HttpServer/FilesystemHttpHandler.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/HttpServer/FilesystemHttpHandler.h	Wed Sep 30 13:23:31 2015 +0200
@@ -32,13 +32,13 @@
 
 #pragma once
 
-#include "HttpHandler.h"
+#include "IHttpHandler.h"
 
 #include <boost/shared_ptr.hpp>
 
 namespace Orthanc
 {
-  class FilesystemHttpHandler : public HttpHandler
+  class FilesystemHttpHandler : public IHttpHandler
   {
   private:
     // PImpl idiom to avoid the inclusion of boost::filesystem
@@ -54,11 +54,15 @@
 
     virtual bool Handle(
       HttpOutput& output,
+      RequestOrigin origin,
+      const char* remoteIp,
+      const char* username,
       HttpMethod method,
       const UriComponents& uri,
       const Arguments& headers,
-      const Arguments& arguments,
-      const std::string&);
+      const GetArguments& arguments,
+      const char* /*bodyData*/,
+      size_t /*bodySize*/);
 
     bool IsListDirectoryContent() const
     {
--- a/Core/HttpServer/FilesystemHttpSender.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/HttpServer/FilesystemHttpSender.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -32,72 +32,45 @@
 #include "../PrecompiledHeaders.h"
 #include "FilesystemHttpSender.h"
 
-#include "../Toolbox.h"
+#include "../OrthancException.h"
 
-#include <stdio.h>
+static const size_t  CHUNK_SIZE = 64 * 1024;   // Use 64KB chunks
 
 namespace Orthanc
 {
-  void FilesystemHttpSender::Setup()
+  void FilesystemHttpSender::Initialize(const boost::filesystem::path& path)
   {
-    //SetDownloadFilename(path_.filename().string());
-
-#if BOOST_HAS_FILESYSTEM_V3 == 1
-    SetContentType(Toolbox::AutodetectMimeType(path_.filename().string()));
-#else
-    SetContentType(Toolbox::AutodetectMimeType(path_.filename()));
-#endif
-  }
+    SetContentFilename(path.filename().string());
+    file_.open(path.string().c_str(), std::ifstream::binary);
 
-  uint64_t FilesystemHttpSender::GetFileSize()
-  {
-    return Toolbox::GetFileSize(path_.string());
-  }
-
-  bool FilesystemHttpSender::SendData(HttpOutput& output)
-  {
-    FILE* fp = fopen(path_.string().c_str(), "rb");
-    if (!fp)
+    if (!file_.is_open())
     {
-      return false;
+      throw OrthancException(ErrorCode_InexistentFile);
     }
 
-    std::vector<uint8_t> buffer(1024 * 1024);  // Chunks of 1MB
-
-    for (;;)
-    {
-      size_t nbytes = fread(&buffer[0], 1, buffer.size(), fp);
-      if (nbytes == 0)
-      {
-        break;
-      }
-      else
-      {
-        output.SendBody(&buffer[0], nbytes);
-      }
-    }
-
-    fclose(fp);
-
-    return true;
+    file_.seekg(0, file_.end);
+    size_ = file_.tellg();
+    file_.seekg(0, file_.beg);
   }
 
-  FilesystemHttpSender::FilesystemHttpSender(const char* path)
-  {
-    path_ = std::string(path);
-    Setup();
-  }
 
-  FilesystemHttpSender::FilesystemHttpSender(const boost::filesystem::path& path)
+  bool FilesystemHttpSender::ReadNextChunk()
   {
-    path_ = path;
-    Setup();
-  }
+    if (chunk_.size() == 0)
+    {
+      chunk_.resize(CHUNK_SIZE);
+    }
+
+    file_.read(&chunk_[0], chunk_.size());
 
-  FilesystemHttpSender::FilesystemHttpSender(const FilesystemStorage& storage,
-                                             const std::string& uuid)
-  {
-    path_ = storage.GetPath(uuid).string();
-    Setup();
+    if ((file_.flags() & std::istream::failbit) ||
+        file_.gcount() < 0)
+    {
+      throw OrthancException(ErrorCode_CorruptedFile);
+    }
+
+    chunkSize_ = file_.gcount();
+
+    return chunkSize_ > 0;
   }
 }
--- a/Core/HttpServer/FilesystemHttpSender.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/HttpServer/FilesystemHttpSender.h	Wed Sep 30 13:23:31 2015 +0200
@@ -32,28 +32,59 @@
 #pragma once
 
 #include "HttpFileSender.h"
+#include "BufferHttpSender.h"
 #include "../FileStorage/FilesystemStorage.h"
 
+#include <fstream>
+
 namespace Orthanc
 {
   class FilesystemHttpSender : public HttpFileSender
   {
   private:
-    boost::filesystem::path path_;
-
-    void Setup();
+    std::ifstream    file_;
+    uint64_t         size_;
+    std::string      chunk_;
+    size_t           chunkSize_;
 
-  protected:
-    virtual uint64_t GetFileSize();
-
-    virtual bool SendData(HttpOutput& output);
+    void Initialize(const boost::filesystem::path& path);
 
   public:
-    FilesystemHttpSender(const char* path);
+    FilesystemHttpSender(const std::string& path)
+    {
+      Initialize(path);
+    }
 
-    FilesystemHttpSender(const boost::filesystem::path& path);
+    FilesystemHttpSender(const boost::filesystem::path& path)
+    {
+      Initialize(path);
+    }
 
     FilesystemHttpSender(const FilesystemStorage& storage,
-                         const std::string& uuid);
+                         const std::string& uuid)
+    {
+      Initialize(storage.GetPath(uuid));
+    }
+
+    /**
+     * Implementation of the IHttpStreamAnswer interface.
+     **/
+
+    virtual uint64_t GetContentLength()
+    {
+      return size_;
+    }
+
+    virtual bool ReadNextChunk();
+
+    virtual const char* GetChunkContent()
+    {
+      return chunk_.c_str();
+    }
+
+    virtual size_t GetChunkSize()
+    {
+      return chunkSize_;
+    }
   };
 }
--- a/Core/HttpServer/HttpFileSender.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/HttpServer/HttpFileSender.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -34,34 +34,45 @@
 #include "HttpFileSender.h"
 
 #include "../OrthancException.h"
+#include "../Toolbox.h"
 
 #include <boost/lexical_cast.hpp>
 
 namespace Orthanc
 {
-  void HttpFileSender::SendHeader(HttpOutput& output)
+  void HttpFileSender::SetContentFilename(const std::string& filename)
   {
-    if (contentType_.size() > 0)
-    {
-      output.SetContentType(contentType_.c_str());
-    }
+    filename_ = filename;
 
-    if (downloadFilename_.size() > 0)
+    if (contentType_.empty())
     {
-      output.SetContentFilename(downloadFilename_.c_str());
+      contentType_ = Toolbox::AutodetectMimeType(filename);
     }
-
-    output.SetContentLength(GetFileSize());
   }
 
-  void HttpFileSender::Send(HttpOutput& output)
+
+  bool HttpFileSender::HasContentFilename(std::string& filename)
   {
-    SendHeader(output);
-
-    if (!SendData(output))
+    if (filename_.empty())
+    {
+      return false;
+    }
+    else
     {
-      throw OrthancException(ErrorCode_InternalError);
-      //output.SendHeader(HttpStatus_500_InternalServerError);
+      filename = filename_;
+      return true;
+    }
+  }
+    
+  std::string HttpFileSender::GetContentType()
+  {
+    if (contentType_.empty())
+    {
+      return "application/octet-stream";
+    }
+    else
+    {
+      return contentType_;
     }
   }
 }
--- a/Core/HttpServer/HttpFileSender.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/HttpServer/HttpFileSender.h	Wed Sep 30 13:23:31 2015 +0200
@@ -36,29 +36,13 @@
 
 namespace Orthanc
 {
-  class HttpFileSender
+  class HttpFileSender : public IHttpStreamAnswer
   {
   private:
     std::string contentType_;
-    std::string downloadFilename_;
-
-    void SendHeader(HttpOutput& output);
-
-  protected:
-    virtual uint64_t GetFileSize() = 0;
-
-    virtual bool SendData(HttpOutput& output) = 0;
+    std::string filename_;
 
   public:
-    virtual ~HttpFileSender()
-    {
-    }
-
-    void ResetContentType()
-    {
-      contentType_.clear();
-    }
-
     void SetContentType(const std::string& contentType)
     {
       contentType_ = contentType;
@@ -69,21 +53,26 @@
       return contentType_;
     }
 
-    void ResetDownloadFilename()
+    void SetContentFilename(const std::string& filename);
+
+    const std::string& GetContentFilename() const
     {
-      downloadFilename_.clear();
+      return filename_;
     }
 
-    void SetDownloadFilename(const std::string& filename)
+
+    /**
+     * Implementation of the IHttpStreamAnswer interface.
+     **/
+
+    virtual HttpCompression SetupHttpCompression(bool /*gzipAllowed*/, 
+                                                 bool /*deflateAllowed*/)
     {
-      downloadFilename_ = filename;
+      return HttpCompression_None;
     }
 
-    const std::string& GetDownloadFilename() const
-    {
-      return downloadFilename_;
-    }
-
-    void Send(HttpOutput& output);
+    virtual bool HasContentFilename(std::string& filename);
+    
+    virtual std::string GetContentType();
   };
 }
--- a/Core/HttpServer/HttpHandler.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,162 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "../PrecompiledHeaders.h"
-#include "HttpHandler.h"
-
-#include <string.h>
-#include <iostream>
-
-
-namespace Orthanc
-{
-  static void SplitGETNameValue(HttpHandler::Arguments& result,
-                                const char* start,
-                                const char* end)
-  {
-    std::string name, value;
-    
-    const char* equal = strchr(start, '=');
-    if (equal == NULL || equal >= end)
-    {
-      name = std::string(start, end - start);
-      //value = "";
-    }
-    else
-    {
-      name = std::string(start, equal - start);
-      value = std::string(equal + 1, end);
-    }
-
-    Toolbox::UrlDecode(name);
-    Toolbox::UrlDecode(value);
-
-    result.insert(std::make_pair(name, value));
-  }
-
-
-  void HttpHandler::ParseGetArguments(HttpHandler::Arguments& result, const char* query)
-  {
-    const char* pos = query;
-
-    while (pos != NULL)
-    {
-      const char* ampersand = strchr(pos, '&');
-      if (ampersand)
-      {
-        SplitGETNameValue(result, pos, ampersand);
-        pos = ampersand + 1;
-      }
-      else
-      {
-        // No more ampersand, this is the last argument
-        SplitGETNameValue(result, pos, pos + strlen(pos));
-        pos = NULL;
-      }
-    }
-  }
-
-
-  void  HttpHandler::ParseGetQuery(UriComponents& uri,
-                                   HttpHandler::Arguments& getArguments, 
-                                   const char* query)
-  {
-    const char *questionMark = ::strchr(query, '?');
-    if (questionMark == NULL)
-    {
-      // No question mark in the string
-      Toolbox::SplitUriComponents(uri, query);
-      getArguments.clear();
-    }
-    else
-    {
-      Toolbox::SplitUriComponents(uri, std::string(query, questionMark));
-      HttpHandler::ParseGetArguments(getArguments, questionMark + 1);
-    }    
-  }
-
-
-  std::string HttpHandler::GetArgument(const Arguments& getArguments,
-                                       const std::string& name,
-                                       const std::string& defaultValue)
-  {
-    Arguments::const_iterator it = getArguments.find(name);
-    if (it == getArguments.end())
-    {
-      return defaultValue;
-    }
-    else
-    {
-      return it->second;
-    }
-  }
-
-
-
-  void HttpHandler::ParseCookies(HttpHandler::Arguments& result, 
-                                 const HttpHandler::Arguments& httpHeaders)
-  {
-    result.clear();
-
-    HttpHandler::Arguments::const_iterator it = httpHeaders.find("cookie");
-    if (it != httpHeaders.end())
-    {
-      const std::string& cookies = it->second;
-
-      size_t pos = 0;
-      while (pos != std::string::npos)
-      {
-        size_t nextSemicolon = cookies.find(";", pos);
-        std::string cookie;
-
-        if (nextSemicolon == std::string::npos)
-        {
-          cookie = cookies.substr(pos);
-          pos = std::string::npos;
-        }
-        else
-        {
-          cookie = cookies.substr(pos, nextSemicolon - pos);
-          pos = nextSemicolon + 1;
-        }
-
-        size_t equal = cookie.find("=");
-        if (equal != std::string::npos)
-        {
-          std::string name = Toolbox::StripSpaces(cookie.substr(0, equal));
-          std::string value = Toolbox::StripSpaces(cookie.substr(equal + 1));
-          result[name] = value;
-        }
-      }
-    }
-  }
-}
--- a/Core/HttpServer/HttpHandler.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include <map>
-#include <vector>
-#include <stdint.h>
-#include "../Toolbox.h"
-
-namespace Orthanc
-{
-  class HttpOutput;
-
-  class HttpHandler
-  {
-  public:
-    typedef std::map<std::string, std::string> Arguments;
-
-    virtual ~HttpHandler()
-    {
-    }
-
-    virtual bool Handle(HttpOutput& output,
-                        HttpMethod method,
-                        const UriComponents& uri,
-                        const Arguments& headers,
-                        const Arguments& getArguments,
-                        const std::string& postData) = 0;
-
-    static void ParseGetArguments(HttpHandler::Arguments& result, 
-                                  const char* query);
-
-    static void ParseGetQuery(UriComponents& uri,
-                              HttpHandler::Arguments& getArguments, 
-                              const char* query);
-
-    static std::string GetArgument(const Arguments& getArguments,
-                                   const std::string& name,
-                                   const std::string& defaultValue);
-
-    static void ParseCookies(HttpHandler::Arguments& result, 
-                             const HttpHandler::Arguments& httpHeaders);
-  };
-}
--- a/Core/HttpServer/HttpOutput.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/HttpServer/HttpOutput.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,13 +33,17 @@
 #include "../PrecompiledHeaders.h"
 #include "HttpOutput.h"
 
+#include "../Logging.h"
+#include "../OrthancException.h"
+#include "../Toolbox.h"
+#include "../Compression/GzipCompressor.h"
+#include "../Compression/ZlibCompressor.h"
+
 #include <iostream>
 #include <vector>
 #include <stdio.h>
-#include <glog/logging.h>
 #include <boost/lexical_cast.hpp>
-#include "../OrthancException.h"
-#include "../Toolbox.h"
+
 
 namespace Orthanc
 {
@@ -151,6 +155,11 @@
       }
     }
 
+    if (state_ == State_WritingMultipart)
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+
     if (state_ == State_WritingHeader)
     {
       // Send the HTTP header before writing the body
@@ -206,6 +215,69 @@
   }
 
 
+  void HttpOutput::StateMachine::CloseBody()
+  {
+    switch (state_)
+    {
+      case State_WritingHeader:
+        SetContentLength(0);
+        SendBody(NULL, 0);
+        break;
+
+      case State_WritingBody:
+        if (!hasContentLength_ ||
+            contentPosition_ == contentLength_)
+        {
+          state_ = State_Done;
+        }
+        else
+        {
+          LOG(ERROR) << "The body size has not reached what was declared with SetContentSize()";
+          throw OrthancException(ErrorCode_BadSequenceOfCalls);
+        }
+
+        break;
+
+      case State_WritingMultipart:
+        LOG(ERROR) << "Cannot invoke CloseBody() with multipart outputs";
+        throw OrthancException(ErrorCode_BadSequenceOfCalls);
+
+      case State_Done:
+        return;  // Ignore
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+    }      
+  }
+
+
+  HttpCompression HttpOutput::GetPreferredCompression(size_t bodySize) const
+  {
+#if 0
+    // TODO Do not compress small files?
+    if (bodySize < 512)
+    {
+      return HttpCompression_None;
+    }
+#endif
+
+    // Prefer "gzip" over "deflate" if the choice is offered
+
+    if (isGzipAllowed_)
+    {
+      return HttpCompression_Gzip;
+    }
+    else if (isDeflateAllowed_)
+    {
+      return HttpCompression_Deflate;
+    }
+    else
+    {
+      return HttpCompression_None;
+    }
+  }
+
+
   void HttpOutput::SendMethodNotAllowed(const std::string& allowed)
   {
     stateMachine_.ClearHeaders();
@@ -215,7 +287,9 @@
   }
 
 
-  void HttpOutput::SendStatus(HttpStatus status)
+  void HttpOutput::SendStatus(HttpStatus status,
+			      const char* message,
+			      size_t messageSize)
   {
     if (status == HttpStatus_200_Ok ||
         status == HttpStatus_301_MovedPermanently ||
@@ -228,7 +302,7 @@
     
     stateMachine_.ClearHeaders();
     stateMachine_.SetHttpStatus(status);
-    stateMachine_.SendBody(NULL, 0);
+    stateMachine_.SendBody(message, messageSize);
   }
 
 
@@ -249,25 +323,229 @@
     stateMachine_.SendBody(NULL, 0);
   }
 
-  void HttpOutput::SendBody(const void* buffer, size_t length)
+  void HttpOutput::Answer(const void* buffer, 
+                          size_t length)
   {
-    stateMachine_.SendBody(buffer, length);
-  }
+    if (length == 0)
+    {
+      AnswerEmpty();
+      return;
+    }
+
+    HttpCompression compression = GetPreferredCompression(length);
+
+    if (compression == HttpCompression_None)
+    {
+      stateMachine_.SetContentLength(length);
+      stateMachine_.SendBody(buffer, length);
+      return;
+    }
+
+    std::string compressed, encoding;
 
-  void HttpOutput::SendBody(const std::string& str)
-  {
-    if (str.size() == 0)
+    switch (compression)
     {
-      stateMachine_.SendBody(NULL, 0);
+      case HttpCompression_Deflate:
+      {
+        encoding = "deflate";
+        ZlibCompressor compressor;
+        // Do not prefix the buffer with its uncompressed size, to be compatible with "deflate"
+        compressor.SetPrefixWithUncompressedSize(false);  
+        compressor.Compress(compressed, buffer, length);
+        break;
+      }
+
+      case HttpCompression_Gzip:
+      {
+        encoding = "gzip";
+        GzipCompressor compressor;
+        compressor.Compress(compressed, buffer, length);
+        break;
+      }
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+    }
+
+    LOG(TRACE) << "Compressing a HTTP answer using " << encoding;
+
+    // The body is empty, do not use HTTP compression
+    if (compressed.size() == 0)
+    {
+      AnswerEmpty();
     }
     else
     {
-      stateMachine_.SendBody(str.c_str(), str.size());
+      stateMachine_.AddHeader("Content-Encoding", encoding);
+      stateMachine_.SetContentLength(compressed.size());
+      stateMachine_.SendBody(compressed.c_str(), compressed.size());
+    }
+
+    stateMachine_.CloseBody();
+  }
+
+
+  void HttpOutput::Answer(const std::string& str)
+  {
+    Answer(str.size() == 0 ? NULL : str.c_str(), str.size());
+  }
+
+
+  void HttpOutput::AnswerEmpty()
+  {
+    stateMachine_.CloseBody();
+  }
+
+
+  void HttpOutput::StateMachine::StartMultipart(const std::string& subType,
+                                                const std::string& contentType)
+  {
+    if (subType != "mixed" &&
+        subType != "related")
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    if (keepAlive_)
+    {
+      LOG(ERROR) << "Multipart answers are not implemented together with keep-alive connections";
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    if (state_ != State_WritingHeader)
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+
+    if (status_ != HttpStatus_200_Ok)
+    {
+      SendBody(NULL, 0);
+      return;
+    }
+
+    stream_.OnHttpStatusReceived(status_);
+
+    std::string header = "HTTP/1.1 200 OK\r\n";
+
+    // Possibly add the cookies
+    for (std::list<std::string>::const_iterator
+           it = headers_.begin(); it != headers_.end(); ++it)
+    {
+      if (!Toolbox::StartsWith(*it, "Set-Cookie: "))
+      {
+        LOG(ERROR) << "The only headers that can be set in multipart answers are Set-Cookie (here: " << *it << " is set)";
+        throw OrthancException(ErrorCode_BadSequenceOfCalls);
+      }
+
+      header += *it;
+    }
+
+    multipartBoundary_ = Toolbox::GenerateUuid();
+    multipartContentType_ = contentType;
+    header += "Content-Type: multipart/related; type=multipart/" + subType + "; boundary=" + multipartBoundary_ + "\r\n\r\n";
+
+    stream_.Send(true, header.c_str(), header.size());
+    state_ = State_WritingMultipart;
+  }
+
+
+  void HttpOutput::StateMachine::SendMultipartItem(const void* item, size_t length)
+  {
+    std::string header = "--" + multipartBoundary_ + "\n";
+    header += "Content-Type: " + multipartContentType_ + "\n";
+    header += "Content-Length: " + boost::lexical_cast<std::string>(length) + "\n";
+    header += "MIME-Version: 1.0\n\n";
+
+    stream_.Send(false, header.c_str(), header.size());
+
+    if (length > 0)
+    {
+      stream_.Send(false, item, length);
+    }
+
+    stream_.Send(false, "\n", 1);
+  }
+
+
+  void HttpOutput::StateMachine::CloseMultipart()
+  {
+    if (state_ != State_WritingMultipart)
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+
+    // The two lines below might throw an exception, if the client has
+    // closed the connection. Such an error is ignored.
+    try
+    {
+      std::string header = "--" + multipartBoundary_ + "--\n";
+      stream_.Send(false, header.c_str(), header.size());
+    }
+    catch (OrthancException&)
+    {
+    }
+
+    state_ = State_Done;
+  }
+
+
+  void HttpOutput::SendMultipartItem(const std::string& item)
+  {
+    if (item.size() > 0)
+    {
+      stateMachine_.SendMultipartItem(item.c_str(), item.size());
+    }
+    else
+    {
+      stateMachine_.SendMultipartItem(NULL, 0);
     }
   }
 
-  void HttpOutput::SendBody()
+
+  void HttpOutput::Answer(IHttpStreamAnswer& stream)
   {
-    stateMachine_.SendBody(NULL, 0);
+    HttpCompression compression = stream.SetupHttpCompression(isGzipAllowed_, isDeflateAllowed_);
+
+    switch (compression)
+    {
+      case HttpCompression_None:
+        break;
+
+      case HttpCompression_Gzip:
+        stateMachine_.AddHeader("Content-Encoding", "gzip");
+        break;
+
+      case HttpCompression_Deflate:
+        stateMachine_.AddHeader("Content-Encoding", "deflate");
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    stateMachine_.SetContentLength(stream.GetContentLength());
+
+    std::string contentType = stream.GetContentType();
+    if (contentType.empty())
+    {
+      contentType = "application/octet-stream";
+    }
+
+    stateMachine_.SetContentType(contentType.c_str());
+
+    std::string filename;
+    if (stream.HasContentFilename(filename))
+    {
+      SetContentFilename(filename.c_str());
+    }
+
+    while (stream.ReadNextChunk())
+    {
+      stateMachine_.SendBody(stream.GetChunkContent(),
+                             stream.GetChunkSize());
+    }
+
+    stateMachine_.CloseBody();
   }
+
 }
--- a/Core/HttpServer/HttpOutput.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/HttpServer/HttpOutput.h	Wed Sep 30 13:23:31 2015 +0200
@@ -37,7 +37,8 @@
 #include <stdint.h>
 #include "../Enumerations.h"
 #include "IHttpOutputStream.h"
-#include "HttpHandler.h"
+#include "IHttpStreamAnswer.h"
+#include "../Uuid.h"
 
 namespace Orthanc
 {
@@ -48,14 +49,16 @@
 
     class StateMachine : public boost::noncopyable
     {
-    private:
+    public:
       enum State
       {
         State_WritingHeader,      
         State_WritingBody,
+        State_WritingMultipart,
         State_Done
       };
 
+    private:
       IHttpOutputStream& stream_;
       State state_;
 
@@ -66,6 +69,9 @@
       bool keepAlive_;
       std::list<std::string> headers_;
 
+      std::string multipartBoundary_;
+      std::string multipartContentType_;
+
     public:
       StateMachine(IHttpOutputStream& stream,
                    bool isKeepAlive);
@@ -89,18 +95,71 @@
       void ClearHeaders();
 
       void SendBody(const void* buffer, size_t length);
+
+      void StartMultipart(const std::string& subType,
+                          const std::string& contentType);
+
+      void SendMultipartItem(const void* item, size_t length);
+
+      void CloseMultipart();
+
+      void CloseBody();
+
+      State GetState() const
+      {
+        return state_;
+      }
     };
 
     StateMachine stateMachine_;
+    bool         isDeflateAllowed_;
+    bool         isGzipAllowed_;
+
+    HttpCompression GetPreferredCompression(size_t bodySize) const;
 
   public:
     HttpOutput(IHttpOutputStream& stream,
                bool isKeepAlive) : 
-      stateMachine_(stream, isKeepAlive)
+      stateMachine_(stream, isKeepAlive),
+      isDeflateAllowed_(false),
+      isGzipAllowed_(false)
     {
     }
 
-    void SendStatus(HttpStatus status);
+    void SetDeflateAllowed(bool allowed)
+    {
+      isDeflateAllowed_ = allowed;
+    }
+
+    bool IsDeflateAllowed() const
+    {
+      return isDeflateAllowed_;
+    }
+
+    void SetGzipAllowed(bool allowed)
+    {
+      isGzipAllowed_ = allowed;
+    }
+
+    bool IsGzipAllowed() const
+    {
+      return isGzipAllowed_;
+    }
+
+    void SendStatus(HttpStatus status,
+		    const char* message,
+		    size_t messageSize);
+
+    void SendStatus(HttpStatus status)
+    {
+      SendStatus(status, NULL, 0);
+    }
+
+    void SendStatus(HttpStatus status,
+		    const std::string& message)
+    {
+      SendStatus(status, message.c_str(), message.size());
+    }
 
     void SetContentType(const char* contentType)
     {
@@ -112,11 +171,6 @@
       stateMachine_.SetContentFilename(filename);
     }
 
-    void SetContentLength(uint64_t length)
-    {
-      stateMachine_.SetContentLength(length);
-    }
-
     void SetCookie(const std::string& cookie,
                    const std::string& value)
     {
@@ -129,16 +183,42 @@
       stateMachine_.AddHeader(key, value);
     }
 
-    void SendBody(const void* buffer, size_t length);
+    void Answer(const void* buffer, 
+                size_t length);
 
-    void SendBody(const std::string& str);
+    void Answer(const std::string& str);
 
-    void SendBody();
+    void AnswerEmpty();
 
     void SendMethodNotAllowed(const std::string& allowed);
 
     void Redirect(const std::string& path);
 
     void SendUnauthorized(const std::string& realm);
+
+    void StartMultipart(const std::string& subType,
+                        const std::string& contentType)
+    {
+      stateMachine_.StartMultipart(subType, contentType);
+    }
+
+    void SendMultipartItem(const std::string& item);
+
+    void SendMultipartItem(const void* item, size_t size)
+    {
+      stateMachine_.SendMultipartItem(item, size);
+    }
+
+    void CloseMultipart()
+    {
+      stateMachine_.CloseMultipart();
+    }
+
+    bool IsWritingMultipart() const
+    {
+      return stateMachine_.GetState() == StateMachine::State_WritingMultipart;
+    }
+
+    void Answer(IHttpStreamAnswer& stream);
   };
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/HttpServer/HttpStreamTranscoder.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,250 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "HttpStreamTranscoder.h"
+
+#include "../OrthancException.h"
+#include "../Compression/ZlibCompressor.h"
+
+#include <string.h>   // For memcpy()
+#include <cassert>
+
+#include <stdio.h>
+
+namespace Orthanc
+{
+  void HttpStreamTranscoder::ReadSource(std::string& buffer)
+  {
+    if (source_.SetupHttpCompression(false, false) != HttpCompression_None)
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+    
+    uint64_t size = source_.GetContentLength();
+    if (static_cast<uint64_t>(static_cast<size_t>(size)) != size)
+    {
+      throw OrthancException(ErrorCode_NotEnoughMemory);
+    }
+
+    buffer.resize(static_cast<size_t>(size));
+    size_t offset = 0;
+
+    while (source_.ReadNextChunk())
+    {
+      size_t chunkSize = static_cast<size_t>(source_.GetChunkSize());
+      memcpy(&buffer[offset], source_.GetChunkContent(), chunkSize);
+      offset += chunkSize;
+    }
+
+    if (offset != size)
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+  }
+
+
+  HttpCompression HttpStreamTranscoder::SetupZlibCompression(bool deflateAllowed)
+  {
+    uint64_t size = source_.GetContentLength();
+
+    if (size == 0)
+    {
+      return HttpCompression_None;
+    }
+
+    if (size < sizeof(uint64_t))
+    {
+      throw OrthancException(ErrorCode_CorruptedFile);
+    }
+
+    if (deflateAllowed)
+    {
+      bytesToSkip_ = sizeof(uint64_t);
+
+      return HttpCompression_Deflate;
+    }
+    else
+    {
+      // TODO Use stream-based zlib decoding to reduce memory usage
+      std::string compressed;
+      ReadSource(compressed);
+
+      uncompressed_.reset(new BufferHttpSender);
+
+      ZlibCompressor compressor;
+      IBufferCompressor::Uncompress(uncompressed_->GetBuffer(), compressor, compressed);
+
+      return HttpCompression_None;
+    }
+  }
+
+
+  HttpCompression HttpStreamTranscoder::SetupHttpCompression(bool gzipAllowed,
+                                                             bool deflateAllowed)
+  {
+    if (ready_)
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+
+    ready_ = true;
+
+    switch (sourceCompression_)
+    {
+      case CompressionType_None:
+        return HttpCompression_None;
+
+      case CompressionType_ZlibWithSize:
+        return SetupZlibCompression(deflateAllowed);
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+
+  uint64_t HttpStreamTranscoder::GetContentLength()
+  {
+    if (!ready_)
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+
+    if (uncompressed_.get() != NULL)
+    {
+      return uncompressed_->GetContentLength();
+    }
+    else
+    {
+      uint64_t length = source_.GetContentLength();
+      if (length < bytesToSkip_)
+      {
+        throw OrthancException(ErrorCode_InternalError);
+      }
+
+      return length - bytesToSkip_;
+    }
+  }
+
+
+  bool HttpStreamTranscoder::ReadNextChunk()
+  {
+    if (!ready_)
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+
+    if (uncompressed_.get() != NULL)
+    {
+      return uncompressed_->ReadNextChunk();
+    }
+
+    assert(skipped_ <= bytesToSkip_);
+    if (skipped_ == bytesToSkip_)
+    {
+      // We have already skipped the first bytes of the stream
+      currentChunkOffset_ = 0;
+      return source_.ReadNextChunk();
+    }
+
+    // This condition can only be true on the first call to "ReadNextChunk()"
+    for (;;)
+    {
+      assert(skipped_ < bytesToSkip_);
+
+      bool ok = source_.ReadNextChunk();
+      if (!ok)
+      {
+        throw OrthancException(ErrorCode_CorruptedFile);
+      }
+
+      size_t remaining = static_cast<size_t>(bytesToSkip_ - skipped_);
+      size_t s = source_.GetChunkSize();
+
+      if (s < remaining)
+      {
+        skipped_ += s;
+      }
+      else if (s == remaining)
+      {
+        // We have skipped enough bytes, but we must read a new chunk
+        currentChunkOffset_ = 0;            
+        skipped_ = bytesToSkip_;
+        return source_.ReadNextChunk();
+      }
+      else
+      {
+        // We have skipped enough bytes, and we have enough data in the current chunk
+        assert(s > remaining);
+        currentChunkOffset_ = remaining;
+        skipped_ = bytesToSkip_;
+        return true;
+      }
+    }
+  }
+
+
+  const char* HttpStreamTranscoder::GetChunkContent()
+  {
+    if (!ready_)
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+
+    if (uncompressed_.get() != NULL)
+    {
+      return uncompressed_->GetChunkContent();
+    }
+    else
+    {
+      return source_.GetChunkContent() + currentChunkOffset_;
+    }
+  }
+
+  size_t HttpStreamTranscoder::GetChunkSize()
+  {
+    if (!ready_)
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+
+    if (uncompressed_.get() != NULL)
+    {
+      return uncompressed_->GetChunkSize();
+    }
+    else
+    {
+      return static_cast<size_t>(source_.GetChunkSize() - currentChunkOffset_);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/HttpServer/HttpStreamTranscoder.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,90 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "BufferHttpSender.h"
+
+#include <memory>  // For std::auto_ptr
+
+namespace Orthanc
+{
+  class HttpStreamTranscoder : public IHttpStreamAnswer
+  {
+  private:
+    IHttpStreamAnswer& source_;
+    CompressionType    sourceCompression_;
+    uint64_t           bytesToSkip_;
+    uint64_t           skipped_;
+    uint64_t           currentChunkOffset_;
+    bool               ready_;
+
+    std::auto_ptr<BufferHttpSender>  uncompressed_;
+
+    void ReadSource(std::string& buffer);
+
+    HttpCompression SetupZlibCompression(bool deflateAllowed);
+
+  public:
+    HttpStreamTranscoder(IHttpStreamAnswer& source,
+                         CompressionType compression) : 
+      source_(source),
+      sourceCompression_(compression),
+      bytesToSkip_(0),
+      skipped_(0),
+      ready_(false)
+    {
+    }
+
+    // This is the first method to be called
+    virtual HttpCompression SetupHttpCompression(bool gzipAllowed,
+                                                 bool deflateAllowed);
+
+    virtual bool HasContentFilename(std::string& filename)
+    {
+      return source_.HasContentFilename(filename);
+    }
+
+    virtual std::string GetContentType()
+    {
+      return source_.GetContentType();
+    }
+
+    virtual uint64_t GetContentLength();
+
+    virtual bool ReadNextChunk();
+
+    virtual const char* GetChunkContent();
+
+    virtual size_t GetChunkSize();
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/HttpServer/HttpToolbox.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,296 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "HttpToolbox.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <iostream>
+
+#include "HttpOutput.h"
+#include "StringHttpOutput.h"
+
+
+static const char* LOCALHOST = "localhost";
+
+
+
+namespace Orthanc
+{
+  static void SplitGETNameValue(IHttpHandler::GetArguments& result,
+                                const char* start,
+                                const char* end)
+  {
+    std::string name, value;
+    
+    const char* equal = strchr(start, '=');
+    if (equal == NULL || equal >= end)
+    {
+      name = std::string(start, end - start);
+      //value = "";
+    }
+    else
+    {
+      name = std::string(start, equal - start);
+      value = std::string(equal + 1, end);
+    }
+
+    Toolbox::UrlDecode(name);
+    Toolbox::UrlDecode(value);
+
+    result.push_back(std::make_pair(name, value));
+  }
+
+
+  void HttpToolbox::ParseGetArguments(IHttpHandler::GetArguments& result, 
+                                      const char* query)
+  {
+    const char* pos = query;
+
+    while (pos != NULL)
+    {
+      const char* ampersand = strchr(pos, '&');
+      if (ampersand)
+      {
+        SplitGETNameValue(result, pos, ampersand);
+        pos = ampersand + 1;
+      }
+      else
+      {
+        // No more ampersand, this is the last argument
+        SplitGETNameValue(result, pos, pos + strlen(pos));
+        pos = NULL;
+      }
+    }
+  }
+
+
+  void  HttpToolbox::ParseGetQuery(UriComponents& uri,
+                                   IHttpHandler::GetArguments& getArguments, 
+                                   const char* query)
+  {
+    const char *questionMark = ::strchr(query, '?');
+    if (questionMark == NULL)
+    {
+      // No question mark in the string
+      Toolbox::SplitUriComponents(uri, query);
+      getArguments.clear();
+    }
+    else
+    {
+      Toolbox::SplitUriComponents(uri, std::string(query, questionMark));
+      HttpToolbox::ParseGetArguments(getArguments, questionMark + 1);
+    }    
+  }
+
+ 
+  std::string HttpToolbox::GetArgument(const IHttpHandler::Arguments& getArguments,
+                                       const std::string& name,
+                                       const std::string& defaultValue)
+  {
+    IHttpHandler::Arguments::const_iterator it = getArguments.find(name);
+    if (it == getArguments.end())
+    {
+      return defaultValue;
+    }
+    else
+    {
+      return it->second;
+    }
+  }
+
+
+  std::string HttpToolbox::GetArgument(const IHttpHandler::GetArguments& getArguments,
+                                       const std::string& name,
+                                       const std::string& defaultValue)
+  {
+    for (size_t i = 0; i < getArguments.size(); i++)
+    {
+      if (getArguments[i].first == name)
+      {
+        return getArguments[i].second;
+      }
+    }
+
+    return defaultValue;
+  }
+
+
+
+  void HttpToolbox::ParseCookies(IHttpHandler::Arguments& result, 
+                                 const IHttpHandler::Arguments& httpHeaders)
+  {
+    result.clear();
+
+    IHttpHandler::Arguments::const_iterator it = httpHeaders.find("cookie");
+    if (it != httpHeaders.end())
+    {
+      const std::string& cookies = it->second;
+
+      size_t pos = 0;
+      while (pos != std::string::npos)
+      {
+        size_t nextSemicolon = cookies.find(";", pos);
+        std::string cookie;
+
+        if (nextSemicolon == std::string::npos)
+        {
+          cookie = cookies.substr(pos);
+          pos = std::string::npos;
+        }
+        else
+        {
+          cookie = cookies.substr(pos, nextSemicolon - pos);
+          pos = nextSemicolon + 1;
+        }
+
+        size_t equal = cookie.find("=");
+        if (equal != std::string::npos)
+        {
+          std::string name = Toolbox::StripSpaces(cookie.substr(0, equal));
+          std::string value = Toolbox::StripSpaces(cookie.substr(equal + 1));
+          result[name] = value;
+        }
+      }
+    }
+  }
+
+
+  void HttpToolbox::CompileGetArguments(IHttpHandler::Arguments& compiled,
+                                        const IHttpHandler::GetArguments& source)
+  {
+    compiled.clear();
+
+    for (size_t i = 0; i < source.size(); i++)
+    {
+      compiled[source[i].first] = source[i].second;
+    }
+  }
+
+
+  bool HttpToolbox::SimpleGet(std::string& result,
+                              IHttpHandler& handler,
+                              RequestOrigin origin,
+                              const std::string& uri)
+  {
+    IHttpHandler::Arguments headers;  // No HTTP header
+
+    UriComponents curi;
+    IHttpHandler::GetArguments getArguments;
+    ParseGetQuery(curi, getArguments, uri.c_str());
+
+    StringHttpOutput stream;
+    HttpOutput http(stream, false /* no keep alive */);
+
+    if (handler.Handle(http, origin, LOCALHOST, "", HttpMethod_Get, curi, 
+                       headers, getArguments, NULL /* no body for GET */, 0))
+    {
+      stream.GetOutput(result);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+  static bool SimplePostOrPut(std::string& result,
+                              IHttpHandler& handler,
+                              RequestOrigin origin,
+                              HttpMethod method,
+                              const std::string& uri,
+                              const char* bodyData,
+                              size_t bodySize)
+  {
+    IHttpHandler::Arguments headers;  // No HTTP header
+    IHttpHandler::GetArguments getArguments;  // No GET argument for POST/PUT
+
+    UriComponents curi;
+    Toolbox::SplitUriComponents(curi, uri);
+
+    StringHttpOutput stream;
+    HttpOutput http(stream, false /* no keep alive */);
+
+    if (handler.Handle(http, origin, LOCALHOST, "", method, curi, 
+                       headers, getArguments, bodyData, bodySize))
+    {
+      stream.GetOutput(result);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+  bool HttpToolbox::SimplePost(std::string& result,
+                               IHttpHandler& handler,
+                               RequestOrigin origin,
+                               const std::string& uri,
+                               const char* bodyData,
+                               size_t bodySize)
+  {
+    return SimplePostOrPut(result, handler, origin, HttpMethod_Post, uri, bodyData, bodySize);
+  }
+
+
+  bool HttpToolbox::SimplePut(std::string& result,
+                              IHttpHandler& handler,
+                              RequestOrigin origin,
+                              const std::string& uri,
+                              const char* bodyData,
+                              size_t bodySize)
+  {
+    return SimplePostOrPut(result, handler, origin, HttpMethod_Put, uri, bodyData, bodySize);
+  }
+
+
+  bool HttpToolbox::SimpleDelete(IHttpHandler& handler,
+                                 RequestOrigin origin,
+                                 const std::string& uri)
+  {
+    UriComponents curi;
+    Toolbox::SplitUriComponents(curi, uri);
+
+    IHttpHandler::Arguments headers;  // No HTTP header
+    IHttpHandler::GetArguments getArguments;  // No GET argument for DELETE
+
+    StringHttpOutput stream;
+    HttpOutput http(stream, false /* no keep alive */);
+
+    return handler.Handle(http, origin, LOCALHOST, "", HttpMethod_Delete, curi, 
+                          headers, getArguments, NULL /* no body for DELETE */, 0);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/HttpServer/HttpToolbox.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,86 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IHttpHandler.h"
+
+namespace Orthanc
+{
+  class HttpToolbox
+  {
+  public:
+    static void ParseGetArguments(IHttpHandler::GetArguments& result, 
+                                  const char* query);
+
+    static void ParseGetQuery(UriComponents& uri,
+                              IHttpHandler::GetArguments& getArguments, 
+                              const char* query);
+
+    static std::string GetArgument(const IHttpHandler::Arguments& getArguments,
+                                   const std::string& name,
+                                   const std::string& defaultValue);
+
+    static std::string GetArgument(const IHttpHandler::GetArguments& getArguments,
+                                   const std::string& name,
+                                   const std::string& defaultValue);
+
+    static void ParseCookies(IHttpHandler::Arguments& result, 
+                             const IHttpHandler::Arguments& httpHeaders);
+
+    static void CompileGetArguments(IHttpHandler::Arguments& compiled,
+                                    const IHttpHandler::GetArguments& source);
+
+    static bool SimpleGet(std::string& result,
+                          IHttpHandler& handler,
+                          RequestOrigin origin,
+                          const std::string& uri);
+
+    static bool SimplePost(std::string& result,
+                           IHttpHandler& handler,
+                           RequestOrigin origin,
+                           const std::string& uri,
+                           const char* bodyData,
+                           size_t bodySize);
+
+    static bool SimplePut(std::string& result,
+                          IHttpHandler& handler,
+                          RequestOrigin origin,
+                          const std::string& uri,
+                          const char* bodyData,
+                          size_t bodySize);
+
+    static bool SimpleDelete(IHttpHandler& handler,
+                             RequestOrigin origin,
+                             const std::string& uri);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/HttpServer/IHttpHandler.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,66 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Enumerations.h"
+#include "HttpOutput.h"
+
+#include <map>
+#include <set>
+#include <vector>
+#include <string>
+
+namespace Orthanc
+{
+  class IHttpHandler : public boost::noncopyable
+  {
+  public:
+    typedef std::map<std::string, std::string>                  Arguments;
+    typedef std::vector< std::pair<std::string, std::string> >  GetArguments;
+
+    virtual ~IHttpHandler()
+    {
+    }
+
+    virtual bool Handle(HttpOutput& output,
+                        RequestOrigin origin,
+                        const char* remoteIp,
+                        const char* username,
+                        HttpMethod method,
+                        const UriComponents& uri,
+                        const Arguments& headers,
+                        const GetArguments& getArguments,
+                        const char* bodyData,
+                        size_t bodySize) = 0;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/HttpServer/IHttpStreamAnswer.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,66 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Enumerations.h"
+
+#include <stdint.h>
+#include <boost/noncopyable.hpp>
+#include <string>
+
+namespace Orthanc
+{
+  class IHttpStreamAnswer : public boost::noncopyable
+  {
+  public:
+    virtual ~IHttpStreamAnswer()
+    {
+    }
+
+    // This is the first method to be called
+    virtual HttpCompression SetupHttpCompression(bool gzipAllowed,
+                                                 bool deflateAllowed) = 0;
+
+    virtual bool HasContentFilename(std::string& filename) = 0;
+
+    virtual std::string GetContentType() = 0;
+
+    virtual uint64_t GetContentLength() = 0;
+
+    virtual bool ReadNextChunk() = 0;
+
+    virtual const char* GetChunkContent() = 0;
+
+    virtual size_t GetChunkSize() = 0;
+  };
+}
--- a/Core/HttpServer/MongooseServer.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/HttpServer/MongooseServer.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -35,6 +35,11 @@
 #include "../PrecompiledHeaders.h"
 #include "MongooseServer.h"
 
+#include "../Logging.h"
+#include "../ChunkedBuffer.h"
+#include "HttpToolbox.h"
+#include "mongoose.h"
+
 #include <algorithm>
 #include <string.h>
 #include <boost/lexical_cast.hpp>
@@ -43,12 +48,6 @@
 #include <string.h>
 #include <stdio.h>
 #include <boost/thread.hpp>
-#include <glog/logging.h>
-
-#include "../OrthancException.h"
-#include "../ChunkedBuffer.h"
-#include "HttpOutput.h"
-#include "mongoose.h"
 
 #if ORTHANC_SSL_ENABLED == 1
 #include <openssl/opensslv.h>
@@ -82,7 +81,12 @@
       {
         if (length > 0)
         {
-          mg_write(connection_, buffer, length);
+          int status = mg_write(connection_, buffer, length);
+          if (status != static_cast<int>(length))
+          {
+            // status == 0 when the connection has been closed, -1 on error
+            throw OrthancException(ErrorCode_NetworkProtocol);
+          }
         }
       }
 
@@ -262,9 +266,9 @@
 
   static PostDataStatus ReadBody(std::string& postData,
                                  struct mg_connection *connection,
-                                 const HttpHandler::Arguments& headers)
+                                 const IHttpHandler::Arguments& headers)
   {
-    HttpHandler::Arguments::const_iterator cs = headers.find("content-length");
+    IHttpHandler::Arguments::const_iterator cs = headers.find("content-length");
     if (cs == headers.end())
     {
       return PostDataStatus_NoLength;
@@ -308,7 +312,7 @@
 
   static PostDataStatus ParseMultipartPost(std::string &completedFile,
                                            struct mg_connection *connection,
-                                           const HttpHandler::Arguments& headers,
+                                           const IHttpHandler::Arguments& headers,
                                            const std::string& contentType,
                                            ChunkStore& chunkStore)
   {
@@ -322,13 +326,13 @@
       return status;
     }
 
-    /*for (HttpHandler::Arguments::const_iterator i = headers.begin(); i != headers.end(); i++)
+    /*for (IHttpHandler::Arguments::const_iterator i = headers.begin(); i != headers.end(); i++)
       {
       std::cout << "Header [" << i->first << "] = " << i->second << "\n";
       }
       printf("CHUNK\n");*/
 
-    typedef HttpHandler::Arguments::const_iterator ArgumentIterator;
+    typedef IHttpHandler::Arguments::const_iterator ArgumentIterator;
 
     ArgumentIterator requestedWith = headers.find("x-requested-with");
     ArgumentIterator fileName = headers.find("x-file-name");
@@ -410,11 +414,11 @@
 
 
   static bool IsAccessGranted(const MongooseServer& that,
-                              const HttpHandler::Arguments& headers)
+                              const IHttpHandler::Arguments& headers)
   {
     bool granted = false;
 
-    HttpHandler::Arguments::const_iterator auth = headers.find("authorization");
+    IHttpHandler::Arguments::const_iterator auth = headers.find("authorization");
     if (auth != headers.end())
     {
       std::string s = auth->second;
@@ -430,9 +434,9 @@
   }
 
 
-  static std::string GetAuthenticatedUsername(const HttpHandler::Arguments& headers)
+  static std::string GetAuthenticatedUsername(const IHttpHandler::Arguments& headers)
   {
-    HttpHandler::Arguments::const_iterator auth = headers.find("authorization");
+    IHttpHandler::Arguments::const_iterator auth = headers.find("authorization");
 
     if (auth == headers.end())
     {
@@ -465,15 +469,15 @@
 
   static bool ExtractMethod(HttpMethod& method,
                             const struct mg_request_info *request,
-                            const HttpHandler::Arguments& headers,
-                            const HttpHandler::Arguments& argumentsGET)
+                            const IHttpHandler::Arguments& headers,
+                            const IHttpHandler::GetArguments& argumentsGET)
   {
     std::string overriden;
 
     // Check whether some PUT/DELETE faking is done
 
     // 1. Faking with Google's approach
-    HttpHandler::Arguments::const_iterator methodOverride =
+    IHttpHandler::Arguments::const_iterator methodOverride =
       headers.find("x-http-method-override");
 
     if (methodOverride != headers.end())
@@ -484,10 +488,13 @@
     {
       // 2. Faking with Ruby on Rail's approach
       // GET /my/resource?_method=delete <=> DELETE /my/resource
-      methodOverride = argumentsGET.find("_method");
-      if (methodOverride != argumentsGET.end())
+      for (size_t i = 0; i < argumentsGET.size(); i++)
       {
-        overriden = methodOverride->second;
+        if (argumentsGET[i].first == "_method")
+        {
+          overriden = argumentsGET[i].second;
+          break;
+        }
       }
     }
 
@@ -540,10 +547,39 @@
   }
 
 
+  static void ConfigureHttpCompression(HttpOutput& output,
+                                       const IHttpHandler::Arguments& headers)
+  {
+    // Look if the client wishes HTTP compression
+    // https://en.wikipedia.org/wiki/HTTP_compression
+    IHttpHandler::Arguments::const_iterator it = headers.find("accept-encoding");
+    if (it != headers.end())
+    {
+      std::vector<std::string> encodings;
+      Toolbox::TokenizeString(encodings, it->second, ',');
+
+      for (size_t i = 0; i < encodings.size(); i++)
+      {
+        std::string s = Toolbox::StripSpaces(encodings[i]);
+
+        if (s == "deflate")
+        {
+          output.SetDeflateAllowed(true);
+        }
+        else if (s == "gzip")
+        {
+          output.SetGzipAllowed(true);
+        }
+      }
+    }
+  }
+
+
   static void InternalCallback(struct mg_connection *connection,
                                const struct mg_request_info *request)
   {
     MongooseServer* that = reinterpret_cast<MongooseServer*>(request->user_data);
+
     MongooseOutputStream stream(connection);
     HttpOutput output(stream, that->IsKeepAliveEnabled());
 
@@ -557,7 +593,7 @@
 
 
     // Extract the HTTP headers
-    HttpHandler::Arguments headers;
+    IHttpHandler::Arguments headers;
     for (int i = 0; i < request->num_headers; i++)
     {
       std::string name = request->http_headers[i].name;
@@ -565,12 +601,17 @@
       headers.insert(std::make_pair(name, request->http_headers[i].value));
     }
 
+    if (that->IsHttpCompressionEnabled())
+    {
+      ConfigureHttpCompression(output, headers);
+    }
+
 
     // Extract the GET arguments
-    HttpHandler::Arguments argumentsGET;
+    IHttpHandler::GetArguments argumentsGET;
     if (!strcmp(request->request_method, "GET"))
     {
-      HttpHandler::ParseGetArguments(argumentsGET, request->query_string);
+      HttpToolbox::ParseGetArguments(argumentsGET, request->query_string);
     }
 
 
@@ -592,18 +633,18 @@
 
 
     // Apply the filter, if it is installed
+    char remoteIp[24];
+    sprintf(remoteIp, "%d.%d.%d.%d", 
+            reinterpret_cast<const uint8_t*>(&request->remote_ip) [3], 
+            reinterpret_cast<const uint8_t*>(&request->remote_ip) [2], 
+            reinterpret_cast<const uint8_t*>(&request->remote_ip) [1], 
+            reinterpret_cast<const uint8_t*>(&request->remote_ip) [0]);
+
+    std::string username = GetAuthenticatedUsername(headers);
+
     const IIncomingHttpRequestFilter *filter = that->GetIncomingHttpRequestFilter();
     if (filter != NULL)
     {
-      std::string username = GetAuthenticatedUsername(headers);
-
-      char remoteIp[24];
-      sprintf(remoteIp, "%d.%d.%d.%d", 
-              reinterpret_cast<const uint8_t*>(&request->remote_ip) [3], 
-              reinterpret_cast<const uint8_t*>(&request->remote_ip) [2], 
-              reinterpret_cast<const uint8_t*>(&request->remote_ip) [1], 
-              reinterpret_cast<const uint8_t*>(&request->remote_ip) [0]);
-
       if (!filter->IsAllowed(method, request->uri, remoteIp, username.c_str()))
       {
         output.SendUnauthorized(ORTHANC_REALM);
@@ -613,13 +654,16 @@
 
 
     // Extract the body of the request for PUT and POST
+
+    // TODO Avoid unneccessary memcopy of the body
+
     std::string body;
     if (method == HttpMethod_Post ||
         method == HttpMethod_Put)
     {
       PostDataStatus status;
 
-      HttpHandler::Arguments::const_iterator ct = headers.find("content-type");
+      IHttpHandler::Arguments::const_iterator ct = headers.find("content-type");
       if (ct == headers.end())
       {
         // No content-type specified. Assume no multi-part content occurs at this point.
@@ -650,7 +694,7 @@
           return;
 
         case PostDataStatus_Pending:
-          output.SendBody();
+          output.AnswerEmpty();
           return;
 
         default:
@@ -672,58 +716,58 @@
     }
 
 
-    // Loop over the candidate handlers for this URI
     LOG(INFO) << EnumerationToString(method) << " " << Toolbox::FlattenUri(uri);
-    bool found = false;
+
 
-    for (MongooseServer::Handlers::const_iterator it = 
-           that->GetHandlers().begin(); it != that->GetHandlers().end() && !found; ++it) 
+    try
     {
+      bool found = false;
+
       try
       {
-        found = (*it)->Handle(output, method, uri, headers, argumentsGET, body);
-      }
-      catch (OrthancException& e)
-      {
-        // Using this candidate handler results in an exception
-        LOG(ERROR) << "Exception in the HTTP handler: " << e.What();
-
-        switch (e.GetErrorCode())
+        if (that->HasHandler())
         {
-          case ErrorCode_InexistentFile:
-          case ErrorCode_InexistentItem:
-          case ErrorCode_UnknownResource:
-            output.SendStatus(HttpStatus_404_NotFound);
-            break;
-
-          case ErrorCode_BadRequest:
-          case ErrorCode_UriSyntax:
-            output.SendStatus(HttpStatus_400_BadRequest);
-            break;
-
-          default:
-            output.SendStatus(HttpStatus_500_InternalServerError);
+          found = that->GetHandler().Handle(output, RequestOrigin_Http, remoteIp, username.c_str(), 
+                                            method, uri, headers, argumentsGET, body.c_str(), body.size());
         }
-
-        return;
       }
       catch (boost::bad_lexical_cast&)
       {
-        LOG(ERROR) << "Exception in the HTTP handler: Bad lexical cast";
-        output.SendStatus(HttpStatus_400_BadRequest);
-        return;
+        throw OrthancException(ErrorCode_BadParameterType);
       }
       catch (std::runtime_error&)
       {
-        LOG(ERROR) << "Exception in the HTTP handler: Presumably a bad JSON request";
-        output.SendStatus(HttpStatus_400_BadRequest);
-        return;
+        // Presumably an error while parsing the JSON body
+        throw OrthancException(ErrorCode_BadRequest);
+      }
+
+      if (!found)
+      {
+        throw OrthancException(ErrorCode_UnknownResource);
       }
     }
+    catch (OrthancException& e)
+    {
+      // Using this candidate handler results in an exception
+      try
+      {
+        if (that->GetExceptionFormatter() == NULL)
+        {
+          LOG(ERROR) << "Exception in the HTTP handler: " << e.What();
+          output.SendStatus(e.GetHttpStatus());
+        }
+        else
+        {
+          that->GetExceptionFormatter()->Format(output, e, method, request->uri);
+        }
+      }
+      catch (OrthancException&)
+      {
+        // An exception here reflects the fact that the status code
+        // was already set by the HTTP handler.
+      }
 
-    if (!found)
-    {
-      output.SendStatus(HttpStatus_404_NotFound);
+      return;
     }
   }
 
@@ -773,12 +817,15 @@
   MongooseServer::MongooseServer() : pimpl_(new PImpl)
   {
     pimpl_->context_ = NULL;
+    handler_ = NULL;
     remoteAllowed_ = false;
     authentication_ = false;
     ssl_ = false;
     port_ = 8000;
     filter_ = NULL;
     keepAlive_ = false;
+    httpCompression_ = true;
+    exceptionFormatter_ = NULL;
 
 #if ORTHANC_SSL_ENABLED == 1
     // Check for the Heartbleed exploit
@@ -795,7 +842,6 @@
   MongooseServer::~MongooseServer()
   {
     Stop();
-    ClearHandlers();
   }
 
 
@@ -845,7 +891,7 @@
 
       if (!pimpl_->context_)
       {
-        throw OrthancException("Unable to launch the Mongoose server");
+        throw OrthancException(ErrorCode_HttpPortInUse);
       }
     }
   }
@@ -860,20 +906,6 @@
   }
 
 
-  void MongooseServer::RegisterHandler(HttpHandler& handler)
-  {
-    Stop();
-
-    handlers_.push_back(&handler);
-  }
-
-
-  void MongooseServer::ClearHandlers()
-  {
-    Stop();
-  }
-
-
   void MongooseServer::ClearUsers()
   {
     Stop();
@@ -937,14 +969,47 @@
     remoteAllowed_ = allowed;
   }
 
+  void MongooseServer::SetHttpCompressionEnabled(bool enabled)
+  {
+    Stop();
+    httpCompression_ = enabled;
+    LOG(WARNING) << "HTTP compression is " << (enabled ? "enabled" : "disabled");
+  }
+  
   void MongooseServer::SetIncomingHttpRequestFilter(IIncomingHttpRequestFilter& filter)
   {
     Stop();
     filter_ = &filter;
   }
 
+
+  void MongooseServer::SetHttpExceptionFormatter(IHttpExceptionFormatter& formatter)
+  {
+    Stop();
+    exceptionFormatter_ = &formatter;
+  }
+
+
   bool MongooseServer::IsValidBasicHttpAuthentication(const std::string& basic) const
   {
     return registeredUsers_.find(basic) != registeredUsers_.end();
   }
+
+
+  void MongooseServer::Register(IHttpHandler& handler)
+  {
+    Stop();
+    handler_ = &handler;
+  }
+
+
+  IHttpHandler& MongooseServer::GetHandler() const
+  {
+    if (handler_ == NULL)
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+
+    return *handler_;
+  }
 }
--- a/Core/HttpServer/MongooseServer.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/HttpServer/MongooseServer.h	Wed Sep 30 13:23:31 2015 +0200
@@ -32,7 +32,9 @@
 
 #pragma once
 
-#include "HttpHandler.h"
+#include "IHttpHandler.h"
+
+#include "../OrthancException.h"
 
 #include <list>
 #include <map>
@@ -57,17 +59,29 @@
                            const char* username) const = 0;
   };
 
-  class MongooseServer
+
+  class IHttpExceptionFormatter
   {
   public:
-    typedef std::list<HttpHandler*> Handlers;
+    virtual ~IHttpExceptionFormatter()
+    {
+    }
 
+    virtual void Format(HttpOutput& output,
+                        const OrthancException& exception,
+                        HttpMethod method,
+                        const char* uri) = 0;
+  };
+
+
+  class MongooseServer
+  {
   private:
     // http://stackoverflow.com/questions/311166/stdauto-ptr-or-boostshared-ptr-for-pimpl-idiom
     struct PImpl;
     boost::shared_ptr<PImpl> pimpl_;
 
-    Handlers handlers_;
+    IHttpHandler *handler_;
 
     typedef std::set<std::string> RegisteredUsers;
     RegisteredUsers registeredUsers_;
@@ -79,6 +93,8 @@
     uint16_t port_;
     IIncomingHttpRequestFilter* filter_;
     bool keepAlive_;
+    bool httpCompression_;
+    IHttpExceptionFormatter* exceptionFormatter_;
   
     bool IsRunning() const;
 
@@ -103,8 +119,6 @@
     void RegisterUser(const char* username,
                       const char* password);
 
-    void RegisterHandler(HttpHandler& handler);
-
     bool IsAuthenticationEnabled() const
     {
       return authentication_;
@@ -140,6 +154,13 @@
 
     void SetRemoteAccessAllowed(bool allowed);
 
+    bool IsHttpCompressionEnabled() const
+    {
+      return httpCompression_;;
+    }
+
+    void SetHttpCompressionEnabled(bool enabled);
+
     const IIncomingHttpRequestFilter* GetIncomingHttpRequestFilter() const
     {
       return filter_;
@@ -147,15 +168,24 @@
 
     void SetIncomingHttpRequestFilter(IIncomingHttpRequestFilter& filter);
 
-    void ClearHandlers();
-
     ChunkStore& GetChunkStore();
 
     bool IsValidBasicHttpAuthentication(const std::string& basic) const;
 
-    const Handlers& GetHandlers() const
+    void Register(IHttpHandler& handler);
+
+    bool HasHandler() const
     {
-      return handlers_;
+      return handler_ != NULL;
+    }
+
+    IHttpHandler& GetHandler() const;
+
+    void SetHttpExceptionFormatter(IHttpExceptionFormatter& formatter);
+
+    IHttpExceptionFormatter* GetExceptionFormatter()
+    {
+      return exceptionFormatter_;
     }
   };
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/HttpServer/StringHttpOutput.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,55 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "StringHttpOutput.h"
+
+#include "../OrthancException.h"
+
+namespace Orthanc
+{
+  void StringHttpOutput::OnHttpStatusReceived(HttpStatus status)
+  {
+    if (status != HttpStatus_200_Ok)
+    {
+      throw OrthancException(ErrorCode_BadRequest);
+    }
+  }
+
+  void StringHttpOutput::Send(bool isHeader, const void* buffer, size_t length)
+  {
+    if (!isHeader)
+    {
+      buffer_.AddChunk(reinterpret_cast<const char*>(buffer), length);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/HttpServer/StringHttpOutput.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,56 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IHttpOutputStream.h"
+
+#include "../ChunkedBuffer.h"
+
+namespace Orthanc
+{
+  class StringHttpOutput : public IHttpOutputStream
+  {
+  private:
+    ChunkedBuffer buffer_;
+
+  public:
+    virtual void OnHttpStatusReceived(HttpStatus status);
+
+    virtual void Send(bool isHeader, const void* buffer, size_t length);
+
+    void GetOutput(std::string& output)
+    {
+      buffer_.Flatten(output);
+    }
+  };
+}
--- a/Core/ImageFormats/ImageAccessor.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,221 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "../PrecompiledHeaders.h"
-#include "ImageAccessor.h"
-
-#include "../OrthancException.h"
-#include "../ChunkedBuffer.h"
-
-#include <stdint.h>
-#include <cassert>
-#include <glog/logging.h>
-#include <boost/lexical_cast.hpp>
-
-namespace Orthanc
-{
-  template <typename PixelType>
-  static void ToMatlabStringInternal(ChunkedBuffer& target,
-                                     const ImageAccessor& source)
-  {
-    target.AddChunk("double([ ");
-
-    for (unsigned int y = 0; y < source.GetHeight(); y++)
-    {
-      const PixelType* p = reinterpret_cast<const PixelType*>(source.GetConstRow(y));
-
-      std::string s;
-      if (y > 0)
-      {
-        s = "; ";
-      }
-
-      s.reserve(source.GetWidth() * 8);
-
-      for (unsigned int x = 0; x < source.GetWidth(); x++, p++)
-      {
-        s += boost::lexical_cast<std::string>(static_cast<int>(*p)) + " ";
-      }
-
-      target.AddChunk(s);
-    }
-
-    target.AddChunk("])");
-  }
-
-
-  static void RGB24ToMatlabString(ChunkedBuffer& target,
-                                  const ImageAccessor& source)
-  {
-    assert(source.GetFormat() == PixelFormat_RGB24);
-
-    target.AddChunk("double(permute(reshape([ ");
-
-    for (unsigned int y = 0; y < source.GetHeight(); y++)
-    {
-      const uint8_t* p = reinterpret_cast<const uint8_t*>(source.GetConstRow(y));
-      
-      std::string s;
-      s.reserve(source.GetWidth() * 3 * 8);
-      
-      for (unsigned int x = 0; x < 3 * source.GetWidth(); x++, p++)
-      {
-        s += boost::lexical_cast<std::string>(static_cast<int>(*p)) + " ";
-      }
-      
-      target.AddChunk(s);
-    }
-
-    target.AddChunk("], [ 3 " + boost::lexical_cast<std::string>(source.GetHeight()) +
-                    " " + boost::lexical_cast<std::string>(source.GetWidth()) + " ]), [ 3 2 1 ]))");
-  }
-
-
-  void* ImageAccessor::GetBuffer() const
-  {
-    if (readOnly_)
-    {
-      LOG(ERROR) << "Trying to write on a read-only image";
-      throw OrthancException(ErrorCode_ReadOnly);
-    }
-
-    return buffer_;
-  }
-
-
-  const void* ImageAccessor::GetConstRow(unsigned int y) const
-  {
-    if (buffer_ != NULL)
-    {
-      return reinterpret_cast<const uint8_t*>(buffer_) + y * pitch_;
-    }
-    else
-    {
-      return NULL;
-    }
-  }
-
-
-  void* ImageAccessor::GetRow(unsigned int y) const
-  {
-    if (readOnly_)
-    {
-      LOG(ERROR) << "Trying to write on a read-only image";
-      throw OrthancException(ErrorCode_ReadOnly);
-    }
-
-    if (buffer_ != NULL)
-    {
-      return reinterpret_cast<uint8_t*>(buffer_) + y * pitch_;
-    }
-    else
-    {
-      return NULL;
-    }
-  }
-
-
-  void ImageAccessor::AssignEmpty(PixelFormat format)
-  {
-    readOnly_ = false;
-    format_ = format;
-    width_ = 0;
-    height_ = 0;
-    pitch_ = 0;
-    buffer_ = NULL;
-  }
-
-
-  void ImageAccessor::AssignReadOnly(PixelFormat format,
-                                     unsigned int width,
-                                     unsigned int height,
-                                     unsigned int pitch,
-                                     const void *buffer)
-  {
-    readOnly_ = true;
-    format_ = format;
-    width_ = width;
-    height_ = height;
-    pitch_ = pitch;
-    buffer_ = const_cast<void*>(buffer);
-
-    assert(GetBytesPerPixel() * width_ <= pitch_);
-  }
-
-
-  void ImageAccessor::AssignWritable(PixelFormat format,
-                                     unsigned int width,
-                                     unsigned int height,
-                                     unsigned int pitch,
-                                     void *buffer)
-  {
-    readOnly_ = false;
-    format_ = format;
-    width_ = width;
-    height_ = height;
-    pitch_ = pitch;
-    buffer_ = buffer;
-
-    assert(GetBytesPerPixel() * width_ <= pitch_);
-  }
-
-
-  void ImageAccessor::ToMatlabString(std::string& target) const
-  {
-    ChunkedBuffer buffer;
-
-    switch (GetFormat())
-    {
-      case PixelFormat_Grayscale8:
-        ToMatlabStringInternal<uint8_t>(buffer, *this);
-        break;
-
-      case PixelFormat_Grayscale16:
-        ToMatlabStringInternal<uint16_t>(buffer, *this);
-        break;
-
-      case PixelFormat_SignedGrayscale16:
-        ToMatlabStringInternal<int16_t>(buffer, *this);
-        break;
-
-      case PixelFormat_RGB24:
-        RGB24ToMatlabString(buffer, *this);
-        break;
-
-      default:
-        throw OrthancException(ErrorCode_NotImplemented);
-    }   
-
-    buffer.Flatten(target);
-  }
-
-}
--- a/Core/ImageFormats/ImageAccessor.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,117 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../Enumerations.h"
-
-namespace Orthanc
-{
-  class ImageAccessor
-  {
-  private:
-    bool readOnly_;
-    PixelFormat format_;
-    unsigned int width_;
-    unsigned int height_;
-    unsigned int pitch_;
-    void *buffer_;
-
-  public:
-    ImageAccessor()
-    {
-      AssignEmpty(PixelFormat_Grayscale8);
-    }
-
-    bool IsReadOnly() const
-    {
-      return readOnly_;
-    }
-
-    PixelFormat GetFormat() const
-    {
-      return format_;
-    }
-
-    unsigned int GetBytesPerPixel() const
-    {
-      return ::Orthanc::GetBytesPerPixel(format_);
-    }
-
-    unsigned int GetWidth() const
-    {
-      return width_;
-    }
-
-    unsigned int GetHeight() const
-    {
-      return height_;
-    }
-
-    unsigned int GetPitch() const
-    {
-      return pitch_;
-    }
-
-    unsigned int GetSize() const
-    {
-      return GetHeight() * GetPitch();
-    }
-
-    const void* GetConstBuffer() const
-    {
-      return buffer_;
-    }
-
-    void* GetBuffer() const;
-
-    const void* GetConstRow(unsigned int y) const;
-
-    void* GetRow(unsigned int y) const;
-
-    void AssignEmpty(PixelFormat format);
-
-    void AssignReadOnly(PixelFormat format,
-                        unsigned int width,
-                        unsigned int height,
-                        unsigned int pitch,
-                        const void *buffer);
-
-    void AssignWritable(PixelFormat format,
-                        unsigned int width,
-                        unsigned int height,
-                        unsigned int pitch,
-                        void *buffer);
-
-    void ToMatlabString(std::string& target) const; 
-  };
-}
--- a/Core/ImageFormats/ImageBuffer.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,192 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "../PrecompiledHeaders.h"
-#include "ImageBuffer.h"
-
-#include "../OrthancException.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-
-namespace Orthanc
-{
-  void ImageBuffer::Allocate()
-  {
-    if (changed_)
-    {
-      Deallocate();
-
-      /*
-        if (forceMinimalPitch_)
-        {
-        TODO: Align pitch and memory buffer to optimal size for SIMD.
-        }
-      */
-
-      pitch_ = GetBytesPerPixel() * width_;
-      size_t size = pitch_ * height_;
-
-      if (size == 0)
-      {
-        buffer_ = NULL;
-      }
-      else
-      {
-        buffer_ = malloc(size);
-        if (buffer_ == NULL)
-        {
-          throw OrthancException(ErrorCode_NotEnoughMemory);
-        }
-      }
-
-      changed_ = false;
-    }
-  }
-
-
-  void ImageBuffer::Deallocate()
-  {
-    if (buffer_ != NULL)
-    {
-      free(buffer_);
-      buffer_ = NULL;
-      changed_ = true;
-    }
-  }
-
-
-  ImageBuffer::ImageBuffer(unsigned int width,
-                           unsigned int height,
-                           PixelFormat format)
-  {
-    Initialize();
-    SetWidth(width);
-    SetHeight(height);
-    SetFormat(format);
-  }
-
-
-  void ImageBuffer::Initialize()
-  {
-    changed_ = false;
-    forceMinimalPitch_ = true;
-    format_ = PixelFormat_Grayscale8;
-    width_ = 0;
-    height_ = 0;
-    pitch_ = 0;
-    buffer_ = NULL;
-  }
-
-
-  void ImageBuffer::SetFormat(PixelFormat format)
-  {
-    if (format != format_)
-    {
-      changed_ = true;
-      format_ = format;
-    }
-  }
-
-
-  void ImageBuffer::SetWidth(unsigned int width)
-  {
-    if (width != width_)
-    {
-      changed_ = true;
-      width_ = width;     
-    }
-  }
-
-
-  void ImageBuffer::SetHeight(unsigned int height)
-  {
-    if (height != height_)
-    {
-      changed_ = true;
-      height_ = height;     
-    }
-  }
-
-
-  ImageAccessor ImageBuffer::GetAccessor()
-  {
-    Allocate();
-
-    ImageAccessor accessor;
-    accessor.AssignWritable(format_, width_, height_, pitch_, buffer_);
-    return accessor;
-  }
-
-
-  ImageAccessor ImageBuffer::GetConstAccessor()
-  {
-    Allocate();
-
-    ImageAccessor accessor;
-    accessor.AssignReadOnly(format_, width_, height_, pitch_, buffer_);
-    return accessor;
-  }
-
-
-  void ImageBuffer::SetMinimalPitchForced(bool force)
-  {
-    if (force != forceMinimalPitch_)
-    {
-      changed_ = true;
-      forceMinimalPitch_ = force;
-    }
-  }
-
-
-  void ImageBuffer::AcquireOwnership(ImageBuffer& other)
-  {
-    // Remove the content of the current image
-    Deallocate();
-
-    // Force the allocation of the other image (if not already
-    // allocated)
-    other.Allocate();
-
-    // Transfer the content of the other image
-    changed_ = false;
-    forceMinimalPitch_ = other.forceMinimalPitch_;
-    format_ = other.format_;
-    width_ = other.width_;
-    height_ = other.height_;
-    pitch_ = other.pitch_;
-    buffer_ = other.buffer_;
-
-    // Force the reinitialization of the other image
-    other.Initialize();
-  }
-}
--- a/Core/ImageFormats/ImageBuffer.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "ImageAccessor.h"
-
-#include <vector>
-#include <stdint.h>
-#include <boost/noncopyable.hpp>
-
-namespace Orthanc
-{
-  class ImageBuffer : public boost::noncopyable
-  {
-  private:
-    bool changed_;
-
-    bool forceMinimalPitch_;  // Currently unused
-    PixelFormat format_;
-    unsigned int width_;
-    unsigned int height_;
-    unsigned int pitch_;
-    void *buffer_;
-
-    void Initialize();
-    
-    void Allocate();
-
-    void Deallocate();
-
-  public:
-    ImageBuffer(unsigned int width,
-                unsigned int height,
-                PixelFormat format);
-
-    ImageBuffer()
-    {
-      Initialize();
-    }
-
-    ~ImageBuffer()
-    {
-      Deallocate();
-    }
-
-    PixelFormat GetFormat() const
-    {
-      return format_;
-    }
-
-    void SetFormat(PixelFormat format);
-
-    unsigned int GetWidth() const
-    {
-      return width_;
-    }
-
-    void SetWidth(unsigned int width);
-
-    unsigned int GetHeight() const
-    {
-      return height_;
-    }
-
-    void SetHeight(unsigned int height);
-
-    unsigned int GetBytesPerPixel() const
-    {
-      return ::Orthanc::GetBytesPerPixel(format_);
-    }
-
-    ImageAccessor GetAccessor();
-
-    ImageAccessor GetConstAccessor();
-
-    bool IsMinimalPitchForced() const
-    {
-      return forceMinimalPitch_;
-    }
-
-    void SetMinimalPitchForced(bool force);
-
-    void AcquireOwnership(ImageBuffer& other);
-  };
-}
--- a/Core/ImageFormats/ImageProcessing.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,532 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "../PrecompiledHeaders.h"
-#include "ImageProcessing.h"
-
-#include "../OrthancException.h"
-
-#include <boost/math/special_functions/round.hpp>
-
-#include <cassert>
-#include <string.h>
-#include <limits>
-#include <stdint.h>
-
-namespace Orthanc
-{
-  template <typename TargetType, typename SourceType>
-  static void ConvertInternal(ImageAccessor& target,
-                              const ImageAccessor& source)
-  {
-    const TargetType minValue = std::numeric_limits<TargetType>::min();
-    const TargetType maxValue = std::numeric_limits<TargetType>::max();
-
-    for (unsigned int y = 0; y < source.GetHeight(); y++)
-    {
-      TargetType* t = reinterpret_cast<TargetType*>(target.GetRow(y));
-      const SourceType* s = reinterpret_cast<const SourceType*>(source.GetConstRow(y));
-
-      for (unsigned int x = 0; x < source.GetWidth(); x++, t++, s++)
-      {
-        if (static_cast<int32_t>(*s) < static_cast<int32_t>(minValue))
-        {
-          *t = minValue;
-        }
-        else if (static_cast<int32_t>(*s) > static_cast<int32_t>(maxValue))
-        {
-          *t = maxValue;
-        }
-        else
-        {
-          *t = static_cast<TargetType>(*s);
-        }
-      }
-    }
-  }
-
-
-  template <typename TargetType>
-  static void ConvertColorToGrayscale(ImageAccessor& target,
-                              const ImageAccessor& source)
-  {
-    assert(source.GetFormat() == PixelFormat_RGB24);
-
-    const TargetType minValue = std::numeric_limits<TargetType>::min();
-    const TargetType maxValue = std::numeric_limits<TargetType>::max();
-
-    for (unsigned int y = 0; y < source.GetHeight(); y++)
-    {
-      TargetType* t = reinterpret_cast<TargetType*>(target.GetRow(y));
-      const uint8_t* s = reinterpret_cast<const uint8_t*>(source.GetConstRow(y));
-
-      for (unsigned int x = 0; x < source.GetWidth(); x++, t++, s += 3)
-      {
-        // Y = 0.2126 R + 0.7152 G + 0.0722 B
-        int32_t v = (2126 * static_cast<int32_t>(s[0]) +
-                     7152 * static_cast<int32_t>(s[1]) +
-                     0722 * static_cast<int32_t>(s[2])) / 1000;
-        
-        if (static_cast<int32_t>(v) < static_cast<int32_t>(minValue))
-        {
-          *t = minValue;
-        }
-        else if (static_cast<int32_t>(v) > static_cast<int32_t>(maxValue))
-        {
-          *t = maxValue;
-        }
-        else
-        {
-          *t = static_cast<TargetType>(v);
-        }
-      }
-    }
-  }
-
-
-  template <typename PixelType>
-  static void SetInternal(ImageAccessor& image,
-                          int64_t constant)
-  {
-    for (unsigned int y = 0; y < image.GetHeight(); y++)
-    {
-      PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
-
-      for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
-      {
-        *p = static_cast<PixelType>(constant);
-      }
-    }
-  }
-
-
-  template <typename PixelType>
-  static void GetMinMaxValueInternal(PixelType& minValue,
-                                     PixelType& maxValue,
-                                     const ImageAccessor& source)
-  {
-    // Deal with the special case of empty image
-    if (source.GetWidth() == 0 ||
-        source.GetHeight() == 0)
-    {
-      minValue = 0;
-      maxValue = 0;
-      return;
-    }
-
-    minValue = std::numeric_limits<PixelType>::max();
-    maxValue = std::numeric_limits<PixelType>::min();
-
-    for (unsigned int y = 0; y < source.GetHeight(); y++)
-    {
-      const PixelType* p = reinterpret_cast<const PixelType*>(source.GetConstRow(y));
-
-      for (unsigned int x = 0; x < source.GetWidth(); x++, p++)
-      {
-        if (*p < minValue)
-        {
-          minValue = *p;
-        }
-
-        if (*p > maxValue)
-        {
-          maxValue = *p;
-        }
-      }
-    }
-  }
-
-
-
-  template <typename PixelType>
-  static void AddConstantInternal(ImageAccessor& image,
-                                  int64_t constant)
-  {
-    if (constant == 0)
-    {
-      return;
-    }
-
-    const int64_t minValue = std::numeric_limits<PixelType>::min();
-    const int64_t maxValue = std::numeric_limits<PixelType>::max();
-
-    for (unsigned int y = 0; y < image.GetHeight(); y++)
-    {
-      PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
-
-      for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
-      {
-        int64_t v = static_cast<int64_t>(*p) + constant;
-
-        if (v > maxValue)
-        {
-          *p = std::numeric_limits<PixelType>::max();
-        }
-        else if (v < minValue)
-        {
-          *p = std::numeric_limits<PixelType>::min();
-        }
-        else
-        {
-          *p = static_cast<PixelType>(v);
-        }
-      }
-    }
-  }
-
-
-
-  template <typename PixelType>
-  void MultiplyConstantInternal(ImageAccessor& image,
-                                float factor)
-  {
-    if (abs(factor - 1.0f) <= std::numeric_limits<float>::epsilon())
-    {
-      return;
-    }
-
-    const int64_t minValue = std::numeric_limits<PixelType>::min();
-    const int64_t maxValue = std::numeric_limits<PixelType>::max();
-
-    for (unsigned int y = 0; y < image.GetHeight(); y++)
-    {
-      PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
-
-      for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
-      {
-        int64_t v = boost::math::llround(static_cast<float>(*p) * factor);
-
-        if (v > maxValue)
-        {
-          *p = std::numeric_limits<PixelType>::max();
-        }
-        else if (v < minValue)
-        {
-          *p = std::numeric_limits<PixelType>::min();
-        }
-        else
-        {
-          *p = static_cast<PixelType>(v);
-        }
-      }
-    }
-  }
-
-
-  template <typename PixelType>
-  void ShiftScaleInternal(ImageAccessor& image,
-                          float offset,
-                          float scaling)
-  {
-    const float minValue = static_cast<float>(std::numeric_limits<PixelType>::min());
-    const float maxValue = static_cast<float>(std::numeric_limits<PixelType>::max());
-
-    for (unsigned int y = 0; y < image.GetHeight(); y++)
-    {
-      PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
-
-      for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
-      {
-        float v = (static_cast<float>(*p) + offset) * scaling;
-
-        if (v > maxValue)
-        {
-          *p = std::numeric_limits<PixelType>::max();
-        }
-        else if (v < minValue)
-        {
-          *p = std::numeric_limits<PixelType>::min();
-        }
-        else
-        {
-          *p = static_cast<PixelType>(boost::math::iround(v));
-        }
-      }
-    }
-  }
-
-
-  void ImageProcessing::Copy(ImageAccessor& target,
-                             const ImageAccessor& source)
-  {
-    if (target.GetWidth() != source.GetWidth() ||
-        target.GetHeight() != source.GetHeight())
-    {
-      throw OrthancException(ErrorCode_IncompatibleImageSize);
-    }
-
-    if (target.GetFormat() != source.GetFormat())
-    {
-      throw OrthancException(ErrorCode_IncompatibleImageFormat);
-    }
-
-    unsigned int lineSize = GetBytesPerPixel(source.GetFormat()) * source.GetWidth();
-
-    assert(source.GetPitch() >= lineSize && target.GetPitch() >= lineSize);
-
-    for (unsigned int y = 0; y < source.GetHeight(); y++)
-    {
-      memcpy(target.GetRow(y), source.GetConstRow(y), lineSize);
-    }
-  }
-
-
-  void ImageProcessing::Convert(ImageAccessor& target,
-                                const ImageAccessor& source)
-  {
-    if (target.GetWidth() != source.GetWidth() ||
-        target.GetHeight() != source.GetHeight())
-    {
-      throw OrthancException(ErrorCode_IncompatibleImageSize);
-    }
-
-    if (source.GetFormat() == target.GetFormat())
-    {
-      Copy(target, source);
-      return;
-    }
-
-    if (target.GetFormat() == PixelFormat_Grayscale16 &&
-        source.GetFormat() == PixelFormat_Grayscale8)
-    {
-      ConvertInternal<uint16_t, uint8_t>(target, source);
-      return;
-    }
-
-    if (target.GetFormat() == PixelFormat_SignedGrayscale16 &&
-        source.GetFormat() == PixelFormat_Grayscale8)
-    {
-      ConvertInternal<int16_t, uint8_t>(target, source);
-      return;
-    }
-
-    if (target.GetFormat() == PixelFormat_Grayscale8 &&
-        source.GetFormat() == PixelFormat_Grayscale16)
-    {
-      ConvertInternal<uint8_t, uint16_t>(target, source);
-      return;
-    }
-
-    if (target.GetFormat() == PixelFormat_SignedGrayscale16 &&
-        source.GetFormat() == PixelFormat_Grayscale16)
-    {
-      ConvertInternal<int16_t, uint16_t>(target, source);
-      return;
-    }
-
-    if (target.GetFormat() == PixelFormat_Grayscale8 &&
-        source.GetFormat() == PixelFormat_SignedGrayscale16)
-    {
-      ConvertInternal<uint8_t, int16_t>(target, source);
-      return;
-    }
-
-    if (target.GetFormat() == PixelFormat_Grayscale16 &&
-        source.GetFormat() == PixelFormat_SignedGrayscale16)
-    {
-      ConvertInternal<uint16_t, int16_t>(target, source);
-      return;
-    }
-
-    if (target.GetFormat() == PixelFormat_Grayscale8 &&
-        source.GetFormat() == PixelFormat_RGB24)
-    {
-      ConvertColorToGrayscale<uint8_t>(target, source);
-      return;
-    }
-
-    if (target.GetFormat() == PixelFormat_Grayscale16 &&
-        source.GetFormat() == PixelFormat_RGB24)
-    {
-      ConvertColorToGrayscale<uint16_t>(target, source);
-      return;
-    }
-
-    if (target.GetFormat() == PixelFormat_SignedGrayscale16 &&
-        source.GetFormat() == PixelFormat_RGB24)
-    {
-      ConvertColorToGrayscale<int16_t>(target, source);
-      return;
-    }
-
-    throw OrthancException(ErrorCode_NotImplemented);
-  }
-
-
-
-  void ImageProcessing::Set(ImageAccessor& image,
-                            int64_t value)
-  {
-    switch (image.GetFormat())
-    {
-      case PixelFormat_Grayscale8:
-        SetInternal<uint8_t>(image, value);
-        return;
-
-      case PixelFormat_Grayscale16:
-        SetInternal<uint16_t>(image, value);
-        return;
-
-      case PixelFormat_SignedGrayscale16:
-        SetInternal<int16_t>(image, value);
-        return;
-
-      default:
-        throw OrthancException(ErrorCode_NotImplemented);
-    }
-  }
-
-
-  void ImageProcessing::ShiftRight(ImageAccessor& image,
-                                   unsigned int shift)
-  {
-    if (image.GetWidth() == 0 ||
-        image.GetHeight() == 0 ||
-        shift == 0)
-    {
-      // Nothing to do
-      return;
-    }
-
-    throw OrthancException(ErrorCode_NotImplemented);
-  }
-
-
-  void ImageProcessing::GetMinMaxValue(int64_t& minValue,
-                                       int64_t& maxValue,
-                                       const ImageAccessor& image)
-  {
-    switch (image.GetFormat())
-    {
-      case PixelFormat_Grayscale8:
-      {
-        uint8_t a, b;
-        GetMinMaxValueInternal<uint8_t>(a, b, image);
-        minValue = a;
-        maxValue = b;
-        break;
-      }
-
-      case PixelFormat_Grayscale16:
-      {
-        uint16_t a, b;
-        GetMinMaxValueInternal<uint16_t>(a, b, image);
-        minValue = a;
-        maxValue = b;
-        break;
-      }
-
-      case PixelFormat_SignedGrayscale16:
-      {
-        int16_t a, b;
-        GetMinMaxValueInternal<int16_t>(a, b, image);
-        minValue = a;
-        maxValue = b;
-        break;
-      }
-
-      default:
-        throw OrthancException(ErrorCode_NotImplemented);
-    }
-  }
-
-
-
-  void ImageProcessing::AddConstant(ImageAccessor& image,
-                                    int64_t value)
-  {
-    switch (image.GetFormat())
-    {
-      case PixelFormat_Grayscale8:
-        AddConstantInternal<uint8_t>(image, value);
-        return;
-
-      case PixelFormat_Grayscale16:
-        AddConstantInternal<uint16_t>(image, value);
-        return;
-
-      case PixelFormat_SignedGrayscale16:
-        AddConstantInternal<int16_t>(image, value);
-        return;
-
-      default:
-        throw OrthancException(ErrorCode_NotImplemented);
-    }
-  }
-
-
-  void ImageProcessing::MultiplyConstant(ImageAccessor& image,
-                                         float factor)
-  {
-    switch (image.GetFormat())
-    {
-      case PixelFormat_Grayscale8:
-        MultiplyConstantInternal<uint8_t>(image, factor);
-        return;
-
-      case PixelFormat_Grayscale16:
-        MultiplyConstantInternal<uint16_t>(image, factor);
-        return;
-
-      case PixelFormat_SignedGrayscale16:
-        MultiplyConstantInternal<int16_t>(image, factor);
-        return;
-
-      default:
-        throw OrthancException(ErrorCode_NotImplemented);
-    }
-  }
-
-
-  void ImageProcessing::ShiftScale(ImageAccessor& image,
-                                   float offset,
-                                   float scaling)
-  {
-    switch (image.GetFormat())
-    {
-      case PixelFormat_Grayscale8:
-        ShiftScaleInternal<uint8_t>(image, offset, scaling);
-        return;
-
-      case PixelFormat_Grayscale16:
-        ShiftScaleInternal<uint16_t>(image, offset, scaling);
-        return;
-
-      case PixelFormat_SignedGrayscale16:
-        ShiftScaleInternal<int16_t>(image, offset, scaling);
-        return;
-
-      default:
-        throw OrthancException(ErrorCode_NotImplemented);
-    }
-  }
-}
--- a/Core/ImageFormats/ImageProcessing.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "ImageAccessor.h"
-
-#include <stdint.h>
-
-namespace Orthanc
-{
-  class ImageProcessing
-  {
-  public:
-    static void Copy(ImageAccessor& target,
-                     const ImageAccessor& source);
-
-    static void Convert(ImageAccessor& target,
-                        const ImageAccessor& source);
-
-    static void Set(ImageAccessor& image,
-                    int64_t value);
-
-    static void ShiftRight(ImageAccessor& target,
-                           unsigned int shift);
-
-    static void GetMinMaxValue(int64_t& minValue,
-                               int64_t& maxValue,
-                               const ImageAccessor& image);
-
-    static void AddConstant(ImageAccessor& image,
-                            int64_t value);
-
-    static void MultiplyConstant(ImageAccessor& image,
-                                 float factor);
-
-    static void ShiftScale(ImageAccessor& image,
-                           float offset,
-                           float scaling);
-  };
-}
--- a/Core/ImageFormats/PngReader.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,313 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "../PrecompiledHeaders.h"
-#include "PngReader.h"
-
-#include "../OrthancException.h"
-#include "../Toolbox.h"
-
-#include <png.h>
-#include <string.h>  // For memcpy()
-
-namespace Orthanc
-{
-  namespace 
-  {
-    struct FileRabi
-    {
-      FILE* fp_;
-
-      FileRabi(const char* filename)
-      {
-        fp_ = fopen(filename, "rb");
-        if (!fp_)
-        {
-          throw OrthancException(ErrorCode_InexistentFile);
-        }
-      }
-
-      ~FileRabi()
-      {
-        if (fp_)
-          fclose(fp_);
-      }
-    };
-  }
-
-
-  struct PngReader::PngRabi
-  {
-    png_structp png_;
-    png_infop info_;
-    png_infop endInfo_;
-
-    void Destruct()
-    {
-      if (png_)
-      {
-        png_destroy_read_struct(&png_, &info_, &endInfo_);
-
-        png_ = NULL;
-        info_ = NULL;
-        endInfo_ = NULL;
-      }
-    }
-
-    PngRabi()
-    {
-      png_ = NULL;
-      info_ = NULL;
-      endInfo_ = NULL;
-
-      png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
-      if (!png_)
-      {
-        throw OrthancException(ErrorCode_NotEnoughMemory);
-      }
-
-      info_ = png_create_info_struct(png_);
-      if (!info_)
-      {
-        png_destroy_read_struct(&png_, NULL, NULL);
-        throw OrthancException(ErrorCode_NotEnoughMemory);
-      }
-
-      endInfo_ = png_create_info_struct(png_);
-      if (!info_)
-      {
-        png_destroy_read_struct(&png_, &info_, NULL);
-        throw OrthancException(ErrorCode_NotEnoughMemory);
-      }
-    }
-
-    ~PngRabi()
-    {
-      Destruct();
-    }
-
-    static void MemoryCallback(png_structp png_ptr, 
-                               png_bytep data, 
-                               png_size_t size);
-  };
-
-
-  void PngReader::CheckHeader(const void* header)
-  {
-    int is_png = !png_sig_cmp((png_bytep) header, 0, 8);
-    if (!is_png)
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-  }
-
-  PngReader::PngReader()
-  {
-  }
-
-  void PngReader::Read(PngRabi& rabi)
-  {
-    png_set_sig_bytes(rabi.png_, 8);
-
-    png_read_info(rabi.png_, rabi.info_);
-
-    png_uint_32 width, height;
-    int bit_depth, color_type, interlace_type;
-    int compression_type, filter_method;
-    // get size and bit-depth of the PNG-image
-    png_get_IHDR(rabi.png_, rabi.info_,
-                 &width, &height,
-                 &bit_depth, &color_type, &interlace_type,
-                 &compression_type, &filter_method);
-
-    PixelFormat format;
-    unsigned int pitch;
-
-    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 8)
-    {
-      format = PixelFormat_Grayscale8;
-      pitch = width;
-    }
-    else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 16)
-    {
-      format = PixelFormat_Grayscale16;
-      pitch = 2 * width;
-
-      if (Toolbox::DetectEndianness() == Endianness_Little)
-      {
-        png_set_swap(rabi.png_);
-      }
-    }
-    else if (color_type == PNG_COLOR_TYPE_RGB && bit_depth == 8)
-    {
-      format = PixelFormat_RGB24;
-      pitch = 3 * width;
-    }
-    else if (color_type == PNG_COLOR_TYPE_RGBA && bit_depth == 8)
-    {
-      format = PixelFormat_RGBA32;
-      pitch = 4 * width;
-    }
-    else
-    {
-      throw OrthancException(ErrorCode_NotImplemented);
-    }
-
-    data_.resize(height * pitch);
-
-    if (height == 0 || width == 0)
-    {
-      // Empty image, we are done
-      AssignEmpty(format);
-      return;
-    }
-    
-    png_read_update_info(rabi.png_, rabi.info_);
-
-    std::vector<png_bytep> rows(height);
-    for (size_t i = 0; i < height; i++)
-    {
-      rows[i] = &data_[0] + i * pitch;
-    }
-
-    png_read_image(rabi.png_, &rows[0]);
-
-    AssignReadOnly(format, width, height, pitch, &data_[0]);
-  }
-
-  void PngReader::ReadFromFile(const char* filename)
-  {
-    FileRabi f(filename);
-
-    char header[8];
-    if (fread(header, 1, 8, f.fp_) != 8)
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-
-    CheckHeader(header);
-
-    PngRabi rabi;
-
-    if (setjmp(png_jmpbuf(rabi.png_)))
-    {
-      rabi.Destruct();
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-
-    png_init_io(rabi.png_, f.fp_);
-
-    Read(rabi);
-  }
-
-
-  namespace
-  {
-    struct MemoryBuffer
-    {
-      const uint8_t* buffer_;
-      size_t size_;
-      size_t pos_;
-      bool ok_;
-    };
-  }
-
-
-  void PngReader::PngRabi::MemoryCallback(png_structp png_ptr, 
-                                          png_bytep outBytes, 
-                                          png_size_t byteCountToRead)
-  {
-    MemoryBuffer* from = reinterpret_cast<MemoryBuffer*>(png_get_io_ptr(png_ptr));
-
-    if (!from->ok_)
-    {
-      return;
-    }
-
-    if (from->pos_ + byteCountToRead > from->size_)
-    {
-      from->ok_ = false;
-      return;
-    }
-
-    memcpy(outBytes, from->buffer_ + from->pos_, byteCountToRead);
-
-    from->pos_ += byteCountToRead;
-  }
-
-
-  void PngReader::ReadFromMemory(const void* buffer,
-                                 size_t size)
-  {
-    if (size < 8)
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-
-    CheckHeader(buffer);
-
-    PngRabi rabi;
-
-    if (setjmp(png_jmpbuf(rabi.png_)))
-    {
-      rabi.Destruct();
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-
-    MemoryBuffer tmp;
-    tmp.buffer_ = reinterpret_cast<const uint8_t*>(buffer) + 8;  // We skip the header
-    tmp.size_ = size - 8;
-    tmp.pos_ = 0;
-    tmp.ok_ = true;
-
-    png_set_read_fn(rabi.png_, &tmp, PngRabi::MemoryCallback);
-
-    Read(rabi);
-
-    if (!tmp.ok_)
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-  }
-
-  void PngReader::ReadFromMemory(const std::string& buffer)
-  {
-    if (buffer.size() != 0)
-    {
-      ReadFromMemory(&buffer[0], buffer.size());
-    }
-    else
-    {
-      ReadFromMemory(NULL, 0);
-    }
-  }
-}
--- a/Core/ImageFormats/PngReader.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "ImageAccessor.h"
-
-#include "../Enumerations.h"
-
-#include <vector>
-#include <stdint.h>
-#include <boost/shared_ptr.hpp>
-
-namespace Orthanc
-{
-  class PngReader : public ImageAccessor
-  {
-  private:
-    struct PngRabi;
-
-    std::vector<uint8_t> data_;
-
-    void CheckHeader(const void* header);
-
-    void Read(PngRabi& rabi);
-
-  public:
-    PngReader();
-
-    void ReadFromFile(const char* filename);
-
-    void ReadFromFile(const std::string& filename)
-    {
-      ReadFromFile(filename.c_str());
-    }
-
-    void ReadFromMemory(const void* buffer,
-                        size_t size);
-
-    void ReadFromMemory(const std::string& buffer);
-  };
-}
--- a/Core/ImageFormats/PngWriter.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,269 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "../PrecompiledHeaders.h"
-#include "PngWriter.h"
-
-#include <vector>
-#include <stdint.h>
-#include <png.h>
-#include "../OrthancException.h"
-#include "../ChunkedBuffer.h"
-#include "../Toolbox.h"
-
-
-// http://www.libpng.org/pub/png/libpng-1.2.5-manual.html#section-4
-// http://zarb.org/~gc/html/libpng.html
-/*
-  void write_row_callback(png_ptr, png_uint_32 row, int pass)
-  {
-  }*/
-
-
-
-
-/*  bool isError_;
-
-// http://www.libpng.org/pub/png/book/chapter14.html#png.ch14.div.2
-
-static void ErrorHandler(png_structp png, png_const_charp message)
-{
-printf("** [%s]\n", message);
-
-PngWriter* that = (PngWriter*) png_get_error_ptr(png);
-that->isError_ = true;
-printf("** %d\n", (int)that);
-
-//((PngWriter*) payload)->isError_ = true;
-}
-
-static void WarningHandler(png_structp png, png_const_charp message)
-{
-  printf("++ %d\n", (int)message);
-}*/
-
-
-namespace Orthanc
-{
-  struct PngWriter::PImpl
-  {
-    png_structp png_;
-    png_infop info_;
-
-    // Filled by Prepare()
-    std::vector<uint8_t*> rows_;
-    int bitDepth_;
-    int colorType_;
-  };
-
-
-
-  PngWriter::PngWriter() : pimpl_(new PImpl)
-  {
-    pimpl_->png_ = NULL;
-    pimpl_->info_ = NULL;
-
-    pimpl_->png_ = png_create_write_struct
-      (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); //this, ErrorHandler, WarningHandler);
-    if (!pimpl_->png_)
-    {
-      throw OrthancException(ErrorCode_NotEnoughMemory);
-    }
-
-    pimpl_->info_ = png_create_info_struct(pimpl_->png_);
-    if (!pimpl_->info_)
-    {
-      png_destroy_write_struct(&pimpl_->png_, NULL);
-      throw OrthancException(ErrorCode_NotEnoughMemory);
-    }
-  }
-
-  PngWriter::~PngWriter()
-  {
-    if (pimpl_->info_)
-    {
-      png_destroy_info_struct(pimpl_->png_, &pimpl_->info_);
-    }
-
-    if (pimpl_->png_)
-    {
-      png_destroy_write_struct(&pimpl_->png_, NULL);
-    }
-  }
-
-
-
-  void PngWriter::Prepare(unsigned int width,
-                          unsigned int height,
-                          unsigned int pitch,
-                          PixelFormat format,
-                          const void* buffer)
-  {
-    pimpl_->rows_.resize(height);
-    for (unsigned int y = 0; y < height; y++)
-    {
-      pimpl_->rows_[y] = const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(buffer)) + y * pitch;
-    }
-
-    switch (format)
-    {
-    case PixelFormat_RGB24:
-      pimpl_->bitDepth_ = 8;
-      pimpl_->colorType_ = PNG_COLOR_TYPE_RGB;
-      break;
-
-    case PixelFormat_RGBA32:
-      pimpl_->bitDepth_ = 8;
-      pimpl_->colorType_ = PNG_COLOR_TYPE_RGBA;
-      break;
-
-    case PixelFormat_Grayscale8:
-      pimpl_->bitDepth_ = 8;
-      pimpl_->colorType_ = PNG_COLOR_TYPE_GRAY;
-      break;
-
-    case PixelFormat_Grayscale16:
-    case PixelFormat_SignedGrayscale16:
-      pimpl_->bitDepth_ = 16;
-      pimpl_->colorType_ = PNG_COLOR_TYPE_GRAY;
-      break;
-
-    default:
-      throw OrthancException(ErrorCode_NotImplemented);
-    }
-  }
-
-
-  void PngWriter::Compress(unsigned int width,
-                           unsigned int height,
-                           unsigned int pitch,
-                           PixelFormat format)
-  {
-    png_set_IHDR(pimpl_->png_, pimpl_->info_, width, height,
-                 pimpl_->bitDepth_, pimpl_->colorType_, PNG_INTERLACE_NONE,
-                 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
-
-    png_write_info(pimpl_->png_, pimpl_->info_);
-
-    if (height > 0)
-    {
-      switch (format)
-      {
-      case PixelFormat_Grayscale16:
-      case PixelFormat_SignedGrayscale16:
-      {
-        int transforms = 0;
-        if (Toolbox::DetectEndianness() == Endianness_Little)
-        {
-          transforms = PNG_TRANSFORM_SWAP_ENDIAN;
-        }
-
-        png_set_rows(pimpl_->png_, pimpl_->info_, &pimpl_->rows_[0]);
-        png_write_png(pimpl_->png_, pimpl_->info_, transforms, NULL);
-
-        break;
-      }
-
-      default:
-        png_write_image(pimpl_->png_, &pimpl_->rows_[0]);
-      }
-    }
-
-    png_write_end(pimpl_->png_, NULL);
-  }
-
-
-  void PngWriter::WriteToFile(const char* filename,
-                              unsigned int width,
-                              unsigned int height,
-                              unsigned int pitch,
-                              PixelFormat format,
-                              const void* buffer)
-  {
-    Prepare(width, height, pitch, format, buffer);
-
-    FILE* fp = fopen(filename, "wb");
-    if (!fp)
-    {
-      throw OrthancException(ErrorCode_CannotWriteFile);
-    }    
-
-    png_init_io(pimpl_->png_, fp);
-
-    if (setjmp(png_jmpbuf(pimpl_->png_)))
-    {
-      // Error during writing PNG
-      throw OrthancException(ErrorCode_CannotWriteFile);      
-    }
-
-    Compress(width, height, pitch, format);
-
-    fclose(fp);
-  }
-
-
-
-
-  static void MemoryCallback(png_structp png_ptr, 
-                             png_bytep data, 
-                             png_size_t size)
-  {
-    ChunkedBuffer* buffer = reinterpret_cast<ChunkedBuffer*>(png_get_io_ptr(png_ptr));
-    buffer->AddChunk(reinterpret_cast<const char*>(data), size);
-  }
-
-
-
-  void PngWriter::WriteToMemory(std::string& png,
-                                unsigned int width,
-                                unsigned int height,
-                                unsigned int pitch,
-                                PixelFormat format,
-                                const void* buffer)
-  {
-    ChunkedBuffer chunks;
-
-    Prepare(width, height, pitch, format, buffer);
-
-    if (setjmp(png_jmpbuf(pimpl_->png_)))
-    {
-      // Error during writing PNG
-      throw OrthancException(ErrorCode_InternalError);      
-    }
-
-    png_set_write_fn(pimpl_->png_, &chunks, MemoryCallback, NULL);
-
-    Compress(width, height, pitch, format);
-
-    chunks.Flatten(png);
-  }
-}
--- a/Core/ImageFormats/PngWriter.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "ImageAccessor.h"
-
-#include <boost/shared_ptr.hpp>
-#include <string>
-
-namespace Orthanc
-{
-  class PngWriter
-  {
-  private:
-    struct PImpl;
-    boost::shared_ptr<PImpl> pimpl_;
-
-    void Compress(unsigned int width,
-                  unsigned int height,
-                  unsigned int pitch,
-                  PixelFormat format);
-
-    void Prepare(unsigned int width,
-                 unsigned int height,
-                 unsigned int pitch,
-                 PixelFormat format,
-                 const void* buffer);
-
-  public:
-    PngWriter();
-
-    ~PngWriter();
-
-    void WriteToFile(const char* filename,
-                     unsigned int width,
-                     unsigned int height,
-                     unsigned int pitch,
-                     PixelFormat format,
-                     const void* buffer);
-
-    void WriteToMemory(std::string& png,
-                       unsigned int width,
-                       unsigned int height,
-                       unsigned int pitch,
-                       PixelFormat format,
-                       const void* buffer);
-
-    void WriteToFile(const char* filename,
-                     const ImageAccessor& accessor)
-    {
-      WriteToFile(filename, accessor.GetWidth(), accessor.GetHeight(),
-                  accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer());
-    }
-
-    void WriteToMemory(std::string& png,
-                       const ImageAccessor& accessor)
-    {
-      WriteToMemory(png, accessor.GetWidth(), accessor.GetHeight(),
-                    accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer());
-    }
-  };
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/Font.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,301 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "Font.h"
+
+#include "../Toolbox.h"
+#include "../OrthancException.h"
+
+#include <stdio.h>
+#include <memory>
+#include <boost/lexical_cast.hpp>
+
+namespace Orthanc
+{
+  Font::~Font()
+  {
+    for (Characters::iterator it = characters_.begin();
+         it != characters_.end(); ++it)
+    {
+      delete it->second;
+    }
+  }
+
+
+  void Font::LoadFromMemory(const std::string& font)
+  {
+    Json::Value v;
+    Json::Reader reader;
+    if (!reader.parse(font, v) ||
+        v.type() != Json::objectValue ||
+        !v.isMember("Name") ||
+        !v.isMember("Size") ||
+        !v.isMember("Characters") ||
+        v["Name"].type() != Json::stringValue ||
+        v["Size"].type() != Json::intValue ||
+        v["Characters"].type() != Json::objectValue)
+    {
+      throw OrthancException(ErrorCode_BadFont);
+    }
+
+    name_ = v["Name"].asString();
+    size_ = v["Size"].asUInt();
+    maxHeight_ = 0;
+
+    Json::Value::Members characters = v["Characters"].getMemberNames();
+
+    for (size_t i = 0; i < characters.size(); i++)
+    {
+      const Json::Value& info = v["Characters"][characters[i]];
+      if (info.type() != Json::objectValue ||
+          !info.isMember("Advance") ||
+          !info.isMember("Bitmap") ||
+          !info.isMember("Height") ||
+          !info.isMember("Top") ||
+          !info.isMember("Width") ||
+          info["Advance"].type() != Json::intValue ||
+          info["Bitmap"].type() != Json::arrayValue ||
+          info["Height"].type() != Json::intValue ||
+          info["Top"].type() != Json::intValue ||
+          info["Width"].type() != Json::intValue)
+      {
+        throw OrthancException(ErrorCode_BadFont);
+      }
+
+      std::auto_ptr<Character> c(new Character);
+      
+      c->advance_ = info["Advance"].asUInt();
+      c->height_ = info["Height"].asUInt();
+      c->top_ = info["Top"].asUInt();
+      c->width_ = info["Width"].asUInt();
+      c->bitmap_.resize(info["Bitmap"].size());
+
+      if (c->height_ > maxHeight_)
+      {
+        maxHeight_ = c->height_;
+      }
+      
+      for (Json::Value::ArrayIndex j = 0; j < info["Bitmap"].size(); j++)
+      {
+        if (info["Bitmap"][j].type() != Json::intValue)
+        {
+          throw OrthancException(ErrorCode_BadFont);
+        }
+
+        int value = info["Bitmap"][j].asInt();
+        if (value < 0 || value > 255)
+        {
+          throw OrthancException(ErrorCode_BadFont);
+        }
+
+        c->bitmap_[j] = static_cast<uint8_t>(value);
+      }
+
+      int index = boost::lexical_cast<int>(characters[i]);
+      if (index < 0 || index > 255)
+      {
+        throw OrthancException(ErrorCode_BadFont);
+      }
+
+      characters_[static_cast<char>(index)] = c.release();
+    }
+  }
+
+
+  void Font::LoadFromFile(const std::string& path)
+  {
+    std::string font;
+    Toolbox::ReadFile(font, path);
+    LoadFromMemory(font);
+  }
+
+
+  static unsigned int MyMin(unsigned int a, 
+                            unsigned int b)
+  {
+    return a < b ? a : b;
+  }
+
+
+  void Font::DrawCharacter(ImageAccessor& target,
+                           const Character& character,
+                           int x,
+                           int y,
+                           const uint8_t color[4]) const
+  {
+    // Compute the bounds of the character
+    if (x >= static_cast<int>(target.GetWidth()) ||
+        y >= static_cast<int>(target.GetHeight()))
+    {
+      // The character is out of the image
+      return;
+    }
+
+    unsigned int left = x < 0 ? -x : 0;
+    unsigned int top = y < 0 ? -y : 0;
+    unsigned int width = MyMin(character.width_, target.GetWidth() - x);
+    unsigned int height = MyMin(character.height_, target.GetHeight() - y);
+
+    unsigned int bpp = target.GetBytesPerPixel();
+
+    // Blit the font bitmap OVER the target image
+    // https://en.wikipedia.org/wiki/Alpha_compositing
+
+    for (unsigned int cy = top; cy < height; cy++)
+    {
+      uint8_t* p = reinterpret_cast<uint8_t*>(target.GetRow(y + cy)) + (x + left) * bpp;
+      unsigned int pos = cy * character.width_ + left;
+
+      switch (target.GetFormat())
+      {
+        case PixelFormat_Grayscale8:
+        {
+          assert(bpp == 1);
+          for (unsigned int cx = left; cx < width; cx++, pos++, p++)
+          {
+            uint16_t alpha = character.bitmap_[pos];
+            uint16_t value = alpha * static_cast<uint16_t>(color[0]) + (255 - alpha) * static_cast<uint16_t>(*p);
+            *p = static_cast<uint8_t>(value >> 8);
+          }
+
+          break;
+        }
+
+        case PixelFormat_RGB24:
+        {
+          assert(bpp == 3);
+          for (unsigned int cx = left; cx < width; cx++, pos++, p += 3)
+          {
+            uint16_t alpha = character.bitmap_[pos];
+            for (uint8_t i = 0; i < 3; i++)
+            {
+              uint16_t value = alpha * static_cast<uint16_t>(color[i]) + (255 - alpha) * static_cast<uint16_t>(p[i]);
+              p[i] = static_cast<uint8_t>(value >> 8);
+            }
+          }
+
+          break;
+        }
+
+        case PixelFormat_RGBA32:
+        {
+          assert(bpp == 4);
+
+          for (unsigned int cx = left; cx < width; cx++, pos++, p += 4)
+          {
+            float alpha = static_cast<float>(character.bitmap_[pos]) / 255.0f;
+            float beta = (1.0f - alpha) * static_cast<float>(p[3]) / 255.0f;
+            float denom = 1.0f / (alpha + beta);
+
+            for (uint8_t i = 0; i < 3; i++)
+            {
+              p[i] = static_cast<uint8_t>((alpha * static_cast<float>(color[i]) +
+                                           beta * static_cast<float>(p[i])) * denom);
+            }
+
+            p[3] = static_cast<uint8_t>(255.0f * (alpha + beta));
+          }
+
+          break;
+        }
+
+        default:
+          throw OrthancException(ErrorCode_NotImplemented);
+      }
+    }
+
+  }
+
+
+  void Font::DrawInternal(ImageAccessor& target,
+                          const std::string& utf8,
+                          int x,
+                          int y,
+                          const uint8_t color[4]) const
+  {
+    if (target.GetFormat() != PixelFormat_Grayscale8 &&
+        target.GetFormat() != PixelFormat_RGB24 &&
+        target.GetFormat() != PixelFormat_RGBA32)
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    int a = x;
+
+    std::string s = Toolbox::ConvertFromUtf8(utf8, Encoding_Latin1);
+
+    for (size_t i = 0; i < s.size(); i++)
+    {
+      if (s[i] == '\n')
+      {
+        // Go to the next line
+        a = x;
+        y += maxHeight_ + 1;
+      }
+      else
+      {
+        Characters::const_iterator c = characters_.find(s[i]);
+        if (c != characters_.end())
+        {
+          DrawCharacter(target, *c->second, a, y + static_cast<int>(c->second->top_), color);
+          a += c->second->advance_;
+        }
+      }
+    }
+  }
+
+
+  void Font::Draw(ImageAccessor& target,
+                  const std::string& utf8,
+                  int x,
+                  int y,
+                  uint8_t grayscale) const
+  {
+    uint8_t color[4] = { grayscale, grayscale, grayscale, 255 };
+    DrawInternal(target, utf8, x, y, color);
+  }
+
+
+  void Font::Draw(ImageAccessor& target,
+                  const std::string& utf8,
+                  int x,
+                  int y,
+                  uint8_t r,
+                  uint8_t g,
+                  uint8_t b) const
+  {
+    uint8_t color[4] = { r, g, b, 255 };
+    DrawInternal(target, utf8, x, y, color);
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/Font.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,112 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ImageAccessor.h"
+
+#include <stdint.h>
+#include <vector>
+#include <map>
+#include <boost/noncopyable.hpp>
+
+namespace Orthanc
+{
+  class Font : public boost::noncopyable
+  {
+  private:
+    struct Character
+    {
+      unsigned int  width_;
+      unsigned int  height_;
+      unsigned int  top_;
+      unsigned int  advance_;
+      std::vector<uint8_t>  bitmap_;
+    };
+
+    typedef std::map<char, Character*>  Characters;
+
+    std::string   name_;
+    unsigned int  size_;
+    Characters    characters_;
+    unsigned int  maxHeight_;
+
+    void DrawCharacter(ImageAccessor& target,
+                       const Character& character,
+                       int x,
+                       int y,
+                       const uint8_t color[4]) const;
+
+    void DrawInternal(ImageAccessor& target,
+                      const std::string& utf8,
+                      int x,
+                      int y,
+                      const uint8_t color[4]) const;
+
+  public:
+    Font() : 
+      size_(0), 
+      maxHeight_(0)
+    {
+    }
+
+    ~Font();
+
+    void LoadFromMemory(const std::string& font);
+
+    void LoadFromFile(const std::string& path);
+
+    const std::string& GetName() const
+    {
+      return name_;
+    }
+
+    unsigned int GetSize() const
+    {
+      return size_;
+    }
+
+    void Draw(ImageAccessor& target,
+              const std::string& utf8,
+              int x,
+              int y,
+              uint8_t grayscale) const;
+
+    void Draw(ImageAccessor& target,
+              const std::string& utf8,
+              int x,
+              int y,
+              uint8_t r,
+              uint8_t g,
+              uint8_t b) const;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/FontRegistry.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,86 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "FontRegistry.h"
+
+#include "../OrthancException.h"
+
+#include <memory>
+
+namespace Orthanc
+{
+  FontRegistry::~FontRegistry()
+  {
+    for (Fonts::iterator it = fonts_.begin(); it != fonts_.end(); ++it)
+    {
+      delete *it;
+    }
+  }
+
+
+  void FontRegistry::AddFromMemory(const std::string& font)
+  {
+    std::auto_ptr<Font> f(new Font);
+    f->LoadFromMemory(font);
+    fonts_.push_back(f.release());
+  }
+
+
+  void FontRegistry::AddFromFile(const std::string& path)
+  {
+    std::auto_ptr<Font> f(new Font);
+    f->LoadFromFile(path);
+    fonts_.push_back(f.release());
+  }
+
+
+  void FontRegistry::AddFromResource(EmbeddedResources::FileResourceId resource)
+  {
+    std::string content;
+    EmbeddedResources::GetFileResource(content, resource);
+    AddFromMemory(content);
+  }
+
+
+  const Font& FontRegistry::GetFont(size_t i) const
+  {
+    if (i >= fonts_.size())
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+    else
+    {
+      return *fonts_[i];
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/FontRegistry.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,64 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "Font.h"
+
+#include <EmbeddedResources.h>   // Autogenerated file
+
+namespace Orthanc
+{
+  class FontRegistry : public boost::noncopyable
+  {
+  private:
+    typedef std::vector<Font*>  Fonts;
+
+    Fonts  fonts_;
+
+  public:
+    ~FontRegistry();
+
+    void AddFromMemory(const std::string& font);
+
+    void AddFromFile(const std::string& path);
+
+    void AddFromResource(EmbeddedResources::FileResourceId resource);
+
+    size_t GetSize() const
+    {
+      return fonts_.size();
+    }
+
+    const Font& GetFont(size_t i) const;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/Image.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,55 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ImageAccessor.h"
+#include "ImageBuffer.h"
+
+namespace Orthanc
+{
+  class Image : public ImageAccessor
+  {
+  private:
+    ImageBuffer  image_;
+
+  public:
+    Image(PixelFormat format,
+          unsigned int width,
+          unsigned int height) :
+      image_(format, width, height)
+    {
+      ImageAccessor accessor = image_.GetAccessor();
+      AssignWritable(format, width, height, accessor.GetPitch(), accessor.GetBuffer());
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/ImageAccessor.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,229 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "ImageAccessor.h"
+
+#include "../Logging.h"
+#include "../OrthancException.h"
+#include "../ChunkedBuffer.h"
+
+#include <stdint.h>
+#include <cassert>
+#include <boost/lexical_cast.hpp>
+
+
+
+namespace Orthanc
+{
+  template <typename PixelType>
+  static void ToMatlabStringInternal(ChunkedBuffer& target,
+                                     const ImageAccessor& source)
+  {
+    target.AddChunk("double([ ");
+
+    for (unsigned int y = 0; y < source.GetHeight(); y++)
+    {
+      const PixelType* p = reinterpret_cast<const PixelType*>(source.GetConstRow(y));
+
+      std::string s;
+      if (y > 0)
+      {
+        s = "; ";
+      }
+
+      s.reserve(source.GetWidth() * 8);
+
+      for (unsigned int x = 0; x < source.GetWidth(); x++, p++)
+      {
+        s += boost::lexical_cast<std::string>(static_cast<int>(*p)) + " ";
+      }
+
+      target.AddChunk(s);
+    }
+
+    target.AddChunk("])");
+  }
+
+
+  static void RGB24ToMatlabString(ChunkedBuffer& target,
+                                  const ImageAccessor& source)
+  {
+    assert(source.GetFormat() == PixelFormat_RGB24);
+
+    target.AddChunk("double(permute(reshape([ ");
+
+    for (unsigned int y = 0; y < source.GetHeight(); y++)
+    {
+      const uint8_t* p = reinterpret_cast<const uint8_t*>(source.GetConstRow(y));
+      
+      std::string s;
+      s.reserve(source.GetWidth() * 3 * 8);
+      
+      for (unsigned int x = 0; x < 3 * source.GetWidth(); x++, p++)
+      {
+        s += boost::lexical_cast<std::string>(static_cast<int>(*p)) + " ";
+      }
+      
+      target.AddChunk(s);
+    }
+
+    target.AddChunk("], [ 3 " + boost::lexical_cast<std::string>(source.GetHeight()) +
+                    " " + boost::lexical_cast<std::string>(source.GetWidth()) + " ]), [ 3 2 1 ]))");
+  }
+
+
+  void* ImageAccessor::GetBuffer() const
+  {
+    if (readOnly_)
+    {
+#if ORTHANC_ENABLE_LOGGING == 1
+      LOG(ERROR) << "Trying to write on a read-only image";
+#endif
+
+      throw OrthancException(ErrorCode_ReadOnly);
+    }
+
+    return buffer_;
+  }
+
+
+  const void* ImageAccessor::GetConstRow(unsigned int y) const
+  {
+    if (buffer_ != NULL)
+    {
+      return reinterpret_cast<const uint8_t*>(buffer_) + y * pitch_;
+    }
+    else
+    {
+      return NULL;
+    }
+  }
+
+
+  void* ImageAccessor::GetRow(unsigned int y) const
+  {
+    if (readOnly_)
+    {
+#if ORTHANC_ENABLE_LOGGING == 1
+      LOG(ERROR) << "Trying to write on a read-only image";
+#endif
+
+      throw OrthancException(ErrorCode_ReadOnly);
+    }
+
+    if (buffer_ != NULL)
+    {
+      return reinterpret_cast<uint8_t*>(buffer_) + y * pitch_;
+    }
+    else
+    {
+      return NULL;
+    }
+  }
+
+
+  void ImageAccessor::AssignEmpty(PixelFormat format)
+  {
+    readOnly_ = false;
+    format_ = format;
+    width_ = 0;
+    height_ = 0;
+    pitch_ = 0;
+    buffer_ = NULL;
+  }
+
+
+  void ImageAccessor::AssignReadOnly(PixelFormat format,
+                                     unsigned int width,
+                                     unsigned int height,
+                                     unsigned int pitch,
+                                     const void *buffer)
+  {
+    readOnly_ = true;
+    format_ = format;
+    width_ = width;
+    height_ = height;
+    pitch_ = pitch;
+    buffer_ = const_cast<void*>(buffer);
+
+    assert(GetBytesPerPixel() * width_ <= pitch_);
+  }
+
+
+  void ImageAccessor::AssignWritable(PixelFormat format,
+                                     unsigned int width,
+                                     unsigned int height,
+                                     unsigned int pitch,
+                                     void *buffer)
+  {
+    readOnly_ = false;
+    format_ = format;
+    width_ = width;
+    height_ = height;
+    pitch_ = pitch;
+    buffer_ = buffer;
+
+    assert(GetBytesPerPixel() * width_ <= pitch_);
+  }
+
+
+  void ImageAccessor::ToMatlabString(std::string& target) const
+  {
+    ChunkedBuffer buffer;
+
+    switch (GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        ToMatlabStringInternal<uint8_t>(buffer, *this);
+        break;
+
+      case PixelFormat_Grayscale16:
+        ToMatlabStringInternal<uint16_t>(buffer, *this);
+        break;
+
+      case PixelFormat_SignedGrayscale16:
+        ToMatlabStringInternal<int16_t>(buffer, *this);
+        break;
+
+      case PixelFormat_RGB24:
+        RGB24ToMatlabString(buffer, *this);
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }   
+
+    buffer.Flatten(target);
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/ImageAccessor.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,123 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Enumerations.h"
+
+#include <string>
+
+namespace Orthanc
+{
+  class ImageAccessor
+  {
+  private:
+    bool readOnly_;
+    PixelFormat format_;
+    unsigned int width_;
+    unsigned int height_;
+    unsigned int pitch_;
+    void *buffer_;
+
+  public:
+    ImageAccessor()
+    {
+      AssignEmpty(PixelFormat_Grayscale8);
+    }
+
+    virtual ~ImageAccessor()
+    {
+    }
+
+    bool IsReadOnly() const
+    {
+      return readOnly_;
+    }
+
+    PixelFormat GetFormat() const
+    {
+      return format_;
+    }
+
+    unsigned int GetBytesPerPixel() const
+    {
+      return ::Orthanc::GetBytesPerPixel(format_);
+    }
+
+    unsigned int GetWidth() const
+    {
+      return width_;
+    }
+
+    unsigned int GetHeight() const
+    {
+      return height_;
+    }
+
+    unsigned int GetPitch() const
+    {
+      return pitch_;
+    }
+
+    unsigned int GetSize() const
+    {
+      return GetHeight() * GetPitch();
+    }
+
+    const void* GetConstBuffer() const
+    {
+      return buffer_;
+    }
+
+    void* GetBuffer() const;
+
+    const void* GetConstRow(unsigned int y) const;
+
+    void* GetRow(unsigned int y) const;
+
+    void AssignEmpty(PixelFormat format);
+
+    void AssignReadOnly(PixelFormat format,
+                        unsigned int width,
+                        unsigned int height,
+                        unsigned int pitch,
+                        const void *buffer);
+
+    void AssignWritable(PixelFormat format,
+                        unsigned int width,
+                        unsigned int height,
+                        unsigned int pitch,
+                        void *buffer);
+
+    void ToMatlabString(std::string& target) const; 
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/ImageBuffer.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,192 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "ImageBuffer.h"
+
+#include "../OrthancException.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+namespace Orthanc
+{
+  void ImageBuffer::Allocate()
+  {
+    if (changed_)
+    {
+      Deallocate();
+
+      /*
+        if (forceMinimalPitch_)
+        {
+        TODO: Align pitch and memory buffer to optimal size for SIMD.
+        }
+      */
+
+      pitch_ = GetBytesPerPixel() * width_;
+      size_t size = pitch_ * height_;
+
+      if (size == 0)
+      {
+        buffer_ = NULL;
+      }
+      else
+      {
+        buffer_ = malloc(size);
+        if (buffer_ == NULL)
+        {
+          throw OrthancException(ErrorCode_NotEnoughMemory);
+        }
+      }
+
+      changed_ = false;
+    }
+  }
+
+
+  void ImageBuffer::Deallocate()
+  {
+    if (buffer_ != NULL)
+    {
+      free(buffer_);
+      buffer_ = NULL;
+      changed_ = true;
+    }
+  }
+
+
+  ImageBuffer::ImageBuffer(PixelFormat format,
+                           unsigned int width,
+                           unsigned int height)
+  {
+    Initialize();
+    SetWidth(width);
+    SetHeight(height);
+    SetFormat(format);
+  }
+
+
+  void ImageBuffer::Initialize()
+  {
+    changed_ = false;
+    forceMinimalPitch_ = true;
+    format_ = PixelFormat_Grayscale8;
+    width_ = 0;
+    height_ = 0;
+    pitch_ = 0;
+    buffer_ = NULL;
+  }
+
+
+  void ImageBuffer::SetFormat(PixelFormat format)
+  {
+    if (format != format_)
+    {
+      changed_ = true;
+      format_ = format;
+    }
+  }
+
+
+  void ImageBuffer::SetWidth(unsigned int width)
+  {
+    if (width != width_)
+    {
+      changed_ = true;
+      width_ = width;     
+    }
+  }
+
+
+  void ImageBuffer::SetHeight(unsigned int height)
+  {
+    if (height != height_)
+    {
+      changed_ = true;
+      height_ = height;     
+    }
+  }
+
+
+  ImageAccessor ImageBuffer::GetAccessor()
+  {
+    Allocate();
+
+    ImageAccessor accessor;
+    accessor.AssignWritable(format_, width_, height_, pitch_, buffer_);
+    return accessor;
+  }
+
+
+  ImageAccessor ImageBuffer::GetConstAccessor()
+  {
+    Allocate();
+
+    ImageAccessor accessor;
+    accessor.AssignReadOnly(format_, width_, height_, pitch_, buffer_);
+    return accessor;
+  }
+
+
+  void ImageBuffer::SetMinimalPitchForced(bool force)
+  {
+    if (force != forceMinimalPitch_)
+    {
+      changed_ = true;
+      forceMinimalPitch_ = force;
+    }
+  }
+
+
+  void ImageBuffer::AcquireOwnership(ImageBuffer& other)
+  {
+    // Remove the content of the current image
+    Deallocate();
+
+    // Force the allocation of the other image (if not already
+    // allocated)
+    other.Allocate();
+
+    // Transfer the content of the other image
+    changed_ = false;
+    forceMinimalPitch_ = other.forceMinimalPitch_;
+    format_ = other.format_;
+    width_ = other.width_;
+    height_ = other.height_;
+    pitch_ = other.pitch_;
+    buffer_ = other.buffer_;
+
+    // Force the reinitialization of the other image
+    other.Initialize();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/ImageBuffer.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,115 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ImageAccessor.h"
+
+#include <vector>
+#include <stdint.h>
+#include <boost/noncopyable.hpp>
+
+namespace Orthanc
+{
+  class ImageBuffer : public boost::noncopyable
+  {
+  private:
+    bool changed_;
+
+    bool forceMinimalPitch_;  // Currently unused
+    PixelFormat format_;
+    unsigned int width_;
+    unsigned int height_;
+    unsigned int pitch_;
+    void *buffer_;
+
+    void Initialize();
+    
+    void Allocate();
+
+    void Deallocate();
+
+  public:
+    ImageBuffer(PixelFormat format,
+                unsigned int width,
+                unsigned int height);
+
+    ImageBuffer()
+    {
+      Initialize();
+    }
+
+    ~ImageBuffer()
+    {
+      Deallocate();
+    }
+
+    PixelFormat GetFormat() const
+    {
+      return format_;
+    }
+
+    void SetFormat(PixelFormat format);
+
+    unsigned int GetWidth() const
+    {
+      return width_;
+    }
+
+    void SetWidth(unsigned int width);
+
+    unsigned int GetHeight() const
+    {
+      return height_;
+    }
+
+    void SetHeight(unsigned int height);
+
+    unsigned int GetBytesPerPixel() const
+    {
+      return ::Orthanc::GetBytesPerPixel(format_);
+    }
+
+    ImageAccessor GetAccessor();
+
+    ImageAccessor GetConstAccessor();
+
+    bool IsMinimalPitchForced() const
+    {
+      return forceMinimalPitch_;
+    }
+
+    void SetMinimalPitchForced(bool force);
+
+    void AcquireOwnership(ImageBuffer& other);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/ImageProcessing.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,592 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "ImageProcessing.h"
+
+#include "../OrthancException.h"
+
+#include <boost/math/special_functions/round.hpp>
+
+#include <cassert>
+#include <string.h>
+#include <limits>
+#include <stdint.h>
+
+namespace Orthanc
+{
+  template <typename TargetType, typename SourceType>
+  static void ConvertInternal(ImageAccessor& target,
+                              const ImageAccessor& source)
+  {
+    const TargetType minValue = std::numeric_limits<TargetType>::min();
+    const TargetType maxValue = std::numeric_limits<TargetType>::max();
+
+    for (unsigned int y = 0; y < source.GetHeight(); y++)
+    {
+      TargetType* t = reinterpret_cast<TargetType*>(target.GetRow(y));
+      const SourceType* s = reinterpret_cast<const SourceType*>(source.GetConstRow(y));
+
+      for (unsigned int x = 0; x < source.GetWidth(); x++, t++, s++)
+      {
+        if (static_cast<int32_t>(*s) < static_cast<int32_t>(minValue))
+        {
+          *t = minValue;
+        }
+        else if (static_cast<int32_t>(*s) > static_cast<int32_t>(maxValue))
+        {
+          *t = maxValue;
+        }
+        else
+        {
+          *t = static_cast<TargetType>(*s);
+        }
+      }
+    }
+  }
+
+
+  template <typename TargetType>
+  static void ConvertColorToGrayscale(ImageAccessor& target,
+                              const ImageAccessor& source)
+  {
+    assert(source.GetFormat() == PixelFormat_RGB24);
+
+    const TargetType minValue = std::numeric_limits<TargetType>::min();
+    const TargetType maxValue = std::numeric_limits<TargetType>::max();
+
+    for (unsigned int y = 0; y < source.GetHeight(); y++)
+    {
+      TargetType* t = reinterpret_cast<TargetType*>(target.GetRow(y));
+      const uint8_t* s = reinterpret_cast<const uint8_t*>(source.GetConstRow(y));
+
+      for (unsigned int x = 0; x < source.GetWidth(); x++, t++, s += 3)
+      {
+        // Y = 0.2126 R + 0.7152 G + 0.0722 B
+        int32_t v = (2126 * static_cast<int32_t>(s[0]) +
+                     7152 * static_cast<int32_t>(s[1]) +
+                     0722 * static_cast<int32_t>(s[2])) / 1000;
+        
+        if (static_cast<int32_t>(v) < static_cast<int32_t>(minValue))
+        {
+          *t = minValue;
+        }
+        else if (static_cast<int32_t>(v) > static_cast<int32_t>(maxValue))
+        {
+          *t = maxValue;
+        }
+        else
+        {
+          *t = static_cast<TargetType>(v);
+        }
+      }
+    }
+  }
+
+
+  template <typename PixelType>
+  static void SetInternal(ImageAccessor& image,
+                          int64_t constant)
+  {
+    for (unsigned int y = 0; y < image.GetHeight(); y++)
+    {
+      PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
+
+      for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
+      {
+        *p = static_cast<PixelType>(constant);
+      }
+    }
+  }
+
+
+  template <typename PixelType>
+  static void GetMinMaxValueInternal(PixelType& minValue,
+                                     PixelType& maxValue,
+                                     const ImageAccessor& source)
+  {
+    // Deal with the special case of empty image
+    if (source.GetWidth() == 0 ||
+        source.GetHeight() == 0)
+    {
+      minValue = 0;
+      maxValue = 0;
+      return;
+    }
+
+    minValue = std::numeric_limits<PixelType>::max();
+    maxValue = std::numeric_limits<PixelType>::min();
+
+    for (unsigned int y = 0; y < source.GetHeight(); y++)
+    {
+      const PixelType* p = reinterpret_cast<const PixelType*>(source.GetConstRow(y));
+
+      for (unsigned int x = 0; x < source.GetWidth(); x++, p++)
+      {
+        if (*p < minValue)
+        {
+          minValue = *p;
+        }
+
+        if (*p > maxValue)
+        {
+          maxValue = *p;
+        }
+      }
+    }
+  }
+
+
+
+  template <typename PixelType>
+  static void AddConstantInternal(ImageAccessor& image,
+                                  int64_t constant)
+  {
+    if (constant == 0)
+    {
+      return;
+    }
+
+    const int64_t minValue = std::numeric_limits<PixelType>::min();
+    const int64_t maxValue = std::numeric_limits<PixelType>::max();
+
+    for (unsigned int y = 0; y < image.GetHeight(); y++)
+    {
+      PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
+
+      for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
+      {
+        int64_t v = static_cast<int64_t>(*p) + constant;
+
+        if (v > maxValue)
+        {
+          *p = std::numeric_limits<PixelType>::max();
+        }
+        else if (v < minValue)
+        {
+          *p = std::numeric_limits<PixelType>::min();
+        }
+        else
+        {
+          *p = static_cast<PixelType>(v);
+        }
+      }
+    }
+  }
+
+
+
+  template <typename PixelType>
+  void MultiplyConstantInternal(ImageAccessor& image,
+                                float factor)
+  {
+    if (std::abs(factor - 1.0f) <= std::numeric_limits<float>::epsilon())
+    {
+      return;
+    }
+
+    const int64_t minValue = std::numeric_limits<PixelType>::min();
+    const int64_t maxValue = std::numeric_limits<PixelType>::max();
+
+    for (unsigned int y = 0; y < image.GetHeight(); y++)
+    {
+      PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
+
+      for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
+      {
+        int64_t v = boost::math::llround(static_cast<float>(*p) * factor);
+
+        if (v > maxValue)
+        {
+          *p = std::numeric_limits<PixelType>::max();
+        }
+        else if (v < minValue)
+        {
+          *p = std::numeric_limits<PixelType>::min();
+        }
+        else
+        {
+          *p = static_cast<PixelType>(v);
+        }
+      }
+    }
+  }
+
+
+  template <typename PixelType>
+  void ShiftScaleInternal(ImageAccessor& image,
+                          float offset,
+                          float scaling)
+  {
+    const float minValue = static_cast<float>(std::numeric_limits<PixelType>::min());
+    const float maxValue = static_cast<float>(std::numeric_limits<PixelType>::max());
+
+    for (unsigned int y = 0; y < image.GetHeight(); y++)
+    {
+      PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
+
+      for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
+      {
+        float v = (static_cast<float>(*p) + offset) * scaling;
+
+        if (v > maxValue)
+        {
+          *p = std::numeric_limits<PixelType>::max();
+        }
+        else if (v < minValue)
+        {
+          *p = std::numeric_limits<PixelType>::min();
+        }
+        else
+        {
+          *p = static_cast<PixelType>(boost::math::iround(v));
+        }
+      }
+    }
+  }
+
+
+  void ImageProcessing::Copy(ImageAccessor& target,
+                             const ImageAccessor& source)
+  {
+    if (target.GetWidth() != source.GetWidth() ||
+        target.GetHeight() != source.GetHeight())
+    {
+      throw OrthancException(ErrorCode_IncompatibleImageSize);
+    }
+
+    if (target.GetFormat() != source.GetFormat())
+    {
+      throw OrthancException(ErrorCode_IncompatibleImageFormat);
+    }
+
+    unsigned int lineSize = GetBytesPerPixel(source.GetFormat()) * source.GetWidth();
+
+    assert(source.GetPitch() >= lineSize && target.GetPitch() >= lineSize);
+
+    for (unsigned int y = 0; y < source.GetHeight(); y++)
+    {
+      memcpy(target.GetRow(y), source.GetConstRow(y), lineSize);
+    }
+  }
+
+
+  void ImageProcessing::Convert(ImageAccessor& target,
+                                const ImageAccessor& source)
+  {
+    if (target.GetWidth() != source.GetWidth() ||
+        target.GetHeight() != source.GetHeight())
+    {
+      throw OrthancException(ErrorCode_IncompatibleImageSize);
+    }
+
+    if (source.GetFormat() == target.GetFormat())
+    {
+      Copy(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_Grayscale16 &&
+        source.GetFormat() == PixelFormat_Grayscale8)
+    {
+      ConvertInternal<uint16_t, uint8_t>(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_SignedGrayscale16 &&
+        source.GetFormat() == PixelFormat_Grayscale8)
+    {
+      ConvertInternal<int16_t, uint8_t>(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_Grayscale8 &&
+        source.GetFormat() == PixelFormat_Grayscale16)
+    {
+      ConvertInternal<uint8_t, uint16_t>(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_SignedGrayscale16 &&
+        source.GetFormat() == PixelFormat_Grayscale16)
+    {
+      ConvertInternal<int16_t, uint16_t>(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_Grayscale8 &&
+        source.GetFormat() == PixelFormat_SignedGrayscale16)
+    {
+      ConvertInternal<uint8_t, int16_t>(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_Grayscale16 &&
+        source.GetFormat() == PixelFormat_SignedGrayscale16)
+    {
+      ConvertInternal<uint16_t, int16_t>(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_Grayscale8 &&
+        source.GetFormat() == PixelFormat_RGB24)
+    {
+      ConvertColorToGrayscale<uint8_t>(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_Grayscale16 &&
+        source.GetFormat() == PixelFormat_RGB24)
+    {
+      ConvertColorToGrayscale<uint16_t>(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_SignedGrayscale16 &&
+        source.GetFormat() == PixelFormat_RGB24)
+    {
+      ConvertColorToGrayscale<int16_t>(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_Grayscale8 &&
+        source.GetFormat() == PixelFormat_RGBA32)
+    {
+      for (unsigned int y = 0; y < source.GetHeight(); y++)
+      {
+        const uint8_t* p = reinterpret_cast<const uint8_t*>(source.GetConstRow(y));
+        uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
+        for (unsigned int x = 0; x < source.GetWidth(); x++, q++)
+        {
+          *q = static_cast<uint8_t>((2126 * static_cast<uint32_t>(p[0]) +
+                                     7152 * static_cast<uint32_t>(p[1]) +
+                                     0722 * static_cast<uint32_t>(p[2])) / 10000);
+          p += 4;
+        }
+      }
+
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_RGB24 &&
+        source.GetFormat() == PixelFormat_RGBA32)
+    {
+      for (unsigned int y = 0; y < source.GetHeight(); y++)
+      {
+        const uint8_t* p = reinterpret_cast<const uint8_t*>(source.GetConstRow(y));
+        uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
+        for (unsigned int x = 0; x < source.GetWidth(); x++)
+        {
+          q[0] = p[0];
+          q[1] = p[1];
+          q[2] = p[2];
+          p += 4;
+          q += 3;
+        }
+      }
+
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_RGBA32 &&
+        source.GetFormat() == PixelFormat_RGB24)
+    {
+      for (unsigned int y = 0; y < source.GetHeight(); y++)
+      {
+        const uint8_t* p = reinterpret_cast<const uint8_t*>(source.GetConstRow(y));
+        uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
+        for (unsigned int x = 0; x < source.GetWidth(); x++)
+        {
+          q[0] = p[0];
+          q[1] = p[1];
+          q[2] = p[2];
+          q[3] = 255;   // Set the alpha channel to full opacity
+          p += 3;
+          q += 4;
+        }
+      }
+
+      return;
+    }
+
+    throw OrthancException(ErrorCode_NotImplemented);
+  }
+
+
+
+  void ImageProcessing::Set(ImageAccessor& image,
+                            int64_t value)
+  {
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        SetInternal<uint8_t>(image, value);
+        return;
+
+      case PixelFormat_Grayscale16:
+        SetInternal<uint16_t>(image, value);
+        return;
+
+      case PixelFormat_SignedGrayscale16:
+        SetInternal<int16_t>(image, value);
+        return;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+
+  void ImageProcessing::ShiftRight(ImageAccessor& image,
+                                   unsigned int shift)
+  {
+    if (image.GetWidth() == 0 ||
+        image.GetHeight() == 0 ||
+        shift == 0)
+    {
+      // Nothing to do
+      return;
+    }
+
+    throw OrthancException(ErrorCode_NotImplemented);
+  }
+
+
+  void ImageProcessing::GetMinMaxValue(int64_t& minValue,
+                                       int64_t& maxValue,
+                                       const ImageAccessor& image)
+  {
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+      {
+        uint8_t a, b;
+        GetMinMaxValueInternal<uint8_t>(a, b, image);
+        minValue = a;
+        maxValue = b;
+        break;
+      }
+
+      case PixelFormat_Grayscale16:
+      {
+        uint16_t a, b;
+        GetMinMaxValueInternal<uint16_t>(a, b, image);
+        minValue = a;
+        maxValue = b;
+        break;
+      }
+
+      case PixelFormat_SignedGrayscale16:
+      {
+        int16_t a, b;
+        GetMinMaxValueInternal<int16_t>(a, b, image);
+        minValue = a;
+        maxValue = b;
+        break;
+      }
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+
+
+  void ImageProcessing::AddConstant(ImageAccessor& image,
+                                    int64_t value)
+  {
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        AddConstantInternal<uint8_t>(image, value);
+        return;
+
+      case PixelFormat_Grayscale16:
+        AddConstantInternal<uint16_t>(image, value);
+        return;
+
+      case PixelFormat_SignedGrayscale16:
+        AddConstantInternal<int16_t>(image, value);
+        return;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+
+  void ImageProcessing::MultiplyConstant(ImageAccessor& image,
+                                         float factor)
+  {
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        MultiplyConstantInternal<uint8_t>(image, factor);
+        return;
+
+      case PixelFormat_Grayscale16:
+        MultiplyConstantInternal<uint16_t>(image, factor);
+        return;
+
+      case PixelFormat_SignedGrayscale16:
+        MultiplyConstantInternal<int16_t>(image, factor);
+        return;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+
+  void ImageProcessing::ShiftScale(ImageAccessor& image,
+                                   float offset,
+                                   float scaling)
+  {
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        ShiftScaleInternal<uint8_t>(image, offset, scaling);
+        return;
+
+      case PixelFormat_Grayscale16:
+        ShiftScaleInternal<uint16_t>(image, offset, scaling);
+        return;
+
+      case PixelFormat_SignedGrayscale16:
+        ShiftScaleInternal<int16_t>(image, offset, scaling);
+        return;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/ImageProcessing.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,70 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ImageAccessor.h"
+
+#include <stdint.h>
+
+namespace Orthanc
+{
+  class ImageProcessing
+  {
+  public:
+    static void Copy(ImageAccessor& target,
+                     const ImageAccessor& source);
+
+    static void Convert(ImageAccessor& target,
+                        const ImageAccessor& source);
+
+    static void Set(ImageAccessor& image,
+                    int64_t value);
+
+    static void ShiftRight(ImageAccessor& target,
+                           unsigned int shift);
+
+    static void GetMinMaxValue(int64_t& minValue,
+                               int64_t& maxValue,
+                               const ImageAccessor& image);
+
+    static void AddConstant(ImageAccessor& image,
+                            int64_t value);
+
+    static void MultiplyConstant(ImageAccessor& image,
+                                 float factor);
+
+    static void ShiftScale(ImageAccessor& image,
+                           float offset,
+                           float scaling);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/JpegErrorManager.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,69 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "JpegErrorManager.h"
+
+namespace Orthanc
+{
+  namespace Internals
+  {
+    void JpegErrorManager::OutputMessage(j_common_ptr cinfo)
+    {
+      char message[JMSG_LENGTH_MAX];
+      (*cinfo->err->format_message) (cinfo, message);
+
+      JpegErrorManager* that = reinterpret_cast<JpegErrorManager*>(cinfo->err);
+      that->message = std::string(message);
+    }
+
+
+    void JpegErrorManager::ErrorExit(j_common_ptr cinfo)
+    {
+      (*cinfo->err->output_message) (cinfo);
+
+      JpegErrorManager* that = reinterpret_cast<JpegErrorManager*>(cinfo->err);
+      longjmp(that->setjmp_buffer, 1);
+    }
+      
+
+    JpegErrorManager::JpegErrorManager()
+    {
+      memset(&pub, 0, sizeof(struct jpeg_error_mgr));
+      memset(&setjmp_buffer, 0, sizeof(jmp_buf));
+
+      jpeg_std_error(&pub);
+      pub.error_exit = ErrorExit;
+      pub.output_message = OutputMessage;
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/JpegErrorManager.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,74 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#pragma once
+
+#include <string.h>
+#include <stdio.h>
+#include <jpeglib.h>
+#include <setjmp.h>
+#include <string>
+
+namespace Orthanc
+{
+  namespace Internals
+  {
+    class JpegErrorManager 
+    {
+    private:
+      struct jpeg_error_mgr pub;  /* "public" fields */
+      jmp_buf setjmp_buffer;      /* for return to caller */
+      std::string message;
+
+      static void OutputMessage(j_common_ptr cinfo);
+
+      static void ErrorExit(j_common_ptr cinfo);
+
+    public:
+      JpegErrorManager();
+
+      struct jpeg_error_mgr* GetPublic()
+      {
+        return &pub;
+      }
+
+      jmp_buf& GetJumpBuffer()
+      {
+        return setjmp_buffer;
+      }
+
+      const std::string& GetMessage() const
+      {
+        return message;
+      }
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/JpegReader.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,184 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "JpegReader.h"
+
+#include "JpegErrorManager.h"
+#include "../OrthancException.h"
+#include "../Logging.h"
+
+namespace Orthanc
+{
+  static void Uncompress(struct jpeg_decompress_struct& cinfo,
+                         std::string& content,
+                         ImageAccessor& accessor)
+  {
+    jpeg_read_header(&cinfo, TRUE);
+    jpeg_start_decompress(&cinfo);
+
+    PixelFormat format;
+    if (cinfo.output_components == 1 &&
+        cinfo.out_color_space == JCS_GRAYSCALE)
+    {
+      format = PixelFormat_Grayscale8;
+    }
+    else if (cinfo.output_components == 3 &&
+             cinfo.out_color_space == JCS_RGB)
+    {
+      format = PixelFormat_RGB24;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    unsigned int pitch = cinfo.output_width * cinfo.output_components;
+
+    /* Make a one-row-high sample array that will go away when done with image */
+    JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, pitch, 1);
+
+    try
+    {
+      content.resize(pitch * cinfo.output_height);
+    }
+    catch (...)
+    {
+      throw OrthancException(ErrorCode_NotEnoughMemory);
+    }
+
+    accessor.AssignWritable(format, cinfo.output_width, cinfo.output_height, pitch, 
+                            content.empty() ? NULL : &content[0]);
+
+    uint8_t* target = reinterpret_cast<uint8_t*>(&content[0]);
+    while (cinfo.output_scanline < cinfo.output_height) 
+    {
+      jpeg_read_scanlines(&cinfo, buffer, 1);
+      memcpy(target, buffer[0], pitch);
+      target += pitch;
+    }
+
+    // Everything went fine, "setjmp()" didn't get called
+
+    jpeg_finish_decompress(&cinfo);
+  }
+
+
+  void JpegReader::ReadFromFile(const char* filename)
+  {
+    FILE* fp = fopen(filename, "rb");
+    if (!fp)
+    {
+      throw OrthancException(ErrorCode_InexistentFile);
+    }
+
+    struct jpeg_decompress_struct cinfo;
+    memset(&cinfo, 0, sizeof(struct jpeg_decompress_struct));
+
+    Internals::JpegErrorManager jerr;
+    cinfo.err = jerr.GetPublic();
+    
+    if (setjmp(jerr.GetJumpBuffer())) 
+    {
+      jpeg_destroy_decompress(&cinfo);
+      fclose(fp);
+      LOG(ERROR) << "Error during JPEG encoding: " << jerr.GetMessage();
+      throw OrthancException(ErrorCode_InternalError);
+    }
+
+    // Below this line, we are under the scope of a "setjmp"
+
+    jpeg_create_decompress(&cinfo);
+    jpeg_stdio_src(&cinfo, fp);
+
+    try
+    {
+      Uncompress(cinfo, content_, *this);
+    }
+    catch (OrthancException&)
+    {
+      jpeg_destroy_decompress(&cinfo);
+      fclose(fp);
+      throw;
+    }
+
+    jpeg_destroy_decompress(&cinfo);
+    fclose(fp);
+  }
+
+
+  void JpegReader::ReadFromMemory(const void* buffer,
+                                  size_t size)
+  {
+    struct jpeg_decompress_struct cinfo;
+    memset(&cinfo, 0, sizeof(struct jpeg_decompress_struct));
+
+    Internals::JpegErrorManager jerr;
+    cinfo.err = jerr.GetPublic();
+    
+    if (setjmp(jerr.GetJumpBuffer())) 
+    {
+      jpeg_destroy_decompress(&cinfo);
+      LOG(ERROR) << "Error during JPEG encoding: " << jerr.GetMessage();
+      throw OrthancException(ErrorCode_InternalError);
+    }
+
+    // Below this line, we are under the scope of a "setjmp"
+    jpeg_create_decompress(&cinfo);
+    jpeg_mem_src(&cinfo, const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(buffer)), size);
+
+    try
+    {
+      Uncompress(cinfo, content_, *this);
+    }
+    catch (OrthancException&)
+    {
+      jpeg_destroy_decompress(&cinfo);
+      throw;
+    }
+
+    jpeg_destroy_decompress(&cinfo);
+  }
+
+
+  void JpegReader::ReadFromMemory(const std::string& buffer)
+  {
+    if (buffer.empty())
+    {
+      ReadFromMemory(NULL, 0);
+    }
+    else
+    {
+      ReadFromMemory(buffer.c_str(), buffer.size());
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/JpegReader.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,59 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ImageAccessor.h"
+
+#include <string>
+
+namespace Orthanc
+{
+  class JpegReader : public ImageAccessor
+  {
+  private:
+    std::string  content_;
+
+  public:
+    void ReadFromFile(const char* filename);
+
+    void ReadFromFile(const std::string& filename)
+    {
+      ReadFromFile(filename.c_str());
+    }
+
+    void ReadFromMemory(const void* buffer,
+                        size_t size);
+
+    void ReadFromMemory(const std::string& buffer);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/JpegWriter.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,202 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "JpegWriter.h"
+
+#include "../OrthancException.h"
+#include "../Logging.h"
+
+#include "JpegErrorManager.h"
+
+#include <vector>
+
+namespace Orthanc
+{
+  static void GetLines(std::vector<uint8_t*>& lines,
+                       unsigned int height,
+                       unsigned int pitch,
+                       PixelFormat format,
+                       const void* buffer)
+  {
+    if (format != PixelFormat_Grayscale8 &&
+        format != PixelFormat_RGB24)
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    lines.resize(height);
+
+    uint8_t* base = const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(buffer));
+    for (unsigned int y = 0; y < height; y++)
+    {
+      lines[y] = base + static_cast<intptr_t>(y) * static_cast<intptr_t>(pitch);
+    }
+  }
+
+
+  static void Compress(struct jpeg_compress_struct& cinfo,
+                       std::vector<uint8_t*>& lines,
+                       unsigned int width,
+                       unsigned int height,
+                       PixelFormat format,
+                       uint8_t quality)
+  {
+    cinfo.image_width = width;
+    cinfo.image_height = height;
+
+    switch (format)
+    {
+      case PixelFormat_Grayscale8:
+        cinfo.input_components = 1;
+        cinfo.in_color_space = JCS_GRAYSCALE;
+        break;
+
+      case PixelFormat_RGB24:
+        cinfo.input_components = 3;
+        cinfo.in_color_space = JCS_RGB;
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+    }
+
+    jpeg_set_defaults(&cinfo);
+    jpeg_set_quality(&cinfo, quality, TRUE);
+    jpeg_start_compress(&cinfo, TRUE);
+    jpeg_write_scanlines(&cinfo, &lines[0], height);
+    jpeg_finish_compress(&cinfo);
+    jpeg_destroy_compress(&cinfo);
+  }
+                       
+
+  void JpegWriter::SetQuality(uint8_t quality)
+  {
+    if (quality <= 0 || quality > 100)
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    quality_ = quality;
+  }
+
+
+  void JpegWriter::WriteToFile(const char* filename,
+                               unsigned int width,
+                               unsigned int height,
+                               unsigned int pitch,
+                               PixelFormat format,
+                               const void* buffer)
+  {
+    FILE* fp = fopen(filename, "wb");
+    if (fp == NULL)
+    {
+      throw OrthancException(ErrorCode_FullStorage);
+    }
+
+    std::vector<uint8_t*> lines;
+    GetLines(lines, height, pitch, format, buffer);
+
+    struct jpeg_compress_struct cinfo;
+    memset(&cinfo, 0, sizeof(struct jpeg_compress_struct));
+
+    Internals::JpegErrorManager jerr;
+    cinfo.err = jerr.GetPublic();
+
+    if (setjmp(jerr.GetJumpBuffer())) 
+    {
+      /* If we get here, the JPEG code has signaled an error.
+       * We need to clean up the JPEG object, close the input file, and return.
+       */
+      jpeg_destroy_compress(&cinfo);
+      fclose(fp);
+      LOG(ERROR) << "Error during JPEG encoding: " << jerr.GetMessage();
+      throw OrthancException(ErrorCode_InternalError);
+    }
+
+    // Do not allocate data on the stack below this line!
+
+    jpeg_create_compress(&cinfo);
+    jpeg_stdio_dest(&cinfo, fp);
+    Compress(cinfo, lines, width, height, format, quality_);
+
+    // Everything went fine, "setjmp()" didn't get called
+
+    fclose(fp);
+  }
+
+
+  void JpegWriter::WriteToMemory(std::string& jpeg,
+                                 unsigned int width,
+                                 unsigned int height,
+                                 unsigned int pitch,
+                                 PixelFormat format,
+                                 const void* buffer)
+  {
+    std::vector<uint8_t*> lines;
+    GetLines(lines, height, pitch, format, buffer);
+
+    struct jpeg_compress_struct cinfo;
+    memset(&cinfo, 0, sizeof(struct jpeg_compress_struct));
+
+    Internals::JpegErrorManager jerr;
+
+    unsigned char* data = NULL;
+    unsigned long size;
+
+    if (setjmp(jerr.GetJumpBuffer())) 
+    {
+      jpeg_destroy_compress(&cinfo);
+
+      if (data != NULL)
+      {
+        free(data);
+      }
+
+      LOG(ERROR) << "Error during JPEG encoding: " << jerr.GetMessage();
+      throw OrthancException(ErrorCode_InternalError);
+    }
+
+    // Do not allocate data on the stack below this line!
+
+    jpeg_create_compress(&cinfo);
+    cinfo.err = jerr.GetPublic();
+    jpeg_mem_dest(&cinfo, &data, &size);
+
+    Compress(cinfo, lines, width, height, format, quality_);
+
+    // Everything went fine, "setjmp()" didn't get called
+
+    jpeg.assign(reinterpret_cast<const char*>(data), size);
+    free(data);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/JpegWriter.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,87 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ImageAccessor.h"
+
+#include <string>
+#include <stdint.h>
+
+namespace Orthanc
+{
+  class JpegWriter
+  {
+  private:
+    uint8_t  quality_;
+
+  public:
+    JpegWriter() : quality_(90)
+    {
+    }
+
+    void SetQuality(uint8_t quality);
+
+    uint8_t GetQuality() const
+    {
+      return quality_;
+    }
+
+    void WriteToFile(const char* filename,
+                     unsigned int width,
+                     unsigned int height,
+                     unsigned int pitch,
+                     PixelFormat format,
+                     const void* buffer);
+
+    void WriteToMemory(std::string& jpeg,
+                       unsigned int width,
+                       unsigned int height,
+                       unsigned int pitch,
+                       PixelFormat format,
+                       const void* buffer);
+
+    void WriteToFile(const char* filename,
+                     const ImageAccessor& accessor)
+    {
+      WriteToFile(filename, accessor.GetWidth(), accessor.GetHeight(),
+                  accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer());
+    }
+
+    void WriteToMemory(std::string& jpeg,
+                       const ImageAccessor& accessor)
+    {
+      WriteToMemory(jpeg, accessor.GetWidth(), accessor.GetHeight(),
+                    accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer());
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/PngReader.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,313 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "PngReader.h"
+
+#include "../OrthancException.h"
+#include "../Toolbox.h"
+
+#include <png.h>
+#include <string.h>  // For memcpy()
+
+namespace Orthanc
+{
+  namespace 
+  {
+    struct FileRabi
+    {
+      FILE* fp_;
+
+      FileRabi(const char* filename)
+      {
+        fp_ = fopen(filename, "rb");
+        if (!fp_)
+        {
+          throw OrthancException(ErrorCode_InexistentFile);
+        }
+      }
+
+      ~FileRabi()
+      {
+        if (fp_)
+          fclose(fp_);
+      }
+    };
+  }
+
+
+  struct PngReader::PngRabi
+  {
+    png_structp png_;
+    png_infop info_;
+    png_infop endInfo_;
+
+    void Destruct()
+    {
+      if (png_)
+      {
+        png_destroy_read_struct(&png_, &info_, &endInfo_);
+
+        png_ = NULL;
+        info_ = NULL;
+        endInfo_ = NULL;
+      }
+    }
+
+    PngRabi()
+    {
+      png_ = NULL;
+      info_ = NULL;
+      endInfo_ = NULL;
+
+      png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+      if (!png_)
+      {
+        throw OrthancException(ErrorCode_NotEnoughMemory);
+      }
+
+      info_ = png_create_info_struct(png_);
+      if (!info_)
+      {
+        png_destroy_read_struct(&png_, NULL, NULL);
+        throw OrthancException(ErrorCode_NotEnoughMemory);
+      }
+
+      endInfo_ = png_create_info_struct(png_);
+      if (!info_)
+      {
+        png_destroy_read_struct(&png_, &info_, NULL);
+        throw OrthancException(ErrorCode_NotEnoughMemory);
+      }
+    }
+
+    ~PngRabi()
+    {
+      Destruct();
+    }
+
+    static void MemoryCallback(png_structp png_ptr, 
+                               png_bytep data, 
+                               png_size_t size);
+  };
+
+
+  void PngReader::CheckHeader(const void* header)
+  {
+    int is_png = !png_sig_cmp((png_bytep) header, 0, 8);
+    if (!is_png)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  }
+
+  PngReader::PngReader()
+  {
+  }
+
+  void PngReader::Read(PngRabi& rabi)
+  {
+    png_set_sig_bytes(rabi.png_, 8);
+
+    png_read_info(rabi.png_, rabi.info_);
+
+    png_uint_32 width, height;
+    int bit_depth, color_type, interlace_type;
+    int compression_type, filter_method;
+    // get size and bit-depth of the PNG-image
+    png_get_IHDR(rabi.png_, rabi.info_,
+                 &width, &height,
+                 &bit_depth, &color_type, &interlace_type,
+                 &compression_type, &filter_method);
+
+    PixelFormat format;
+    unsigned int pitch;
+
+    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 8)
+    {
+      format = PixelFormat_Grayscale8;
+      pitch = width;
+    }
+    else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 16)
+    {
+      format = PixelFormat_Grayscale16;
+      pitch = 2 * width;
+
+      if (Toolbox::DetectEndianness() == Endianness_Little)
+      {
+        png_set_swap(rabi.png_);
+      }
+    }
+    else if (color_type == PNG_COLOR_TYPE_RGB && bit_depth == 8)
+    {
+      format = PixelFormat_RGB24;
+      pitch = 3 * width;
+    }
+    else if (color_type == PNG_COLOR_TYPE_RGBA && bit_depth == 8)
+    {
+      format = PixelFormat_RGBA32;
+      pitch = 4 * width;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    data_.resize(height * pitch);
+
+    if (height == 0 || width == 0)
+    {
+      // Empty image, we are done
+      AssignEmpty(format);
+      return;
+    }
+    
+    png_read_update_info(rabi.png_, rabi.info_);
+
+    std::vector<png_bytep> rows(height);
+    for (size_t i = 0; i < height; i++)
+    {
+      rows[i] = &data_[0] + i * pitch;
+    }
+
+    png_read_image(rabi.png_, &rows[0]);
+
+    AssignWritable(format, width, height, pitch, &data_[0]);
+  }
+
+  void PngReader::ReadFromFile(const char* filename)
+  {
+    FileRabi f(filename);
+
+    char header[8];
+    if (fread(header, 1, 8, f.fp_) != 8)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    CheckHeader(header);
+
+    PngRabi rabi;
+
+    if (setjmp(png_jmpbuf(rabi.png_)))
+    {
+      rabi.Destruct();
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    png_init_io(rabi.png_, f.fp_);
+
+    Read(rabi);
+  }
+
+
+  namespace
+  {
+    struct MemoryBuffer
+    {
+      const uint8_t* buffer_;
+      size_t size_;
+      size_t pos_;
+      bool ok_;
+    };
+  }
+
+
+  void PngReader::PngRabi::MemoryCallback(png_structp png_ptr, 
+                                          png_bytep outBytes, 
+                                          png_size_t byteCountToRead)
+  {
+    MemoryBuffer* from = reinterpret_cast<MemoryBuffer*>(png_get_io_ptr(png_ptr));
+
+    if (!from->ok_)
+    {
+      return;
+    }
+
+    if (from->pos_ + byteCountToRead > from->size_)
+    {
+      from->ok_ = false;
+      return;
+    }
+
+    memcpy(outBytes, from->buffer_ + from->pos_, byteCountToRead);
+
+    from->pos_ += byteCountToRead;
+  }
+
+
+  void PngReader::ReadFromMemory(const void* buffer,
+                                 size_t size)
+  {
+    if (size < 8)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    CheckHeader(buffer);
+
+    PngRabi rabi;
+
+    if (setjmp(png_jmpbuf(rabi.png_)))
+    {
+      rabi.Destruct();
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    MemoryBuffer tmp;
+    tmp.buffer_ = reinterpret_cast<const uint8_t*>(buffer) + 8;  // We skip the header
+    tmp.size_ = size - 8;
+    tmp.pos_ = 0;
+    tmp.ok_ = true;
+
+    png_set_read_fn(rabi.png_, &tmp, PngRabi::MemoryCallback);
+
+    Read(rabi);
+
+    if (!tmp.ok_)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  }
+
+  void PngReader::ReadFromMemory(const std::string& buffer)
+  {
+    if (buffer.size() != 0)
+    {
+      ReadFromMemory(&buffer[0], buffer.size());
+    }
+    else
+    {
+      ReadFromMemory(NULL, 0);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/PngReader.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,71 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ImageAccessor.h"
+
+#include "../Enumerations.h"
+
+#include <vector>
+#include <stdint.h>
+#include <boost/shared_ptr.hpp>
+
+namespace Orthanc
+{
+  class PngReader : public ImageAccessor
+  {
+  private:
+    struct PngRabi;
+
+    std::vector<uint8_t> data_;
+
+    void CheckHeader(const void* header);
+
+    void Read(PngRabi& rabi);
+
+  public:
+    PngReader();
+
+    void ReadFromFile(const char* filename);
+
+    void ReadFromFile(const std::string& filename)
+    {
+      ReadFromFile(filename.c_str());
+    }
+
+    void ReadFromMemory(const void* buffer,
+                        size_t size);
+
+    void ReadFromMemory(const std::string& buffer);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/PngWriter.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,269 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "PngWriter.h"
+
+#include <vector>
+#include <stdint.h>
+#include <png.h>
+#include "../OrthancException.h"
+#include "../ChunkedBuffer.h"
+#include "../Toolbox.h"
+
+
+// http://www.libpng.org/pub/png/libpng-1.2.5-manual.html#section-4
+// http://zarb.org/~gc/html/libpng.html
+/*
+  void write_row_callback(png_ptr, png_uint_32 row, int pass)
+  {
+  }*/
+
+
+
+
+/*  bool isError_;
+
+// http://www.libpng.org/pub/png/book/chapter14.html#png.ch14.div.2
+
+static void ErrorHandler(png_structp png, png_const_charp message)
+{
+printf("** [%s]\n", message);
+
+PngWriter* that = (PngWriter*) png_get_error_ptr(png);
+that->isError_ = true;
+printf("** %d\n", (int)that);
+
+//((PngWriter*) payload)->isError_ = true;
+}
+
+static void WarningHandler(png_structp png, png_const_charp message)
+{
+  printf("++ %d\n", (int)message);
+}*/
+
+
+namespace Orthanc
+{
+  struct PngWriter::PImpl
+  {
+    png_structp png_;
+    png_infop info_;
+
+    // Filled by Prepare()
+    std::vector<uint8_t*> rows_;
+    int bitDepth_;
+    int colorType_;
+  };
+
+
+
+  PngWriter::PngWriter() : pimpl_(new PImpl)
+  {
+    pimpl_->png_ = NULL;
+    pimpl_->info_ = NULL;
+
+    pimpl_->png_ = png_create_write_struct
+      (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); //this, ErrorHandler, WarningHandler);
+    if (!pimpl_->png_)
+    {
+      throw OrthancException(ErrorCode_NotEnoughMemory);
+    }
+
+    pimpl_->info_ = png_create_info_struct(pimpl_->png_);
+    if (!pimpl_->info_)
+    {
+      png_destroy_write_struct(&pimpl_->png_, NULL);
+      throw OrthancException(ErrorCode_NotEnoughMemory);
+    }
+  }
+
+  PngWriter::~PngWriter()
+  {
+    if (pimpl_->info_)
+    {
+      png_destroy_info_struct(pimpl_->png_, &pimpl_->info_);
+    }
+
+    if (pimpl_->png_)
+    {
+      png_destroy_write_struct(&pimpl_->png_, NULL);
+    }
+  }
+
+
+
+  void PngWriter::Prepare(unsigned int width,
+                          unsigned int height,
+                          unsigned int pitch,
+                          PixelFormat format,
+                          const void* buffer)
+  {
+    pimpl_->rows_.resize(height);
+    for (unsigned int y = 0; y < height; y++)
+    {
+      pimpl_->rows_[y] = const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(buffer)) + y * pitch;
+    }
+
+    switch (format)
+    {
+    case PixelFormat_RGB24:
+      pimpl_->bitDepth_ = 8;
+      pimpl_->colorType_ = PNG_COLOR_TYPE_RGB;
+      break;
+
+    case PixelFormat_RGBA32:
+      pimpl_->bitDepth_ = 8;
+      pimpl_->colorType_ = PNG_COLOR_TYPE_RGBA;
+      break;
+
+    case PixelFormat_Grayscale8:
+      pimpl_->bitDepth_ = 8;
+      pimpl_->colorType_ = PNG_COLOR_TYPE_GRAY;
+      break;
+
+    case PixelFormat_Grayscale16:
+    case PixelFormat_SignedGrayscale16:
+      pimpl_->bitDepth_ = 16;
+      pimpl_->colorType_ = PNG_COLOR_TYPE_GRAY;
+      break;
+
+    default:
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+
+  void PngWriter::Compress(unsigned int width,
+                           unsigned int height,
+                           unsigned int pitch,
+                           PixelFormat format)
+  {
+    png_set_IHDR(pimpl_->png_, pimpl_->info_, width, height,
+                 pimpl_->bitDepth_, pimpl_->colorType_, PNG_INTERLACE_NONE,
+                 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+    png_write_info(pimpl_->png_, pimpl_->info_);
+
+    if (height > 0)
+    {
+      switch (format)
+      {
+      case PixelFormat_Grayscale16:
+      case PixelFormat_SignedGrayscale16:
+      {
+        int transforms = 0;
+        if (Toolbox::DetectEndianness() == Endianness_Little)
+        {
+          transforms = PNG_TRANSFORM_SWAP_ENDIAN;
+        }
+
+        png_set_rows(pimpl_->png_, pimpl_->info_, &pimpl_->rows_[0]);
+        png_write_png(pimpl_->png_, pimpl_->info_, transforms, NULL);
+
+        break;
+      }
+
+      default:
+        png_write_image(pimpl_->png_, &pimpl_->rows_[0]);
+      }
+    }
+
+    png_write_end(pimpl_->png_, NULL);
+  }
+
+
+  void PngWriter::WriteToFile(const char* filename,
+                              unsigned int width,
+                              unsigned int height,
+                              unsigned int pitch,
+                              PixelFormat format,
+                              const void* buffer)
+  {
+    Prepare(width, height, pitch, format, buffer);
+
+    FILE* fp = fopen(filename, "wb");
+    if (!fp)
+    {
+      throw OrthancException(ErrorCode_CannotWriteFile);
+    }    
+
+    png_init_io(pimpl_->png_, fp);
+
+    if (setjmp(png_jmpbuf(pimpl_->png_)))
+    {
+      // Error during writing PNG
+      throw OrthancException(ErrorCode_CannotWriteFile);      
+    }
+
+    Compress(width, height, pitch, format);
+
+    fclose(fp);
+  }
+
+
+
+
+  static void MemoryCallback(png_structp png_ptr, 
+                             png_bytep data, 
+                             png_size_t size)
+  {
+    ChunkedBuffer* buffer = reinterpret_cast<ChunkedBuffer*>(png_get_io_ptr(png_ptr));
+    buffer->AddChunk(reinterpret_cast<const char*>(data), size);
+  }
+
+
+
+  void PngWriter::WriteToMemory(std::string& png,
+                                unsigned int width,
+                                unsigned int height,
+                                unsigned int pitch,
+                                PixelFormat format,
+                                const void* buffer)
+  {
+    ChunkedBuffer chunks;
+
+    Prepare(width, height, pitch, format, buffer);
+
+    if (setjmp(png_jmpbuf(pimpl_->png_)))
+    {
+      // Error during writing PNG
+      throw OrthancException(ErrorCode_InternalError);      
+    }
+
+    png_set_write_fn(pimpl_->png_, &chunks, MemoryCallback, NULL);
+
+    Compress(width, height, pitch, format);
+
+    chunks.Flatten(png);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/PngWriter.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,92 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ImageAccessor.h"
+
+#include <boost/shared_ptr.hpp>
+#include <string>
+
+namespace Orthanc
+{
+  class PngWriter
+  {
+  private:
+    struct PImpl;
+    boost::shared_ptr<PImpl> pimpl_;
+
+    void Compress(unsigned int width,
+                  unsigned int height,
+                  unsigned int pitch,
+                  PixelFormat format);
+
+    void Prepare(unsigned int width,
+                 unsigned int height,
+                 unsigned int pitch,
+                 PixelFormat format,
+                 const void* buffer);
+
+  public:
+    PngWriter();
+
+    ~PngWriter();
+
+    void WriteToFile(const char* filename,
+                     unsigned int width,
+                     unsigned int height,
+                     unsigned int pitch,
+                     PixelFormat format,
+                     const void* buffer);
+
+    void WriteToMemory(std::string& png,
+                       unsigned int width,
+                       unsigned int height,
+                       unsigned int pitch,
+                       PixelFormat format,
+                       const void* buffer);
+
+    void WriteToFile(const char* filename,
+                     const ImageAccessor& accessor)
+    {
+      WriteToFile(filename, accessor.GetWidth(), accessor.GetHeight(),
+                  accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer());
+    }
+
+    void WriteToMemory(std::string& png,
+                       const ImageAccessor& accessor)
+    {
+      WriteToMemory(png, accessor.GetWidth(), accessor.GetHeight(),
+                    accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer());
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Logging.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,421 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "PrecompiledHeaders.h"
+#include "Logging.h"
+
+#if ORTHANC_ENABLE_LOGGING != 1
+
+namespace Orthanc
+{
+  namespace Logging
+  {
+    void Initialize()
+    {
+    }
+
+    void Finalize()
+    {
+    }
+
+    void EnableInfoLevel(bool enabled)
+    {
+    }
+
+    void EnableTraceLevel(bool enabled)
+    {
+    }
+
+    void SetTargetFolder(const std::string& path)
+    {
+    }
+  }
+}
+
+#elif ORTHANC_ENABLE_GOOGLE_LOG == 1
+
+/*********************************************************
+ * Wrapper around Google Log
+ *********************************************************/
+
+namespace Orthanc
+{  
+  namespace Logging
+  {
+    void Initialize()
+    {
+      // Initialize Google's logging library.
+      FLAGS_logtostderr = true;
+      FLAGS_minloglevel = 1;   // Do not print LOG(INFO) by default
+      FLAGS_v = 0;             // Do not print trace-level VLOG(1) by default
+
+      google::InitGoogleLogging("Orthanc");
+    }
+
+    void Finalize()
+    {
+      google::ShutdownGoogleLogging();
+    }
+
+    void EnableInfoLevel(bool enabled)
+    {
+      FLAGS_minloglevel = (enabled ? 0 : 1);
+    }
+
+    void EnableTraceLevel(bool enabled)
+    {
+      if (enabled)
+      {
+        FLAGS_minloglevel = 0;
+        FLAGS_v = 1;
+      }
+      else
+      {
+        FLAGS_v = 0;
+      }
+    }
+
+    void SetTargetFolder(const std::string& path)
+    {
+      FLAGS_logtostderr = false;
+      FLAGS_log_dir = path;
+    }
+  }
+}
+
+#else
+
+/*********************************************************
+ * Use internal logger, not Google Log
+ *********************************************************/
+
+#include "OrthancException.h"
+#include "Enumerations.h"
+#include "Toolbox.h"
+
+#include <fstream>
+#include <boost/filesystem.hpp>
+#include <boost/thread.hpp>
+
+#if BOOST_HAS_DATE_TIME == 1
+#  include <boost/date_time/posix_time/posix_time.hpp>
+#else
+#  error Boost::date_time is required
+#endif
+
+
+namespace
+{
+  struct LoggingState
+  {
+    bool infoEnabled_;
+    bool traceEnabled_;
+
+    std::ostream* error_;
+    std::ostream* warning_;
+    std::ostream* info_;
+
+    std::auto_ptr<std::ofstream> errorFile_;
+    std::auto_ptr<std::ofstream> warningFile_;
+    std::auto_ptr<std::ofstream> infoFile_;
+
+    LoggingState() : 
+      infoEnabled_(false),
+      traceEnabled_(false),
+      error_(&std::cerr),
+      warning_(&std::cerr),
+      info_(&std::cerr)
+    {
+    }
+  };
+}
+
+
+
+static std::auto_ptr<LoggingState> loggingState_;
+static boost::mutex  loggingMutex_;
+
+
+
+namespace Orthanc
+{
+  namespace Logging
+  {
+    static void GetLogPath(boost::filesystem::path& log,
+                           boost::filesystem::path& link,
+                           const char* level,
+                           const std::string& directory)
+    {
+      /**
+         From Google Log documentation:
+
+         Unless otherwise specified, logs will be written to the filename
+         "<program name>.<hostname>.<user name>.log.<severity level>.",
+         followed by the date, time, and pid (you can't prevent the date,
+         time, and pid from being in the filename).
+
+         In this implementation : "hostname" and "username" are not used
+      **/
+
+      boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
+      boost::filesystem::path root(directory);
+      boost::filesystem::path exe(Toolbox::GetPathToExecutable());
+      
+      if (!boost::filesystem::exists(root) ||
+          !boost::filesystem::is_directory(root))
+      {
+        throw OrthancException(ErrorCode_CannotWriteFile);
+      }
+
+      char date[64];
+      sprintf(date, "%04d%02d%02d-%02d%02d%02d.%d",
+              static_cast<int>(now.date().year()),
+              now.date().month().as_number(),
+              now.date().day().as_number(),
+              now.time_of_day().hours(),
+              now.time_of_day().minutes(),
+              now.time_of_day().seconds(),
+              Toolbox::GetProcessId());
+
+      std::string programName = exe.filename().replace_extension("").string();
+
+      log = (root / (programName + ".log." +
+                     std::string(level) + "." +
+                     std::string(date)));
+
+      link = (root / (programName + "." + std::string(level)));
+    }
+
+
+    static void PrepareLogFile(std::ostream*& stream,
+                               std::auto_ptr<std::ofstream>& file,
+                               const char* level,
+                               const std::string& directory)
+    {
+      boost::filesystem::path log, link;
+      GetLogPath(log, link, level, directory);
+
+#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)))
+      boost::filesystem::remove(link);
+      boost::filesystem::create_symlink(log.filename(), link);
+#endif
+
+      file.reset(new std::ofstream(log.string().c_str()));
+      stream = file.get();
+    }
+
+
+    void Initialize()
+    {
+      boost::mutex::scoped_lock lock(loggingMutex_);
+      loggingState_.reset(new LoggingState);
+    }
+
+    void Finalize()
+    {
+      boost::mutex::scoped_lock lock(loggingMutex_);
+      loggingState_.reset(NULL);
+    }
+
+    void EnableInfoLevel(bool enabled)
+    {
+      boost::mutex::scoped_lock lock(loggingMutex_);
+      assert(loggingState_.get() != NULL);
+
+      loggingState_->infoEnabled_ = enabled;
+    }
+
+    void EnableTraceLevel(bool enabled)
+    {
+      boost::mutex::scoped_lock lock(loggingMutex_);
+      assert(loggingState_.get() != NULL);
+
+      loggingState_->traceEnabled_ = enabled;
+      
+      if (enabled)
+      {
+        // Also enable the "INFO" level when trace-level debugging is enabled
+        loggingState_->infoEnabled_ = true;
+      }
+    }
+
+    void SetTargetFolder(const std::string& path)
+    {
+      boost::mutex::scoped_lock lock(loggingMutex_);
+      assert(loggingState_.get() != NULL);
+
+      PrepareLogFile(loggingState_->error_,   loggingState_->errorFile_,   "ERROR", path);
+      PrepareLogFile(loggingState_->warning_, loggingState_->warningFile_, "WARNING", path);
+      PrepareLogFile(loggingState_->info_,    loggingState_->infoFile_,    "INFO", path);
+    }
+
+    InternalLogger::InternalLogger(const char* level,
+                                   const char* file,
+                                   int line) : 
+      lock_(loggingMutex_), 
+      stream_(&null_)  // By default, logging to "/dev/null" is simulated
+    {
+      if (loggingState_.get() == NULL)
+      {
+        fprintf(stderr, "ERROR: Trying to log a message after the finalization of the logging engine\n");
+        return;
+      }
+
+      LogLevel l = StringToLogLevel(level);
+      
+      if ((l == LogLevel_Info  && !loggingState_->infoEnabled_) ||
+          (l == LogLevel_Trace && !loggingState_->traceEnabled_))
+      {
+        // This logging level is disabled, directly exit and unlock
+        // the mutex to speed-up things. The stream is set to "/dev/null"
+        lock_.unlock();
+        return;
+      }
+
+      // Compute the header of the line, temporary release the lock as
+      // this is a time-consuming operation
+      lock_.unlock();
+      std::string header;
+
+      {
+        boost::filesystem::path path(file);
+        boost::posix_time::ptime now = boost::posix_time::microsec_clock::local_time();
+        boost::posix_time::time_duration duration = now.time_of_day();
+
+        /**
+           From Google Log documentation:
+
+           "Log lines have this form:
+
+           Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg...
+
+           where the fields are defined as follows:
+
+           L                A single character, representing the log level (eg 'I' for INFO)
+           mm               The month (zero padded; ie May is '05')
+           dd               The day (zero padded)
+           hh:mm:ss.uuuuuu  Time in hours, minutes and fractional seconds
+           threadid         The space-padded thread ID as returned by GetTID() (this matches the PID on Linux)
+           file             The file name
+           line             The line number
+           msg              The user-supplied message"
+
+           In this implementation, "threadid" is not printed.
+         **/
+
+        char date[32];
+        sprintf(date, "%c%02d%02d %02d:%02d:%02d.%06d ",
+                level[0],
+                now.date().month().as_number(),
+                now.date().day().as_number(),
+                duration.hours(),
+                duration.minutes(),
+                duration.seconds(),
+                static_cast<int>(duration.fractional_seconds()));
+
+        header = std::string(date) + path.filename().string() + ":" + boost::lexical_cast<std::string>(line) + "] ";
+      }
+
+
+      // The header is computed, we now re-lock the mutex to access
+      // the stream objects. Pay attention that "loggingState_",
+      // "infoEnabled_" or "traceEnabled_" might have changed while
+      // the mutex was unlocked.
+      lock_.lock();
+
+      if (loggingState_.get() == NULL)
+      {
+        fprintf(stderr, "ERROR: Trying to log a message after the finalization of the logging engine\n");
+        return;
+      }
+
+      switch (l)
+      {
+        case LogLevel_Error:
+          stream_ = loggingState_->error_;
+          break;
+
+        case LogLevel_Warning:
+          stream_ = loggingState_->warning_;
+          break;
+
+        case LogLevel_Info:
+          if (loggingState_->infoEnabled_)
+          {
+            stream_ = loggingState_->info_;
+          }
+
+          break;
+
+        case LogLevel_Trace:
+          if (loggingState_->traceEnabled_)
+          {
+            stream_ = loggingState_->info_;
+          }
+
+          break;
+
+        default:
+          throw OrthancException(ErrorCode_InternalError);
+      }
+
+      if (stream_ == &null_)
+      {
+        // The logging is disabled for this level. The stream is the
+        // "null_" member of this object, so we can release the global
+        // mutex.
+        lock_.unlock();
+      }
+
+      (*stream_) << header;
+    }
+
+
+    InternalLogger::~InternalLogger()
+    {
+      if (stream_ != &null_)
+      {
+#if defined(_WIN32)
+        *stream_ << "\r\n";
+#else
+        *stream_ << "\n";
+#endif
+
+        stream_->flush();
+      }
+    }
+      
+
+  }
+}
+
+#endif   // ORTHANC_ENABLE_LOGGING
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Logging.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,112 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include <iostream>
+
+namespace Orthanc
+{
+  namespace Logging
+  {
+    void Initialize();
+
+    void Finalize();
+
+    void EnableInfoLevel(bool enabled);
+
+    void EnableTraceLevel(bool enabled);
+
+    void SetTargetFolder(const std::string& path);
+
+    struct NullStream : public std::ostream 
+    {
+      NullStream() : 
+        std::ios(0), 
+        std::ostream(0)
+      {
+      }
+      
+      std::ostream& operator<< (const std::string& message)
+      {
+        return *this;
+      }
+    };
+  }
+}
+
+
+#if ORTHANC_ENABLE_LOGGING != 1
+
+#  define LOG(level)   ::Orthanc::Logging::NullStream()
+#  define VLOG(level)  ::Orthanc::Logging::NullStream()
+
+#else  /* ORTHANC_ENABLE_LOGGING == 1 */
+
+#if ORTHANC_ENABLE_GOOGLE_LOG == 1
+#  include <stdlib.h>  // Including this fixes a problem in glog for recent releases of MinGW
+#  include <glog/logging.h>
+#else
+#  include <boost/thread/mutex.hpp>
+#  define LOG(level)  ::Orthanc::Logging::InternalLogger(#level,  __FILE__, __LINE__)
+#  define VLOG(level) ::Orthanc::Logging::InternalLogger("TRACE", __FILE__, __LINE__)
+#endif
+
+#if ORTHANC_ENABLE_GOOGLE_LOG != 1
+namespace Orthanc
+{
+  namespace Logging
+  {
+    class InternalLogger
+    {
+    private:
+      boost::mutex::scoped_lock lock_;
+      NullStream                null_;
+      std::ostream*             stream_;
+
+    public:
+      InternalLogger(const char* level,
+                     const char* file,
+                     int line);
+
+      ~InternalLogger();
+      
+      std::ostream& operator<< (const std::string& message)
+      {
+        return (*stream_) << message;
+      }
+    };
+  }
+}
+#endif
+
+#endif  // ORTHANC_ENABLE_LOGGING
--- a/Core/Lua/LuaContext.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/Lua/LuaContext.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,8 +33,12 @@
 #include "../PrecompiledHeaders.h"
 #include "LuaContext.h"
 
-#include <glog/logging.h>
+#include "../Logging.h"
+#include "../OrthancException.h"
+
+#include <set>
 #include <cassert>
+#include <boost/lexical_cast.hpp>
 
 extern "C" 
 {
@@ -44,16 +48,26 @@
 
 namespace Orthanc
 {
+  static bool OnlyContainsDigits(const std::string& s)
+  {
+    for (size_t i = 0; i < s.size(); i++)
+    {
+      if (!isdigit(s[i]))
+      {
+        return false;
+      }
+    }
+
+    return true;
+  }
+  
+
   LuaContext& LuaContext::GetLuaContext(lua_State *state)
   {
-    // Get the pointer to the "LuaContext" underlying object
-    lua_getglobal(state, "_LuaContext");
-    assert(lua_type(state, -1) == LUA_TLIGHTUSERDATA);
-    LuaContext* that = const_cast<LuaContext*>(reinterpret_cast<const LuaContext*>(lua_topointer(state, -1)));
-    assert(that != NULL);
-    lua_pop(state, 1);
+    const void* value = GetGlobalVariable(state, "_LuaContext");
+    assert(value != NULL);
 
-    return *that;
+    return *const_cast<LuaContext*>(reinterpret_cast<const LuaContext*>(value));
   }
 
   int LuaContext::PrintToLog(lua_State *state)
@@ -94,6 +108,64 @@
   }
 
 
+  int LuaContext::ParseJson(lua_State *state)
+  {
+    LuaContext& that = GetLuaContext(state);
+
+    int nArgs = lua_gettop(state);
+    if (nArgs != 1 ||
+        !lua_isstring(state, 1))    // Password
+    {
+      lua_pushnil(state);
+      return 1;
+    }
+
+    const char* str = lua_tostring(state, 1);
+
+    Json::Value value;
+    Json::Reader reader;
+    if (reader.parse(str, str + strlen(str), value))
+    {
+      that.PushJson(value);
+    }
+    else
+    {
+      lua_pushnil(state);
+    }
+
+    return 1;
+  }
+
+
+  int LuaContext::DumpJson(lua_State *state)
+  {
+    LuaContext& that = GetLuaContext(state);
+
+    int nArgs = lua_gettop(state);
+    if ((nArgs != 1 && nArgs != 2) ||
+        (nArgs == 2 && !lua_isboolean(state, 2)))
+    {
+      lua_pushnil(state);
+      return 1;
+    }
+
+    bool keepStrings = false;
+    if (nArgs == 2)
+    {
+      keepStrings = lua_toboolean(state, 2) ? true : false;
+    }
+
+    Json::Value json;
+    that.GetJson(json, 1, keepStrings);
+
+    Json::FastWriter writer;
+    std::string s = writer.write(json);
+    lua_pushlstring(state, s.c_str(), s.size());
+
+    return 1;
+  }
+
+
   int LuaContext::SetHttpCredentials(lua_State *state)
   {
     LuaContext& that = GetLuaContext(state);
@@ -126,13 +198,13 @@
     {
       httpClient_.Apply(str);
     }
-    catch (OrthancException& e)
+    catch (OrthancException&)
     {
       return false;
     }
 
     // Return the result of the HTTP request
-    lua_pushstring(state, str.c_str());
+    lua_pushlstring(state, str.c_str(), str.size());
 
     return true;
   }
@@ -147,7 +219,7 @@
     if (nArgs != 1 || !lua_isstring(state, 1))  // URL
     {
       LOG(ERROR) << "Lua: Bad parameters to HttpGet()";
-      lua_pushstring(state, "ERROR");
+      lua_pushnil(state);
       return 1;
     }
 
@@ -160,7 +232,7 @@
     if (!that.AnswerHttpQuery(state))
     {
       LOG(ERROR) << "Lua: Error in HttpGet() for URL " << url;
-      lua_pushstring(state, "ERROR");
+      lua_pushnil(state);
     }
 
     return 1;
@@ -179,7 +251,7 @@
         (nArgs >= 2 && !lua_isstring(state, 2)))  // Body data
     {
       LOG(ERROR) << "Lua: Bad parameters to HttpPost() or HttpPut()";
-      lua_pushstring(state, "ERROR");
+      lua_pushnil(state);
       return 1;
     }
 
@@ -190,18 +262,18 @@
 
     if (nArgs >= 2)
     {
-      that.httpClient_.SetPostData(lua_tostring(state, 2));
+      that.httpClient_.SetBody(lua_tostring(state, 2));
     }
     else
     {
-      that.httpClient_.AccessPostData().clear();
+      that.httpClient_.GetBody().clear();
     }
 
     // Do the HTTP POST/PUT request
     if (!that.AnswerHttpQuery(state))
     {
       LOG(ERROR) << "Lua: Error in HttpPost() or HttpPut() for URL " << url;
-      lua_pushstring(state, "ERROR");
+      lua_pushnil(state);
     }
 
     return 1;
@@ -229,7 +301,7 @@
     if (nArgs != 1 || !lua_isstring(state, 1))  // URL
     {
       LOG(ERROR) << "Lua: Bad parameters to HttpDelete()";
-      lua_pushstring(state, "ERROR");
+      lua_pushnil(state);
       return 1;
     }
 
@@ -243,7 +315,7 @@
     if (!that.httpClient_.Apply(s))
     {
       LOG(ERROR) << "Lua: Error in HttpDelete() for URL " << url;
-      lua_pushstring(state, "ERROR");
+      lua_pushnil(state);
     }
     else
     {
@@ -258,7 +330,8 @@
   {
     if (value.isString())
     {
-      lua_pushstring(lua_, value.asCString());
+      const std::string s = value.asString();
+      lua_pushlstring(lua_, s.c_str(), s.size());
     }
     else if (value.isDouble())
     {
@@ -307,7 +380,7 @@
              it = members.begin(); it != members.end(); ++it)
       {
         // Push the index of the cell
-        lua_pushstring(lua_, it->c_str());
+        lua_pushlstring(lua_, it->c_str(), it->size());
 
         // Push the value of the cell
         PushJson(value[*it]);
@@ -318,7 +391,116 @@
     }
     else
     {
-      throw LuaException("Unsupported JSON conversion");
+      throw OrthancException(ErrorCode_JsonToLuaTable);
+    }
+  }
+
+
+  void LuaContext::GetJson(Json::Value& result,
+                           int top,
+                           bool keepStrings)
+  {
+    if (lua_istable(lua_, top))
+    {
+      Json::Value tmp = Json::objectValue;
+      bool isArray = true;
+      size_t size = 0;
+
+      // Code adapted from: http://stackoverflow.com/a/6142700/881731
+      
+      // Push another reference to the table on top of the stack (so we know
+      // where it is, and this function can work for negative, positive and
+      // pseudo indices
+      lua_pushvalue(lua_, top);
+      // stack now contains: -1 => table
+      lua_pushnil(lua_);
+      // stack now contains: -1 => nil; -2 => table
+      while (lua_next(lua_, -2))
+      {
+        // stack now contains: -1 => value; -2 => key; -3 => table
+        // copy the key so that lua_tostring does not modify the original
+        lua_pushvalue(lua_, -2);
+        // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table
+        std::string key(lua_tostring(lua_, -1));
+        Json::Value v;
+        GetJson(v, -2, keepStrings);
+
+        tmp[key] = v;
+
+        size += 1;
+        try
+        {
+          if (!OnlyContainsDigits(key) ||
+              boost::lexical_cast<size_t>(key) != size)
+          {
+            isArray = false;
+          }
+        }
+        catch (boost::bad_lexical_cast&)
+        {
+          isArray = false;
+        }
+        
+        // pop value + copy of key, leaving original key
+        lua_pop(lua_, 2);
+        // stack now contains: -1 => key; -2 => table
+      }
+      // stack now contains: -1 => table (when lua_next returns 0 it pops the key
+      // but does not push anything.)
+      // Pop table
+      lua_pop(lua_, 1);
+
+      // Stack is now the same as it was on entry to this function
+
+      if (isArray)
+      {
+        result = Json::arrayValue;
+        for (size_t i = 0; i < size; i++)
+        {
+          result.append(tmp[boost::lexical_cast<std::string>(i + 1)]);
+        }
+      }
+      else
+      {
+        result = tmp;
+      }
+    }
+    else if (lua_isnil(lua_, top))
+    {
+      result = Json::nullValue;
+    }
+    else if (!keepStrings &&
+             lua_isboolean(lua_, top))
+    {
+      result = lua_toboolean(lua_, top) ? true : false;
+    }
+    else if (!keepStrings &&
+             lua_isnumber(lua_, top))
+    {
+      // Convert to "int" if truncation does not loose precision
+      double value = static_cast<double>(lua_tonumber(lua_, top));
+      int truncated = static_cast<int>(value);
+
+      if (std::abs(value - static_cast<double>(truncated)) <= 
+          std::numeric_limits<double>::epsilon())
+      {
+        result = truncated;
+      }
+      else
+      {
+        result = value;
+      }
+    }
+    else if (lua_isstring(lua_, top))
+    {
+      // Caution: The "lua_isstring()" case must be the last, since
+      // Lua can convert most types to strings by default.
+      result = std::string(lua_tostring(lua_, top));
+    }
+    else
+    {
+      LOG(WARNING) << "Unsupported Lua type when returning Json";
+      result = Json::nullValue;
     }
   }
 
@@ -328,19 +510,20 @@
     lua_ = luaL_newstate();
     if (!lua_)
     {
-      throw LuaException("Unable to create the Lua context");
+      throw OrthancException(ErrorCode_CannotCreateLua);
     }
 
     luaL_openlibs(lua_);
     lua_register(lua_, "print", PrintToLog);
+    lua_register(lua_, "ParseJson", ParseJson);
+    lua_register(lua_, "DumpJson", DumpJson);
     lua_register(lua_, "HttpGet", CallHttpGet);
     lua_register(lua_, "HttpPost", CallHttpPost);
     lua_register(lua_, "HttpPut", CallHttpPut);
     lua_register(lua_, "HttpDelete", CallHttpDelete);
     lua_register(lua_, "SetHttpCredentials", SetHttpCredentials);
-    
-    lua_pushlightuserdata(lua_, this);
-    lua_setglobal(lua_, "_LuaContext");
+
+    SetGlobalVariable("_LuaContext", this);
   }
 
 
@@ -364,7 +547,7 @@
       std::string description(lua_tostring(lua_, -1));
       lua_pop(lua_, 1); /* pop error message from the stack */
       LOG(ERROR) << "Error while executing Lua script: " << description;
-      throw LuaException(description);
+      throw OrthancException(ErrorCode_CannotExecuteLua);
     }
 
     if (output != NULL)
@@ -399,8 +582,33 @@
     Json::Reader reader;
     if (!reader.parse(s, output))
     {
-      throw OrthancException(ErrorCode_BadFileFormat);
+      throw OrthancException(ErrorCode_BadJson);
     }
   }
 
+
+  void LuaContext::RegisterFunction(const char* name,
+                                    lua_CFunction func)
+  {
+    lua_register(lua_, name, func);
+  }
+
+
+  void LuaContext::SetGlobalVariable(const char* name,
+                                     void* value)
+  {
+    lua_pushlightuserdata(lua_, value);
+    lua_setglobal(lua_, name);
+  }
+
+  
+  const void* LuaContext::GetGlobalVariable(lua_State* state,
+                                            const char* name)
+  {
+    lua_getglobal(state, name);
+    assert(lua_type(state, -1) == LUA_TLIGHTUSERDATA);
+    const void* value = lua_topointer(state, -1);
+    lua_pop(state, 1);
+    return value;
+  }
 }
--- a/Core/Lua/LuaContext.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/Lua/LuaContext.h	Wed Sep 30 13:23:31 2015 +0200
@@ -32,7 +32,6 @@
 
 #pragma once
 
-#include "LuaException.h"
 #include "../HttpClient.h"
 
 extern "C" 
@@ -54,9 +53,9 @@
     std::string log_;
     HttpClient httpClient_;
 
-    static LuaContext& GetLuaContext(lua_State *state);
-
     static int PrintToLog(lua_State *state);
+    static int ParseJson(lua_State *state);
+    static int DumpJson(lua_State *state);
 
     static int SetHttpCredentials(lua_State *state);
 
@@ -72,7 +71,9 @@
     void ExecuteInternal(std::string* output,
                          const std::string& command);
 
-    void PushJson(const Json::Value& value);
+    void GetJson(Json::Value& result,
+                 int top,
+                 bool keepStrings);
     
   public:
     LuaContext();
@@ -107,5 +108,18 @@
     {
       httpClient_.SetProxy(proxy);
     }
+
+    void RegisterFunction(const char* name,
+                          lua_CFunction func);
+
+    void SetGlobalVariable(const char* name,
+                           void* value);
+
+    static LuaContext& GetLuaContext(lua_State *state);
+
+    static const void* GetGlobalVariable(lua_State* state,
+                                         const char* name);
+
+    void PushJson(const Json::Value& value);
   };
 }
--- a/Core/Lua/LuaException.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../OrthancException.h"
-
-namespace Orthanc
-{
-  class LuaException : public OrthancException
-  {
-  public:
-    LuaException(const char* explanation) : 
-      OrthancException(explanation)
-    {
-    }
-
-    LuaException(const std::string& explanation) : 
-      OrthancException(explanation)
-    {
-    }
-  };
-}
--- a/Core/Lua/LuaFunctionCall.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/Lua/LuaFunctionCall.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,10 +33,12 @@
 #include "../PrecompiledHeaders.h"
 #include "LuaFunctionCall.h"
 
+#include "../OrthancException.h"
+#include "../Logging.h"
+
 #include <cassert>
 #include <stdio.h>
 #include <boost/lexical_cast.hpp>
-#include <glog/logging.h>
 
 namespace Orthanc
 {
@@ -44,7 +46,7 @@
   {
     if (isExecuted_)
     {
-      throw LuaException("Arguments cannot be pushed after the function is executed");
+      throw OrthancException(ErrorCode_LuaAlreadyExecuted);
     }
   }
 
@@ -61,7 +63,7 @@
   void LuaFunctionCall::PushString(const std::string& value)
   {
     CheckAlreadyExecuted();
-    lua_pushstring(context_.lua_, value.c_str());
+    lua_pushlstring(context_.lua_, value.c_str(), value.size());
   }
 
   void LuaFunctionCall::PushBoolean(bool value)
@@ -102,12 +104,14 @@
           
       std::string description(lua_tostring(context_.lua_, -1));
       lua_pop(context_.lua_, 1); /* pop error message from the stack */
-      throw LuaException(description);
+      LOG(ERROR) << description;
+
+      throw OrthancException(ErrorCode_CannotExecuteLua);
     }
 
     if (lua_gettop(context_.lua_) < numOutputs)
     {
-      throw LuaException("The function does not give the expected number of outputs");
+      throw OrthancException(ErrorCode_LuaBadOutput);
     }
 
     isExecuted_ = true;
@@ -119,104 +123,33 @@
     
     if (!lua_isboolean(context_.lua_, 1))
     {
-      throw LuaException("The function is not a predicate (only true/false outputs allowed)");
+      throw OrthancException(ErrorCode_NotLuaPredicate);
     }
 
     return lua_toboolean(context_.lua_, 1) != 0;
   }
 
 
-  static void PopJson(Json::Value& result,
-                      lua_State* lua,
-                      int top)
+  void LuaFunctionCall::ExecuteToJson(Json::Value& result,
+                                      bool keepStrings)
   {
-    if (lua_istable(lua, top))
-    {
-      Json::Value tmp = Json::objectValue;
-      bool isArray = true;
-      size_t size = 0;
+    ExecuteInternal(1);
+    context_.GetJson(result, lua_gettop(context_.lua_), keepStrings);
+  }
 
-      // http://stackoverflow.com/a/6142700/881731
-      
-      // Push another reference to the table on top of the stack (so we know
-      // where it is, and this function can work for negative, positive and
-      // pseudo indices
-      lua_pushvalue(lua, top);
-      // stack now contains: -1 => table
-      lua_pushnil(lua);
-      // stack now contains: -1 => nil; -2 => table
-      while (lua_next(lua, -2))
-      {
-        // stack now contains: -1 => value; -2 => key; -3 => table
-        // copy the key so that lua_tostring does not modify the original
-        lua_pushvalue(lua, -2);
-        // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table
-        std::string key(lua_tostring(lua, -1));
-        Json::Value v;
-        PopJson(v, lua, -2);
-
-        tmp[key] = v;
 
-        size += 1;
-        try
-        {
-          if (boost::lexical_cast<size_t>(key) != size)
-          {
-            isArray = false;
-          }
-        }
-        catch (boost::bad_lexical_cast&)
-        {
-          isArray = false;
-        }
-        
-        // pop value + copy of key, leaving original key
-        lua_pop(lua, 2);
-        // stack now contains: -1 => key; -2 => table
-      }
-      // stack now contains: -1 => table (when lua_next returns 0 it pops the key
-      // but does not push anything.)
-      // Pop table
-      lua_pop(lua, 1);
-
-      // Stack is now the same as it was on entry to this function
-
-      if (isArray)
-      {
-        result = Json::arrayValue;
-        for (size_t i = 0; i < size; i++)
-        {
-          result.append(tmp[boost::lexical_cast<std::string>(i + 1)]);
-        }
-      }
-      else
-      {
-        result = tmp;
-      }
-    }
-    else if (lua_isnumber(lua, top))
+  void LuaFunctionCall::ExecuteToString(std::string& result)
+  {
+    ExecuteInternal(1);
+    
+    int top = lua_gettop(context_.lua_);
+    if (lua_isstring(context_.lua_, top))
     {
-      result = static_cast<float>(lua_tonumber(lua, top));
-    }
-    else if (lua_isstring(lua, top))
-    {
-      result = std::string(lua_tostring(lua, top));
-    }
-    else if (lua_isboolean(lua, top))
-    {
-      result = static_cast<bool>(lua_toboolean(lua, top));
+      result = lua_tostring(context_.lua_, top);
     }
     else
     {
-      LOG(WARNING) << "Unsupported Lua type when returning Json";
-      result = Json::nullValue;
+      throw OrthancException(ErrorCode_LuaReturnsNoString);
     }
   }
-
-
-  void LuaFunctionCall::ExecuteToJson(Json::Value& result)
-  {
-    ExecuteInternal(1);
-    PopJson(result, context_.lua_, lua_gettop(context_.lua_));
-  }
 }
--- a/Core/Lua/LuaFunctionCall.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/Lua/LuaFunctionCall.h	Wed Sep 30 13:23:31 2015 +0200
@@ -69,6 +69,9 @@
 
     bool ExecutePredicate();
 
-    void ExecuteToJson(Json::Value& result);                    
+    void ExecuteToJson(Json::Value& result,
+                       bool keepStrings);
+
+    void ExecuteToString(std::string& result);
   };
 }
--- a/Core/MultiThreading/ArrayFilledByThreads.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "../PrecompiledHeaders.h"
-#include "ArrayFilledByThreads.h"
-
-#include "../MultiThreading/ThreadedCommandProcessor.h"
-#include "../OrthancException.h"
-
-namespace Orthanc
-{
-  class ArrayFilledByThreads::Command : public ICommand
-  {
-  private:
-    ArrayFilledByThreads&  that_;
-    size_t  index_;
-
-  public:
-    Command(ArrayFilledByThreads& that,
-            size_t index) :
-      that_(that),
-      index_(index)
-    {
-    }
-
-    virtual bool Execute()
-    {
-      std::auto_ptr<IDynamicObject> obj(that_.filler_.GetFillerItem(index_));
-      if (obj.get() == NULL)
-      {
-        return false;
-      }
-      else
-      {
-        boost::mutex::scoped_lock lock(that_.mutex_);
-        that_.array_[index_] = obj.release();
-        return true;
-      }
-    }
-  };
-
-  void ArrayFilledByThreads::Clear()
-  {
-    for (size_t i = 0; i < array_.size(); i++)
-    {
-      if (array_[i])
-        delete array_[i];
-    }
-
-    array_.clear();
-    filled_ = false;
-  }
-
-  void ArrayFilledByThreads::Update()
-  {
-    if (!filled_)
-    {
-      array_.resize(filler_.GetFillerSize());
-
-      Orthanc::ThreadedCommandProcessor processor(threadCount_);
-      for (size_t i = 0; i < array_.size(); i++)
-      {
-        processor.Post(new Command(*this, i));
-      }
-
-      processor.Join();
-      filled_ = true;
-    }
-  }
-
-
-  ArrayFilledByThreads::ArrayFilledByThreads(IFiller& filler) : filler_(filler)
-  {
-    filled_ = false;
-    threadCount_ = 4;
-  }
-
-
-  ArrayFilledByThreads::~ArrayFilledByThreads()
-  {
-    Clear();
-  }
-
-  
-  void ArrayFilledByThreads::Reload()
-  {
-    Clear();
-    Update();
-  }
-
-
-  void ArrayFilledByThreads::Invalidate()
-  {
-    Clear();
-  }
-
-
-  void ArrayFilledByThreads::SetThreadCount(unsigned int t)
-  {
-    if (t < 1)
-    {
-      throw OrthancException(ErrorCode_ParameterOutOfRange);
-    }
-
-    threadCount_ = t;
-  }
-
-
-  size_t ArrayFilledByThreads::GetSize()
-  {
-    Update();
-    return array_.size();
-  }
-
-
-  IDynamicObject& ArrayFilledByThreads::GetItem(size_t index)
-  {
-    if (index >= GetSize())
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    return *array_[index];
-  }
-}
--- a/Core/MultiThreading/ArrayFilledByThreads.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-#pragma once
-
-#include <boost/thread.hpp>
-
-#include "../IDynamicObject.h"
-
-namespace Orthanc
-{
-  class ArrayFilledByThreads
-  {
-  public:
-    class IFiller
-    {
-    public:
-      virtual size_t GetFillerSize() = 0;
-
-      virtual IDynamicObject* GetFillerItem(size_t index) = 0;
-    };
-
-  private:
-    IFiller& filler_;
-    boost::mutex  mutex_;
-    std::vector<IDynamicObject*>  array_;
-    bool filled_;
-    unsigned int threadCount_;
-
-    class Command;
-
-    void Clear();
-
-    void Update();
-
-  public:
-    ArrayFilledByThreads(IFiller& filler);
-
-    ~ArrayFilledByThreads();
-  
-    void Reload();
-
-    void Invalidate();
-
-    void SetThreadCount(unsigned int t);
-
-    unsigned int GetThreadCount() const
-    {
-      return threadCount_;
-    }
-
-    size_t GetSize();
-
-    IDynamicObject& GetItem(size_t index);
-  };
-}
-
--- a/Core/MultiThreading/BagOfRunnablesBySteps.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/MultiThreading/BagOfRunnablesBySteps.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,6 +33,8 @@
 #include "../PrecompiledHeaders.h"
 #include "BagOfRunnablesBySteps.h"
 
+#include "../Logging.h"
+
 #include <stack>
 #include <boost/thread.hpp>
 
@@ -128,15 +130,10 @@
 
   BagOfRunnablesBySteps::~BagOfRunnablesBySteps()
   {
-    StopAll();
-
-    // Stop the finish listener
-    pimpl_->stopFinishListener_ = true;
-    pimpl_->oneThreadIsStopped_.notify_one();  // Awakens the listener
-
-    if (pimpl_->finishListener_->joinable())
+    if (!pimpl_->stopFinishListener_)
     {
-      pimpl_->finishListener_->join();
+      LOG(ERROR) << "INTERNAL ERROR: BagOfRunnablesBySteps::Finalize() should be invoked manually to avoid mess in the destruction order!";
+      Finalize();
     }
   }
 
@@ -165,4 +162,24 @@
 
     pimpl_->continue_ = true;
   }
+
+
+
+  void BagOfRunnablesBySteps::Finalize()
+  {
+    if (!pimpl_->stopFinishListener_)
+    {
+      StopAll();
+
+      // Stop the finish listener
+      pimpl_->stopFinishListener_ = true;
+      pimpl_->oneThreadIsStopped_.notify_one();  // Awakens the listener
+
+      if (pimpl_->finishListener_->joinable())
+      {
+        pimpl_->finishListener_->join();
+      }
+    }
+  }
+
 }
--- a/Core/MultiThreading/BagOfRunnablesBySteps.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/MultiThreading/BagOfRunnablesBySteps.h	Wed Sep 30 13:23:31 2015 +0200
@@ -58,5 +58,7 @@
     void Add(IRunnableBySteps* runnable);
 
     void StopAll();
+
+    void Finalize();
   };
 }
--- a/Core/MultiThreading/Mutex.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/MultiThreading/Mutex.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -37,7 +37,7 @@
 
 #if defined(_WIN32)
 #include <windows.h>
-#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
+#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__FreeBSD__)
 #include <pthread.h>
 #else
 #error Support your platform here
@@ -75,7 +75,7 @@
   }
 
 
-#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
+#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__FreeBSD__)
 
   struct Mutex::PImpl
   {
--- a/Core/MultiThreading/Semaphore.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/MultiThreading/Semaphore.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -30,6 +30,7 @@
  **/
 
 
+#include "../PrecompiledHeaders.h"
 #include "Semaphore.h"
 
 #include "../OrthancException.h"
--- a/Core/MultiThreading/ThreadedCommandProcessor.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,211 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "../PrecompiledHeaders.h"
-#include "ThreadedCommandProcessor.h"
-
-#include "../OrthancException.h"
-
-namespace Orthanc
-{
-  static const int32_t TIMEOUT = 10;
-
-
-  void ThreadedCommandProcessor::Processor(ThreadedCommandProcessor* that)
-  {
-    while (!that->done_)
-    {
-      std::auto_ptr<IDynamicObject> command(that->queue_.Dequeue(TIMEOUT));
-
-      if (command.get() != NULL)
-      {
-        bool success = false;
-
-        try
-        {
-          if (that->success_)
-          {
-            // No command has failed so far
-
-            if (that->cancel_)
-            {
-              // The commands have been canceled. Skip the execution
-              // of this command, yet mark it as succeeded.
-              success = true;
-            }
-            else
-            {
-              success = dynamic_cast<ICommand&>(*command).Execute();
-            }
-          }
-          else
-          {
-            // A command has already failed. Skip the execution of this command.
-          }
-        }
-        catch (OrthancException)
-        {
-        }
-
-        {
-          boost::mutex::scoped_lock lock(that->mutex_);
-          assert(that->remainingCommands_ > 0);
-          that->remainingCommands_--;
-
-          if (!success)
-          {
-            if (!that->cancel_ && that->listener_ && that->success_)
-            {
-              // This is the first command that fails
-              that->listener_->SignalFailure();
-            }
-
-            that->success_ = false;
-          }
-          else
-          {
-            if (!that->cancel_ && that->listener_)
-            {
-              if (that->remainingCommands_ == 0)
-              {
-                that->listener_->SignalSuccess(that->totalCommands_);
-              }
-              else
-              {
-                that->listener_->SignalProgress(that->totalCommands_ - that->remainingCommands_,
-                                                that->totalCommands_);
-              }
-            }
-          }
-
-          that->processedCommand_.notify_all();
-        }
-      }
-    }
-  }
-
-
-  ThreadedCommandProcessor::ThreadedCommandProcessor(unsigned int numThreads)
-  {
-    if (numThreads < 1)
-    {
-      throw OrthancException(ErrorCode_ParameterOutOfRange);
-    }
-
-    listener_ = NULL;
-    success_ = true;
-    done_ = false;
-    cancel_ = false;
-    threads_.resize(numThreads);
-    remainingCommands_ = 0;
-    totalCommands_ = 0;
-
-    for (unsigned int i = 0; i < numThreads; i++)
-    {
-      threads_[i] = new boost::thread(Processor, this);
-    }
-  }
-
-
-  ThreadedCommandProcessor::~ThreadedCommandProcessor()
-  {
-    done_ = true;
-      
-    for (unsigned int i = 0; i < threads_.size(); i++)
-    {
-      boost::thread* t = threads_[i];
-
-      if (t != NULL)
-      {
-        if (t->joinable())
-        {
-          t->join();
-        }
-
-        delete t;
-      }
-    }
-  }
-
-
-  void ThreadedCommandProcessor::Post(ICommand* command)
-  {
-    if (command == NULL)
-    {
-      throw OrthancException(ErrorCode_ParameterOutOfRange);
-    }
-
-    boost::mutex::scoped_lock lock(mutex_);
-    queue_.Enqueue(command);
-    remainingCommands_++;
-    totalCommands_++;
-  }
-
-
-  bool ThreadedCommandProcessor::Join()
-  {
-    boost::mutex::scoped_lock lock(mutex_);
-
-    while (!remainingCommands_ == 0)
-    {
-      processedCommand_.wait(lock);
-    }
-
-    if (cancel_ && listener_)
-    {
-      listener_->SignalCancel();
-    }
-
-    // Reset the sequence counters for subsequent commands
-    bool hasSucceeded = success_;
-    success_ = true;
-    totalCommands_ = 0;
-    cancel_ = false;
-
-    return hasSucceeded;
-  }
-
-
-  void ThreadedCommandProcessor::Cancel()
-  {
-    boost::mutex::scoped_lock lock(mutex_);
-
-    cancel_ = true;
-  }
-
-
-  void ThreadedCommandProcessor::SetListener(IListener& listener)
-  {
-    boost::mutex::scoped_lock lock(mutex_);
-    listener_ = &listener;
-  }
-}
--- a/Core/MultiThreading/ThreadedCommandProcessor.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,94 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../ICommand.h"
-
-#include "SharedMessageQueue.h"
-
-namespace Orthanc
-{
-  class ThreadedCommandProcessor
-  {
-  public:
-    class IListener
-    {
-    public:
-      virtual ~IListener()
-      {
-      }
-
-      virtual void SignalProgress(unsigned int current,
-                                  unsigned int total) = 0;
-
-      virtual void SignalSuccess(unsigned int total) = 0;
-
-      virtual void SignalFailure() = 0;
-
-      virtual void SignalCancel() = 0;
-    };
-
-  private:
-    SharedMessageQueue  queue_;
-    bool done_;
-    bool cancel_;
-    std::vector<boost::thread*>  threads_;
-    IListener* listener_;
-
-    boost::mutex mutex_;
-    bool success_;
-    unsigned int remainingCommands_, totalCommands_;
-    boost::condition_variable processedCommand_;
-
-    static void Processor(ThreadedCommandProcessor* that);
-
-  public:
-    ThreadedCommandProcessor(unsigned int numThreads);
-
-    ~ThreadedCommandProcessor();
-
-    // This takes the ownership of the command
-    void Post(ICommand* command);
-
-    bool Join();
-
-    void Cancel();
-
-    void SetListener(IListener& listener);
-
-    IListener& GetListener() const
-    {
-      return *listener_;
-    }
-  };
-}
--- a/Core/OrthancException.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,141 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "PrecompiledHeaders.h"
-#include "OrthancException.h"
-
-namespace Orthanc
-{
-  const char* OrthancException::What() const
-  {
-    if (error_ == ErrorCode_Custom)
-    {
-      return custom_.c_str();
-    }
-    else
-    {
-      return GetDescription(error_);
-    }
-  }
-
-
-  const char* OrthancException::GetDescription(ErrorCode error)
-  {
-    switch (error)
-    {
-      case ErrorCode_Success:
-        return "Success";
-
-      case ErrorCode_ParameterOutOfRange:
-        return "Parameter out of range";
-
-      case ErrorCode_NotImplemented:
-        return "Not implemented yet";
-
-      case ErrorCode_InternalError:
-        return "Internal error";
-
-      case ErrorCode_NotEnoughMemory:
-        return "Not enough memory";
-
-      case ErrorCode_UriSyntax:
-        return "Badly formatted URI";
-
-      case ErrorCode_BadParameterType:
-        return "Bad type for a parameter";
-
-      case ErrorCode_InexistentFile:
-        return "Inexistent file";
-
-      case ErrorCode_BadFileFormat:
-        return "Bad file format";
-
-      case ErrorCode_CannotWriteFile:
-        return "Cannot write to file";
-
-      case ErrorCode_Timeout:
-        return "Timeout";
-
-      case ErrorCode_UnknownResource:
-        return "Unknown resource";
-
-      case ErrorCode_BadSequenceOfCalls:
-        return "Bad sequence of calls";
-
-      case ErrorCode_IncompatibleDatabaseVersion:
-        return "Incompatible version of the database";
-
-      case ErrorCode_FullStorage:
-        return "The file storage is full";
-
-      case ErrorCode_InexistentItem:
-        return "Accessing an inexistent item";
-
-      case ErrorCode_BadRequest:
-        return "Bad request";
-
-      case ErrorCode_NetworkProtocol:
-        return "Error in the network protocol";
-
-      case ErrorCode_CorruptedFile:
-        return "Corrupted file (inconsistent MD5 hash)";
-
-      case ErrorCode_InexistentTag:
-        return "Inexistent tag";
-
-      case ErrorCode_ReadOnly:
-        return "Cannot modify a read-only data structure";
-
-      case ErrorCode_IncompatibleImageSize:
-        return "Incompatible size of the images";
-
-      case ErrorCode_IncompatibleImageFormat:
-        return "Incompatible format of the images";
-
-      case ErrorCode_SharedLibrary:
-        return "Error while using a shared library (plugin)";
-
-      case ErrorCode_SystemCommand:
-        return "Error while calling a system command";
-
-      case ErrorCode_Plugin:
-        return "Error encountered inside a plugin";
-
-      case ErrorCode_Database:
-        return "Error with the database engine";
-
-      case ErrorCode_Custom:
-      default:
-        return "???";
-    }
-  }
-}
--- a/Core/OrthancException.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/OrthancException.h	Wed Sep 30 13:23:31 2015 +0200
@@ -32,6 +32,7 @@
 
 #pragma once
 
+#include <stdint.h>
 #include <string>
 #include "Enumerations.h"
 
@@ -40,33 +41,36 @@
   class OrthancException
   {
   protected:
-    ErrorCode error_;
-    std::string custom_;
+    ErrorCode  errorCode_;
+    HttpStatus httpStatus_;
 
   public:
-    static const char* GetDescription(ErrorCode error);
-
-    OrthancException(const char* custom) : 
-      error_(ErrorCode_Custom),
-      custom_(custom)
+    OrthancException(ErrorCode errorCode) : 
+      errorCode_(errorCode),
+      httpStatus_(ConvertErrorCodeToHttpStatus(errorCode))
     {
     }
 
-    OrthancException(const std::string& custom) : 
-      error_(ErrorCode_Custom),
-      custom_(custom)
-    {
-    }
-
-    OrthancException(ErrorCode error) : error_(error)
+    OrthancException(ErrorCode errorCode,
+                     HttpStatus httpStatus) :
+      errorCode_(errorCode),
+      httpStatus_(httpStatus)
     {
     }
 
     ErrorCode GetErrorCode() const
     {
-      return error_;
+      return errorCode_;
     }
 
-    const char* What() const;
+    HttpStatus GetHttpStatus() const
+    {
+      return httpStatus_;
+    }
+
+    const char* What() const
+    {
+      return EnumerationToString(errorCode_);
+    }
   };
 }
--- a/Core/PrecompiledHeaders.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/PrecompiledHeaders.h	Wed Sep 30 13:23:31 2015 +0200
@@ -46,7 +46,6 @@
 #include <boost/thread.hpp>
 #include <boost/thread/shared_mutex.hpp>
 
-#include <glog/logging.h>
 #include <json/value.h>
 
 #if ORTHANC_PUGIXML_ENABLED == 1
@@ -54,6 +53,7 @@
 #endif
 
 #include "Enumerations.h"
+#include "Logging.h"
 #include "OrthancException.h"
 #include "Toolbox.h"
 #include "Uuid.h"
--- a/Core/RestApi/RestApi.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/RestApi/RestApi.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,9 +33,9 @@
 #include "../PrecompiledHeaders.h"
 #include "RestApi.h"
 
+#include "../Logging.h"
+
 #include <stdlib.h>   // To define "_exit()" under Windows
-#include <glog/logging.h>
-
 #include <stdio.h>
 
 namespace Orthanc
@@ -48,30 +48,42 @@
     private:
       RestApi& api_;
       RestApiOutput& output_;
+      RequestOrigin origin_;
+      const char* remoteIp_;
+      const char* username_;
       HttpMethod method_;
-      const HttpHandler::Arguments& headers_;
-      const HttpHandler::Arguments& getArguments_;
-      const std::string& postData_;
+      const IHttpHandler::Arguments& headers_;
+      const IHttpHandler::Arguments& getArguments_;
+      const char* bodyData_;
+      size_t bodySize_;
 
     public:
       HttpHandlerVisitor(RestApi& api,
                          RestApiOutput& output,
+                         RequestOrigin origin,
+                         const char* remoteIp,
+                         const char* username,
                          HttpMethod method,
-                         const HttpHandler::Arguments& headers,
-                         const HttpHandler::Arguments& getArguments,
-                         const std::string& postData) :
+                         const IHttpHandler::Arguments& headers,
+                         const IHttpHandler::Arguments& getArguments,
+                         const char* bodyData,
+                         size_t bodySize) :
         api_(api),
         output_(output),
+        origin_(origin),
+        remoteIp_(remoteIp),
+        username_(username),
         method_(method),
         headers_(headers),
         getArguments_(getArguments),
-        postData_(postData)
+        bodyData_(bodyData),
+        bodySize_(bodySize)
       {
       }
 
       virtual bool Visit(const RestApiHierarchy::Resource& resource,
                          const UriComponents& uri,
-                         const HttpHandler::Arguments& components,
+                         const IHttpHandler::Arguments& components,
                          const UriComponents& trailing)
       {
         if (resource.HasHandler(method_))
@@ -80,28 +92,32 @@
           {
             case HttpMethod_Get:
             {
-              RestApiGetCall call(output_, api_, headers_, components, trailing, uri, getArguments_);
+              RestApiGetCall call(output_, api_, origin_, remoteIp_, username_, 
+                                  headers_, components, trailing, uri, getArguments_);
               resource.Handle(call);
               return true;
             }
 
             case HttpMethod_Post:
             {
-              RestApiPostCall call(output_, api_, headers_, components, trailing, uri, postData_);
+              RestApiPostCall call(output_, api_, origin_, remoteIp_, username_, 
+                                   headers_, components, trailing, uri, bodyData_, bodySize_);
               resource.Handle(call);
               return true;
             }
 
             case HttpMethod_Delete:
             {
-              RestApiDeleteCall call(output_, api_, headers_, components, trailing, uri);
+              RestApiDeleteCall call(output_, api_, origin_, remoteIp_, username_, 
+                                     headers_, components, trailing, uri);
               resource.Handle(call);
               return true;
             }
 
             case HttpMethod_Put:
             {
-              RestApiPutCall call(output_, api_, headers_, components, trailing, uri, postData_);
+              RestApiPutCall call(output_, api_, origin_, remoteIp_, username_, 
+                                  headers_, components, trailing, uri, bodyData_, bodySize_);
               resource.Handle(call);
               return true;
             }
@@ -157,38 +173,48 @@
 
 
   bool RestApi::Handle(HttpOutput& output,
+                       RequestOrigin origin,
+                       const char* remoteIp,
+                       const char* username,
                        HttpMethod method,
                        const UriComponents& uri,
                        const Arguments& headers,
-                       const Arguments& getArguments,
-                       const std::string& postData)
+                       const GetArguments& getArguments,
+                       const char* bodyData,
+                       size_t bodySize)
   {
-    RestApiOutput wrappedOutput(output);
+    RestApiOutput wrappedOutput(output, method);
 
 #if ORTHANC_PUGIXML_ENABLED == 1
-    // Look if the user wishes XML answers instead of JSON
-    // http://www.w3.org/Protocols/HTTP/HTRQ_Headers.html#z3
-    Arguments::const_iterator it = headers.find("accept");
-    if (it != headers.end())
     {
-      std::vector<std::string> accepted;
-      Toolbox::TokenizeString(accepted, it->second, ';');
-      for (size_t i = 0; i < accepted.size(); i++)
+      // Look if the client wishes XML answers instead of JSON
+      // http://www.w3.org/Protocols/HTTP/HTRQ_Headers.html#z3
+      Arguments::const_iterator it = headers.find("accept");
+      if (it != headers.end())
       {
-        if (accepted[i] == "application/xml")
+        std::vector<std::string> accepted;
+        Toolbox::TokenizeString(accepted, it->second, ';');
+        for (size_t i = 0; i < accepted.size(); i++)
         {
-          wrappedOutput.SetConvertJsonToXml(true);
-        }
+          if (accepted[i] == "application/xml")
+          {
+            wrappedOutput.SetConvertJsonToXml(true);
+          }
 
-        if (accepted[i] == "application/json")
-        {
-          wrappedOutput.SetConvertJsonToXml(false);
+          if (accepted[i] == "application/json")
+          {
+            wrappedOutput.SetConvertJsonToXml(false);
+          }
         }
       }
     }
 #endif
 
-    HttpHandlerVisitor visitor(*this, wrappedOutput, method, headers, getArguments, postData);
+    Arguments compiled;
+    HttpToolbox::CompileGetArguments(compiled, getArguments);
+
+    HttpHandlerVisitor visitor(*this, wrappedOutput, origin, remoteIp, username, 
+                               method, headers, compiled, bodyData, bodySize);
 
     if (root_.LookupResource(uri, visitor))
     {
--- a/Core/RestApi/RestApi.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/RestApi/RestApi.h	Wed Sep 30 13:23:31 2015 +0200
@@ -38,7 +38,7 @@
 
 namespace Orthanc
 {
-  class RestApi : public HttpHandler
+  class RestApi : public IHttpHandler
   {
   private:
     RestApiHierarchy root_;
@@ -47,11 +47,15 @@
     static void AutoListChildren(RestApiGetCall& call);
 
     virtual bool Handle(HttpOutput& output,
+                        RequestOrigin origin,
+                        const char* remoteIp,
+                        const char* username,
                         HttpMethod method,
                         const UriComponents& uri,
                         const Arguments& headers,
-                        const Arguments& getArguments,
-                        const std::string& postData);
+                        const GetArguments& getArguments,
+                        const char* bodyData,
+                        size_t bodySize);
 
     void Register(const std::string& path,
                   RestApiGetCall::Handler handler);
--- a/Core/RestApi/RestApiCall.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/RestApi/RestApiCall.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -30,6 +30,7 @@
  **/
 
 
+#include "../PrecompiledHeaders.h"
 #include "RestApiCall.h"
 
 namespace Orthanc
@@ -41,4 +42,17 @@
     Json::Reader reader;
     return reader.parse(request, result);
   }
+
+
+  std::string RestApiCall::FlattenUri() const
+  {
+    std::string s = "/";
+
+    for (size_t i = 0; i < fullUri_.size(); i++)
+    {
+      s += fullUri_[i] + "/";
+    }
+
+    return s;
+  }
 }
--- a/Core/RestApi/RestApiCall.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/RestApi/RestApiCall.h	Wed Sep 30 13:23:31 2015 +0200
@@ -32,7 +32,8 @@
 
 #pragma once
 
-#include "../HttpServer/HttpHandler.h"
+#include "../HttpServer/IHttpHandler.h"
+#include "../HttpServer/HttpToolbox.h"
 #include "RestApiPath.h"
 #include "RestApiOutput.h"
 
@@ -47,8 +48,11 @@
   private:
     RestApiOutput& output_;
     RestApi& context_;
-    const HttpHandler::Arguments& httpHeaders_;
-    const HttpHandler::Arguments& uriComponents_;
+    RequestOrigin origin_;
+    const char* remoteIp_;
+    const char* username_;
+    const IHttpHandler::Arguments& httpHeaders_;
+    const IHttpHandler::Arguments& uriComponents_;
     const UriComponents& trailing_;
     const UriComponents& fullUri_;
 
@@ -59,12 +63,18 @@
   public:
     RestApiCall(RestApiOutput& output,
                 RestApi& context,
-                const HttpHandler::Arguments& httpHeaders,
-                const HttpHandler::Arguments& uriComponents,
+                RequestOrigin origin,
+                const char* remoteIp,
+                const char* username,
+                const IHttpHandler::Arguments& httpHeaders,
+                const IHttpHandler::Arguments& uriComponents,
                 const UriComponents& trailing,
                 const UriComponents& fullUri) :
       output_(output),
       context_(context),
+      origin_(origin),
+      remoteIp_(remoteIp),
+      username_(username),
       httpHeaders_(httpHeaders),
       uriComponents_(uriComponents),
       trailing_(trailing),
@@ -95,23 +105,40 @@
     std::string GetUriComponent(const std::string& name,
                                 const std::string& defaultValue) const
     {
-      return HttpHandler::GetArgument(uriComponents_, name, defaultValue);
+      return HttpToolbox::GetArgument(uriComponents_, name, defaultValue);
     }
 
     std::string GetHttpHeader(const std::string& name,
                               const std::string& defaultValue) const
     {
-      return HttpHandler::GetArgument(httpHeaders_, name, defaultValue);
+      return HttpToolbox::GetArgument(httpHeaders_, name, defaultValue);
     }
 
-    const HttpHandler::Arguments& GetHttpHeaders() const
+    const IHttpHandler::Arguments& GetHttpHeaders() const
     {
       return httpHeaders_;
     }
 
-    void ParseCookies(HttpHandler::Arguments& result) const
+    void ParseCookies(IHttpHandler::Arguments& result) const
+    {
+      HttpToolbox::ParseCookies(result, httpHeaders_);
+    }
+
+    std::string FlattenUri() const;
+
+    RequestOrigin GetRequestOrigin() const
     {
-      HttpHandler::ParseCookies(result, httpHeaders_);
+      return origin_;
+    }
+
+    const char* GetRemoteIp() const
+    {
+      return remoteIp_;
+    }
+
+    const char* GetUsername() const
+    {
+      return username_;
     }
 
     virtual bool ParseJsonRequest(Json::Value& result) const = 0;
--- a/Core/RestApi/RestApiDeleteCall.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/RestApi/RestApiDeleteCall.h	Wed Sep 30 13:23:31 2015 +0200
@@ -43,11 +43,15 @@
     
     RestApiDeleteCall(RestApiOutput& output,
                       RestApi& context,
-                      const HttpHandler::Arguments& httpHeaders,
-                      const HttpHandler::Arguments& uriComponents,
+                      RequestOrigin origin,
+                      const char* remoteIp,
+                      const char* username,
+                      const IHttpHandler::Arguments& httpHeaders,
+                      const IHttpHandler::Arguments& uriComponents,
                       const UriComponents& trailing,
                       const UriComponents& fullUri) :
-      RestApiCall(output, context, httpHeaders, uriComponents, trailing, fullUri)
+      RestApiCall(output, context, origin, remoteIp, username,
+                  httpHeaders, uriComponents, trailing, fullUri)
     {
     }
 
--- a/Core/RestApi/RestApiGetCall.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/RestApi/RestApiGetCall.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -30,6 +30,7 @@
  **/
 
 
+#include "../PrecompiledHeaders.h"
 #include "RestApiGetCall.h"
 
 namespace Orthanc
@@ -38,7 +39,7 @@
   {
     result.clear();
 
-    for (HttpHandler::Arguments::const_iterator 
+    for (IHttpHandler::Arguments::const_iterator 
            it = getArguments_.begin(); it != getArguments_.end(); ++it)
     {
       result[it->first] = it->second;
--- a/Core/RestApi/RestApiGetCall.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/RestApi/RestApiGetCall.h	Wed Sep 30 13:23:31 2015 +0200
@@ -39,19 +39,23 @@
   class RestApiGetCall : public RestApiCall
   {
   private:
-    const HttpHandler::Arguments& getArguments_;
+    const IHttpHandler::Arguments& getArguments_;
 
   public:
     typedef void (*Handler) (RestApiGetCall& call);   
 
     RestApiGetCall(RestApiOutput& output,
                    RestApi& context,
-                   const HttpHandler::Arguments& httpHeaders,
-                   const HttpHandler::Arguments& uriComponents,
+                   RequestOrigin origin,
+                   const char* remoteIp,
+                   const char* username,
+                   const IHttpHandler::Arguments& httpHeaders,
+                   const IHttpHandler::Arguments& uriComponents,
                    const UriComponents& trailing,
                    const UriComponents& fullUri,
-                   const HttpHandler::Arguments& getArguments) :
-      RestApiCall(output, context, httpHeaders, uriComponents, trailing, fullUri),
+                   const IHttpHandler::Arguments& getArguments) :
+      RestApiCall(output, context, origin, remoteIp, username, 
+                  httpHeaders, uriComponents, trailing, fullUri),
       getArguments_(getArguments)
     {
     }
@@ -59,7 +63,7 @@
     std::string GetArgument(const std::string& name,
                             const std::string& defaultValue) const
     {
-      return HttpHandler::GetArgument(getArguments_, name, defaultValue);
+      return HttpToolbox::GetArgument(getArguments_, name, defaultValue);
     }
 
     bool HasArgument(const std::string& name) const
--- a/Core/RestApi/RestApiHierarchy.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/RestApi/RestApiHierarchy.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -30,6 +30,7 @@
  **/
 
 
+#include "../PrecompiledHeaders.h"
 #include "RestApiHierarchy.h"
 
 #include "../OrthancException.h"
@@ -199,7 +200,7 @@
   }
 
 
-  bool RestApiHierarchy::LookupResource(HttpHandler::Arguments& components,
+  bool RestApiHierarchy::LookupResource(IHttpHandler::Arguments& components,
                                        const UriComponents& uri,
                                        IVisitor& visitor,
                                        size_t level)
@@ -240,7 +241,7 @@
       for (child = wildcardChildren_.begin();
            child != wildcardChildren_.end(); ++child)
       {
-        HttpHandler::Arguments subComponents = components;
+        IHttpHandler::Arguments subComponents = components;
         subComponents[child->first] = uri[level];
 
         if (child->second->LookupResource(subComponents, uri, visitor, level + 1))
@@ -404,7 +405,7 @@
   bool RestApiHierarchy::LookupResource(const UriComponents& uri,
                                         IVisitor& visitor)
   {
-    HttpHandler::Arguments components;
+    IHttpHandler::Arguments components;
     return LookupResource(components, uri, visitor, 0);
   }    
 
@@ -426,7 +427,7 @@
 
       virtual bool Visit(const RestApiHierarchy::Resource& resource,
                          const UriComponents& uri,
-                         const HttpHandler::Arguments& components,
+                         const IHttpHandler::Arguments& components,
                          const UriComponents& trailing)
       {
         if (trailing.size() == 0)  // Ignore universal handlers
@@ -460,14 +461,15 @@
   void RestApiHierarchy::GetAcceptedMethods(std::set<HttpMethod>& methods,
                                             const UriComponents& uri)
   {
-    HttpHandler::Arguments components;
+    IHttpHandler::Arguments components;
     AcceptedMethodsVisitor visitor(methods);
-    LookupResource(components, uri, visitor, 0);
-
-    Json::Value d;
-    if (GetDirectory(d, uri))
+    if (LookupResource(components, uri, visitor, 0))
     {
-      methods.insert(HttpMethod_Get);
+      Json::Value d;
+      if (GetDirectory(d, uri))
+      {
+        methods.insert(HttpMethod_Get);
+      }
     }
   }
 }
--- a/Core/RestApi/RestApiHierarchy.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/RestApi/RestApiHierarchy.h	Wed Sep 30 13:23:31 2015 +0200
@@ -98,7 +98,7 @@
 
       virtual bool Visit(const Resource& resource,
                          const UriComponents& uri,
-                         const HttpHandler::Arguments& components,
+                         const IHttpHandler::Arguments& components,
                          const UriComponents& trailing) = 0;
     };
 
@@ -123,7 +123,7 @@
 
     bool CanGenerateDirectory() const;
 
-    bool LookupResource(HttpHandler::Arguments& components,
+    bool LookupResource(IHttpHandler::Arguments& components,
                         const UriComponents& uri,
                         IVisitor& visitor,
                         size_t level);
--- a/Core/RestApi/RestApiOutput.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/RestApi/RestApiOutput.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,15 +33,18 @@
 #include "../PrecompiledHeaders.h"
 #include "RestApiOutput.h"
 
+#include "../Logging.h"
+#include "../OrthancException.h"
+
 #include <boost/lexical_cast.hpp>
-#include <glog/logging.h>
 
-#include "../OrthancException.h"
 
 namespace Orthanc
 {
-  RestApiOutput::RestApiOutput(HttpOutput& output) : 
+  RestApiOutput::RestApiOutput(HttpOutput& output,
+                               HttpMethod method) : 
     output_(output),
+    method_(method),
     convertJsonToXml_(false)
   {
     alreadySent_ = false;
@@ -55,7 +58,14 @@
   {
     if (!alreadySent_)
     {
-      output_.SendStatus(HttpStatus_404_NotFound);
+      if (method_ == HttpMethod_Post)
+      {
+        output_.SendStatus(HttpStatus_400_BadRequest);
+      }
+      else
+      {
+        output_.SendStatus(HttpStatus_404_NotFound);
+      }
     }
   }
   
@@ -67,10 +77,11 @@
     }
   }
 
-  void RestApiOutput::AnswerFile(HttpFileSender& sender)
+
+  void RestApiOutput::AnswerStream(IHttpStreamAnswer& stream)
   {
     CheckStatus();
-    sender.Send(output_);
+    output_.Answer(stream);
     alreadySent_ = true;
   }
 
@@ -84,7 +95,7 @@
       std::string s;
       Toolbox::JsonToXml(s, value);
       output_.SetContentType("application/xml");
-      output_.SendBody(s);
+      output_.Answer(s);
 #else
       LOG(ERROR) << "Orthanc was compiled without XML support";
       throw OrthancException(ErrorCode_InternalError);
@@ -94,7 +105,7 @@
     {
       Json::StyledWriter writer;
       output_.SetContentType("application/json");
-      output_.SendBody(writer.write(value));
+      output_.Answer(writer.write(value));
     }
 
     alreadySent_ = true;
@@ -103,10 +114,8 @@
   void RestApiOutput::AnswerBuffer(const std::string& buffer,
                                    const std::string& contentType)
   {
-    CheckStatus();
-    output_.SetContentType(contentType.c_str());
-    output_.SendBody(buffer);
-    alreadySent_ = true;
+    AnswerBuffer(buffer.size() == 0 ? NULL : buffer.c_str(),
+                 buffer.size(), contentType);
   }
 
   void RestApiOutput::AnswerBuffer(const void* buffer,
@@ -115,7 +124,7 @@
   {
     CheckStatus();
     output_.SetContentType(contentType.c_str());
-    output_.SendBody(buffer, length);
+    output_.Answer(buffer, length);
     alreadySent_ = true;
   }
 
@@ -126,21 +135,34 @@
     alreadySent_ = true;
   }
 
-  void RestApiOutput::SignalError(HttpStatus status)
+  void RestApiOutput::SignalErrorInternal(HttpStatus status,
+					  const char* message,
+					  size_t messageSize)
   {
     if (status != HttpStatus_400_BadRequest &&
         status != HttpStatus_403_Forbidden &&
         status != HttpStatus_500_InternalServerError &&
         status != HttpStatus_415_UnsupportedMediaType)
     {
-      throw OrthancException("This HTTP status is not allowed in a REST API");
+      throw OrthancException(ErrorCode_BadHttpStatusInRest);
     }
 
     CheckStatus();
-    output_.SendStatus(status);
+    output_.SendStatus(status, message, messageSize);
     alreadySent_ = true;    
   }
 
+  void RestApiOutput::SignalError(HttpStatus status)
+  {
+    SignalErrorInternal(status, NULL, 0);
+  }
+
+  void RestApiOutput::SignalError(HttpStatus status,
+				  const std::string& message)
+  {
+    SignalErrorInternal(status, message.c_str(), message.size());
+  }
+
   void RestApiOutput::SetCookie(const std::string& name,
                                 const std::string& value,
                                 unsigned int maxAge)
--- a/Core/RestApi/RestApiOutput.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/RestApi/RestApiOutput.h	Wed Sep 30 13:23:31 2015 +0200
@@ -42,27 +42,23 @@
   class RestApiOutput
   {
   private:
-    HttpOutput& output_;
-    bool alreadySent_;
-    bool convertJsonToXml_;
+    HttpOutput&  output_;
+    HttpMethod   method_;
+    bool         alreadySent_;
+    bool         convertJsonToXml_;
 
     void CheckStatus();
 
+    void SignalErrorInternal(HttpStatus status,
+			     const char* message,
+			     size_t messageSize);
+
   public:
-    RestApiOutput(HttpOutput& output);
+    RestApiOutput(HttpOutput& output,
+                  HttpMethod method);
 
     ~RestApiOutput();
 
-    HttpOutput& GetLowLevelOutput()
-    {
-      return output_;
-    }
-
-    void MarkLowLevelOutputDone()
-    {
-      alreadySent_ = true;
-    }
-
     void SetConvertJsonToXml(bool convert)
     {
       convertJsonToXml_ = convert;
@@ -73,7 +69,7 @@
       return convertJsonToXml_;
     }
 
-    void AnswerFile(HttpFileSender& sender);
+    void AnswerStream(IHttpStreamAnswer& stream);
 
     void AnswerJson(const Json::Value& value);
 
@@ -86,6 +82,9 @@
 
     void SignalError(HttpStatus status);
 
+    void SignalError(HttpStatus status,
+		     const std::string& message);
+
     void Redirect(const std::string& path);
 
     void SetCookie(const std::string& name,
--- a/Core/RestApi/RestApiPath.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/RestApi/RestApiPath.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -78,7 +78,7 @@
     }
   }
 
-  bool RestApiPath::Match(HttpHandler::Arguments& components,
+  bool RestApiPath::Match(IHttpHandler::Arguments& components,
                           UriComponents& trailing,
                           const std::string& uriRaw) const
   {
@@ -87,7 +87,7 @@
     return Match(components, trailing, uri);
   }
 
-  bool RestApiPath::Match(HttpHandler::Arguments& components,
+  bool RestApiPath::Match(IHttpHandler::Arguments& components,
                           UriComponents& trailing,
                           const UriComponents& uri) const
   {
@@ -135,7 +135,7 @@
 
   bool RestApiPath::Match(const UriComponents& uri) const
   {
-    HttpHandler::Arguments components;
+    IHttpHandler::Arguments components;
     UriComponents trailing;
     return Match(components, trailing, uri);
   }
--- a/Core/RestApi/RestApiPath.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/RestApi/RestApiPath.h	Wed Sep 30 13:23:31 2015 +0200
@@ -33,7 +33,7 @@
 #pragma once
 
 #include "../Toolbox.h"
-#include "../HttpServer/HttpHandler.h"
+#include "../HttpServer/IHttpHandler.h"
 
 #include <map>
 
@@ -50,11 +50,11 @@
     RestApiPath(const std::string& uri);
 
     // This version is slower
-    bool Match(HttpHandler::Arguments& components,
+    bool Match(IHttpHandler::Arguments& components,
                UriComponents& trailing,
                const std::string& uriRaw) const;
 
-    bool Match(HttpHandler::Arguments& components,
+    bool Match(IHttpHandler::Arguments& components,
                UriComponents& trailing,
                const UriComponents& uri) const;
 
--- a/Core/RestApi/RestApiPostCall.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/RestApi/RestApiPostCall.h	Wed Sep 30 13:23:31 2015 +0200
@@ -39,31 +39,48 @@
   class RestApiPostCall : public RestApiCall
   {
   private:
-    const std::string& data_;
+    const char* bodyData_;
+    size_t bodySize_;
 
   public:
     typedef void (*Handler) (RestApiPostCall& call);
     
     RestApiPostCall(RestApiOutput& output,
                     RestApi& context,
-                    const HttpHandler::Arguments& httpHeaders,
-                    const HttpHandler::Arguments& uriComponents,
+                    RequestOrigin origin,
+                    const char* remoteIp,
+                    const char* username,
+                    const IHttpHandler::Arguments& httpHeaders,
+                    const IHttpHandler::Arguments& uriComponents,
                     const UriComponents& trailing,
                     const UriComponents& fullUri,
-                    const std::string& data) :
-      RestApiCall(output, context, httpHeaders, uriComponents, trailing, fullUri),
-      data_(data)
+                    const char* bodyData,
+                    size_t bodySize) :
+      RestApiCall(output, context, origin, remoteIp, username, 
+                  httpHeaders, uriComponents, trailing, fullUri),
+      bodyData_(bodyData),
+      bodySize_(bodySize)
     {
     }
 
-    const std::string& GetPostBody() const
+    const char* GetBodyData() const
+    {
+      return bodyData_;
+    }
+
+    size_t GetBodySize() const
     {
-      return data_;
+      return bodySize_;
+    }
+
+    void BodyToString(std::string& result) const
+    {
+      result.assign(bodyData_, bodySize_);
     }
 
     virtual bool ParseJsonRequest(Json::Value& result) const
     {
-      return ParseJsonRequestInternal(result, GetPostBody().c_str());
+      return ParseJsonRequestInternal(result, bodyData_);
     }      
   };
 }
--- a/Core/RestApi/RestApiPutCall.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/RestApi/RestApiPutCall.h	Wed Sep 30 13:23:31 2015 +0200
@@ -39,31 +39,48 @@
   class RestApiPutCall : public RestApiCall
   {
   private:
-    const std::string& data_;
+    const char* bodyData_;
+    size_t bodySize_;
 
   public:
     typedef void (*Handler) (RestApiPutCall& call);
     
     RestApiPutCall(RestApiOutput& output,
                    RestApi& context,
-                   const HttpHandler::Arguments& httpHeaders,
-                   const HttpHandler::Arguments& uriComponents,
+                   RequestOrigin origin,
+                   const char* remoteIp,
+                   const char* username,
+                   const IHttpHandler::Arguments& httpHeaders,
+                   const IHttpHandler::Arguments& uriComponents,
                    const UriComponents& trailing,
                    const UriComponents& fullUri,
-                   const std::string& data) :
-      RestApiCall(output, context, httpHeaders, uriComponents, trailing, fullUri),
-      data_(data)
+                   const char* bodyData,
+                   size_t bodySize) :
+      RestApiCall(output, context, origin, remoteIp, username,
+                  httpHeaders, uriComponents, trailing, fullUri),
+      bodyData_(bodyData),
+      bodySize_(bodySize)
     {
     }
 
-    const std::string& GetPutBody() const
+    const char* GetBodyData() const
+    {
+      return bodyData_;
+    }
+
+    size_t GetBodySize() const
     {
-      return data_;
+      return bodySize_;
+    }
+
+    void BodyToString(std::string& result) const
+    {
+      result.assign(bodyData_, bodySize_);
     }
 
     virtual bool ParseJsonRequest(Json::Value& result) const
     {
-      return ParseJsonRequestInternal(result, GetPutBody().c_str());
+      return ParseJsonRequestInternal(result, bodyData_);
     }      
   };
 }
--- a/Core/SQLite/Connection.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/SQLite/Connection.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -44,13 +44,14 @@
 
 #include <memory>
 #include <cassert>
-#include <sqlite3.h>
 #include <string.h>
 
 #if ORTHANC_SQLITE_STANDALONE != 1
-#include <glog/logging.h>
+#include "../Logging.h"
 #endif
 
+#include "sqlite3.h"
+
 
 namespace Orthanc
 {
@@ -74,7 +75,7 @@
     {
       if (!db_)
       {
-        throw OrthancSQLiteException("SQLite: The database is not opened");
+        throw OrthancSQLiteException(ErrorCode_SQLiteNotOpened);
       }
     }
 
@@ -82,7 +83,7 @@
     {
       if (db_) 
       {
-        throw OrthancSQLiteException("SQLite: Connection is already open");
+        throw OrthancSQLiteException(ErrorCode_SQLiteAlreadyOpened);
       }
 
       int err = sqlite3_open(path.c_str(), &db_);
@@ -90,7 +91,7 @@
       {
         Close();
         db_ = NULL;
-        throw OrthancSQLiteException("SQLite: Unable to open the database");
+        throw OrthancSQLiteException(ErrorCode_SQLiteCannotOpen);
       }
 
       // Execute PRAGMAs at this point
@@ -136,7 +137,7 @@
       {
         if (i->second->GetReferenceCount() >= 1)
         {
-          throw OrthancSQLiteException("SQLite: This cached statement is already being referred to");
+          throw OrthancSQLiteException(ErrorCode_SQLiteStatementAlreadyUsed);
         }
 
         return *i->second;
@@ -161,7 +162,11 @@
       int error = sqlite3_exec(db_, sql, NULL, NULL, NULL);
       if (error == SQLITE_ERROR)
       {
-        throw OrthancSQLiteException("SQLite Execute error: " + std::string(sqlite3_errmsg(db_)));
+#if ORTHANC_SQLITE_STANDALONE != 1
+        LOG(ERROR) << "SQLite execute error: " << sqlite3_errmsg(db_);
+#endif
+
+        throw OrthancSQLiteException(ErrorCode_SQLiteExecute);
       }
       else
       {
@@ -282,7 +287,7 @@
     {
       if (!transactionNesting_)
       {
-        throw OrthancSQLiteException("Rolling back a nonexistent transaction");
+        throw OrthancSQLiteException(ErrorCode_SQLiteRollbackWithoutTransaction);
       }
 
       transactionNesting_--;
@@ -301,7 +306,7 @@
     {
       if (!transactionNesting_) 
       {
-        throw OrthancSQLiteException("Committing a nonexistent transaction");
+        throw OrthancSQLiteException(ErrorCode_SQLiteCommitWithoutTransaction);
       }
       transactionNesting_--;
 
@@ -369,7 +374,7 @@
       if (err != SQLITE_OK)
       {
         delete func;
-        throw OrthancSQLiteException("SQLite: Unable to register a function");
+        throw OrthancSQLiteException(ErrorCode_SQLiteRegisterFunction);
       }
 
       return func;
@@ -386,7 +391,7 @@
 
       if (err != SQLITE_OK)
       {
-        throw OrthancSQLiteException("SQLite: Unable to flush the database");
+        throw OrthancSQLiteException(ErrorCode_SQLiteFlush);
       }
     }
   }
--- a/Core/SQLite/FunctionContext.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/SQLite/FunctionContext.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -39,7 +39,9 @@
 #include "FunctionContext.h"
 #include "OrthancSQLiteException.h"
 
-#include <sqlite3.h>
+#include <string>
+
+#include "sqlite3.h"
 
 namespace Orthanc
 {
@@ -62,7 +64,7 @@
     {
       if (index >= argc_)
       {
-        throw OrthancSQLiteException("Parameter out of range");
+        throw OrthancSQLiteException(ErrorCode_ParameterOutOfRange);
       }
     }
 
--- a/Core/SQLite/OrthancSQLiteException.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/SQLite/OrthancSQLiteException.h	Wed Sep 30 13:23:31 2015 +0200
@@ -45,17 +45,99 @@
 {
   namespace SQLite
   {
+    // Auto-generated by "Resources/GenerateErrorCodes.py"
+    enum ErrorCode
+    {
+      ErrorCode_ParameterOutOfRange,
+      ErrorCode_BadParameterType,
+      ErrorCode_SQLiteNotOpened,
+      ErrorCode_SQLiteAlreadyOpened,
+      ErrorCode_SQLiteCannotOpen,
+      ErrorCode_SQLiteStatementAlreadyUsed,
+      ErrorCode_SQLiteExecute,
+      ErrorCode_SQLiteRollbackWithoutTransaction,
+      ErrorCode_SQLiteCommitWithoutTransaction,
+      ErrorCode_SQLiteRegisterFunction,
+      ErrorCode_SQLiteFlush,
+      ErrorCode_SQLiteCannotRun,
+      ErrorCode_SQLiteCannotStep,
+      ErrorCode_SQLiteBindOutOfRange,
+      ErrorCode_SQLitePrepareStatement,
+      ErrorCode_SQLiteTransactionAlreadyStarted,
+      ErrorCode_SQLiteTransactionCommit,
+      ErrorCode_SQLiteTransactionBegin
+    };
+
     class OrthancSQLiteException : public ::std::runtime_error
     {
     public:
-      OrthancSQLiteException(const std::string& what) :
-        ::std::runtime_error(what)
+      OrthancSQLiteException(ErrorCode error) :
+        ::std::runtime_error(EnumerationToString(error))
       {
       }
 
-      OrthancSQLiteException(const char* what) : 
-        ::std::runtime_error(what)
+      // Auto-generated by "Resources/GenerateErrorCodes.py"
+      static const char* EnumerationToString(ErrorCode code)
       {
+        switch (code)
+        {
+          case ErrorCode_ParameterOutOfRange:
+            return "Parameter out of range";
+
+          case ErrorCode_BadParameterType:
+            return "Bad type for a parameter";
+
+          case ErrorCode_SQLiteNotOpened:
+            return "SQLite: The database is not opened";
+
+          case ErrorCode_SQLiteAlreadyOpened:
+            return "SQLite: Connection is already open";
+
+          case ErrorCode_SQLiteCannotOpen:
+            return "SQLite: Unable to open the database";
+
+          case ErrorCode_SQLiteStatementAlreadyUsed:
+            return "SQLite: This cached statement is already being referred to";
+
+          case ErrorCode_SQLiteExecute:
+            return "SQLite: Cannot execute a command";
+
+          case ErrorCode_SQLiteRollbackWithoutTransaction:
+            return "SQLite: Rolling back a nonexistent transaction (have you called Begin()?)";
+
+          case ErrorCode_SQLiteCommitWithoutTransaction:
+            return "SQLite: Committing a nonexistent transaction";
+
+          case ErrorCode_SQLiteRegisterFunction:
+            return "SQLite: Unable to register a function";
+
+          case ErrorCode_SQLiteFlush:
+            return "SQLite: Unable to flush the database";
+
+          case ErrorCode_SQLiteCannotRun:
+            return "SQLite: Cannot run a cached statement";
+
+          case ErrorCode_SQLiteCannotStep:
+            return "SQLite: Cannot step over a cached statement";
+
+          case ErrorCode_SQLiteBindOutOfRange:
+            return "SQLite: Bing a value while out of range (serious error)";
+
+          case ErrorCode_SQLitePrepareStatement:
+            return "SQLite: Cannot prepare a cached statement";
+
+          case ErrorCode_SQLiteTransactionAlreadyStarted:
+            return "SQLite: Beginning the same transaction twice";
+
+          case ErrorCode_SQLiteTransactionCommit:
+            return "SQLite: Failure when committing the transaction";
+
+          case ErrorCode_SQLiteTransactionBegin:
+            return "SQLite: Cannot start a transaction";
+
+          default:
+            return "Unknown error code";
+        }
       }
     };
   }
--- a/Core/SQLite/Statement.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/SQLite/Statement.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -42,15 +42,16 @@
 #include "Statement.h"
 #include "Connection.h"
 
-#include <sqlite3.h>
 #include <string.h>
 #include <stdio.h>
 #include <algorithm>
 
 #if ORTHANC_SQLITE_STANDALONE != 1
-#include <glog/logging.h>
+#include "../Logging.h"
 #endif
 
+#include "sqlite3.h"
+
 #if defined(_MSC_VER)
 #define snprintf _snprintf
 #endif
@@ -59,31 +60,39 @@
 {
   namespace SQLite
   {
-    int Statement::CheckError(int err) const
+    int Statement::CheckError(int err, ErrorCode code) const
     {
       bool succeeded = (err == SQLITE_OK || err == SQLITE_ROW || err == SQLITE_DONE);
       if (!succeeded)
       {
+#if ORTHANC_SQLITE_STANDALONE != 1
         char buffer[128];
         snprintf(buffer, sizeof(buffer) - 1, "SQLite error code %d", err);
-        throw OrthancSQLiteException(buffer);
+        LOG(ERROR) << buffer;
+#endif
+
+        throw OrthancSQLiteException(code);
       }
 
       return err;
     }
 
-    void Statement::CheckOk(int err) const 
+    void Statement::CheckOk(int err, ErrorCode code) const 
     {
       if (err == SQLITE_RANGE)
       {
         // Binding to a non-existent variable is evidence of a serious error.
-        throw OrthancSQLiteException("Bind value out of range");
+        throw OrthancSQLiteException(ErrorCode_SQLiteBindOutOfRange);
       }
       else if (err != SQLITE_OK)
       {
+#if ORTHANC_SQLITE_STANDALONE != 1
         char buffer[128];
         snprintf(buffer, sizeof(buffer) - 1, "SQLite error code %d", err);
-        throw OrthancSQLiteException(buffer);
+        LOG(ERROR) << buffer;
+#endif
+
+        throw OrthancSQLiteException(code);
       }
     }
 
@@ -126,7 +135,7 @@
       VLOG(1) << "SQLite::Statement::Run " << sqlite3_sql(GetStatement());
 #endif
 
-      return CheckError(sqlite3_step(GetStatement())) == SQLITE_DONE;
+      return CheckError(sqlite3_step(GetStatement()), ErrorCode_SQLiteCannotRun) == SQLITE_DONE;
     }
 
     bool Statement::Step()
@@ -135,7 +144,7 @@
       VLOG(1) << "SQLite::Statement::Step " << sqlite3_sql(GetStatement());
 #endif
 
-      return CheckError(sqlite3_step(GetStatement())) == SQLITE_ROW;
+      return CheckError(sqlite3_step(GetStatement()), ErrorCode_SQLiteCannotStep) == SQLITE_ROW;
     }
 
     void Statement::Reset(bool clear_bound_vars) 
@@ -157,7 +166,8 @@
 
     void Statement::BindNull(int col)
     {
-      CheckOk(sqlite3_bind_null(GetStatement(), col + 1));
+      CheckOk(sqlite3_bind_null(GetStatement(), col + 1),
+              ErrorCode_BadParameterType);
     }
 
     void Statement::BindBool(int col, bool val) 
@@ -167,22 +177,26 @@
 
     void Statement::BindInt(int col, int val) 
     {
-      CheckOk(sqlite3_bind_int(GetStatement(), col + 1, val));
+      CheckOk(sqlite3_bind_int(GetStatement(), col + 1, val),
+              ErrorCode_BadParameterType);
     }
 
     void Statement::BindInt64(int col, int64_t val) 
     {
-      CheckOk(sqlite3_bind_int64(GetStatement(), col + 1, val));
+      CheckOk(sqlite3_bind_int64(GetStatement(), col + 1, val),
+              ErrorCode_BadParameterType);
     }
 
     void Statement::BindDouble(int col, double val) 
     {
-      CheckOk(sqlite3_bind_double(GetStatement(), col + 1, val));
+      CheckOk(sqlite3_bind_double(GetStatement(), col + 1, val),
+              ErrorCode_BadParameterType);
     }
 
     void Statement::BindCString(int col, const char* val) 
     {
-      CheckOk(sqlite3_bind_text(GetStatement(), col + 1, val, -1, SQLITE_TRANSIENT));
+      CheckOk(sqlite3_bind_text(GetStatement(), col + 1, val, -1, SQLITE_TRANSIENT),
+              ErrorCode_BadParameterType);
     }
 
     void Statement::BindString(int col, const std::string& val) 
@@ -191,7 +205,8 @@
                                 col + 1,
                                 val.data(),
                                 val.size(),
-                                SQLITE_TRANSIENT));
+                                SQLITE_TRANSIENT),
+              ErrorCode_BadParameterType);
     }
 
     /*void Statement::BindString16(int col, const string16& value) 
@@ -201,7 +216,8 @@
 
     void Statement::BindBlob(int col, const void* val, int val_len) 
     {
-      CheckOk(sqlite3_bind_blob(GetStatement(), col + 1, val, val_len, SQLITE_TRANSIENT));
+      CheckOk(sqlite3_bind_blob(GetStatement(), col + 1, val, val_len, SQLITE_TRANSIENT),
+              ErrorCode_BadParameterType);
     }
 
 
--- a/Core/SQLite/Statement.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/SQLite/Statement.h	Wed Sep 30 13:23:31 2015 +0200
@@ -81,9 +81,11 @@
     private:
       StatementReference  reference_;
 
-      int CheckError(int err) const;
+      int CheckError(int err, 
+                     ErrorCode code) const;
 
-      void CheckOk(int err) const;
+      void CheckOk(int err, 
+                   ErrorCode code) const;
 
       struct sqlite3_stmt* GetStatement() const
       {
--- a/Core/SQLite/StatementReference.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/SQLite/StatementReference.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -43,9 +43,10 @@
 #include "OrthancSQLiteException.h"
 
 #if ORTHANC_SQLITE_STANDALONE != 1
-#include <glog/logging.h>
+#include "../Logging.h"
 #endif
 
+#include <string>
 #include <cassert>
 #include "sqlite3.h"
 
@@ -71,7 +72,7 @@
     {
       if (database == NULL || sql == NULL)
       {
-        throw OrthancSQLiteException("Parameter out of range");
+        throw OrthancSQLiteException(ErrorCode_ParameterOutOfRange);
       }
 
       root_ = NULL;
@@ -80,7 +81,11 @@
       int error = sqlite3_prepare_v2(database, sql, -1, &statement_, NULL);
       if (error != SQLITE_OK)
       {
-        throw OrthancSQLiteException("SQLite: " + std::string(sqlite3_errmsg(database)));
+#if ORTHANC_SQLITE_STANDALONE != 1
+        LOG(ERROR) << "SQLite: " << sqlite3_errmsg(database);
+#endif
+
+        throw OrthancSQLiteException(ErrorCode_SQLitePrepareStatement);
       }
 
       assert(IsRoot());
--- a/Core/SQLite/Transaction.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/SQLite/Transaction.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -64,13 +64,13 @@
     {
       if (isOpen_) 
       {
-        throw OrthancSQLiteException("SQLite: Beginning a transaction twice!");
+        throw OrthancSQLiteException(ErrorCode_SQLiteTransactionAlreadyStarted);
       }
 
       isOpen_ = connection_.BeginTransaction();
       if (!isOpen_)
       {
-        throw OrthancSQLiteException("SQLite: Unable to create a transaction");
+        throw OrthancSQLiteException(ErrorCode_SQLiteTransactionBegin);
       }
     }
 
@@ -78,8 +78,7 @@
     {
       if (!isOpen_) 
       {
-        throw OrthancSQLiteException("SQLite: Attempting to roll back a nonexistent transaction. "
-                                     "Did you remember to call Begin()?");
+        throw OrthancSQLiteException(ErrorCode_SQLiteRollbackWithoutTransaction);
       }
 
       isOpen_ = false;
@@ -91,15 +90,14 @@
     {
       if (!isOpen_) 
       {
-        throw OrthancSQLiteException("SQLite: Attempting to roll back a nonexistent transaction. "
-                                     "Did you remember to call Begin()?");
+        throw OrthancSQLiteException(ErrorCode_SQLiteRollbackWithoutTransaction);
       }
 
       isOpen_ = false;
 
       if (!connection_.CommitTransaction())
       {
-        throw OrthancSQLiteException("SQLite: Failure when committing the transaction");
+        throw OrthancSQLiteException(ErrorCode_SQLiteTransactionCommit);
       }
     }
   }
--- a/Core/Toolbox.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/Toolbox.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -34,21 +34,29 @@
 #include "Toolbox.h"
 
 #include "OrthancException.h"
+#include "Logging.h"
 
+#include <string>
 #include <stdint.h>
 #include <string.h>
 #include <boost/filesystem.hpp>
 #include <boost/filesystem/fstream.hpp>
-#include <boost/date_time/posix_time/posix_time.hpp>
 #include <boost/uuid/sha1.hpp>
+#include <boost/lexical_cast.hpp>
 #include <algorithm>
 #include <ctype.h>
+
+#if BOOST_HAS_DATE_TIME == 1
+#include <boost/date_time/posix_time/posix_time.hpp>
+#endif
+
+#if BOOST_HAS_REGEX == 1
 #include <boost/regex.hpp> 
-#include <glog/logging.h>
+#endif
 
 #if defined(_WIN32)
 #include <windows.h>
-#include <process.h>   // For "_spawnvp()"
+#include <process.h>   // For "_spawnvp()" and "_getpid()"
 #else
 #include <unistd.h>    // For "execvp()"
 #include <sys/wait.h>  // For "waitpid()"
@@ -59,7 +67,7 @@
 #include <limits.h>      /* PATH_MAX */
 #endif
 
-#if defined(__linux) || defined(__FreeBSD_kernel__)
+#if defined(__linux) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
 #include <limits.h>      /* PATH_MAX */
 #include <signal.h>
 #include <unistd.h>
@@ -71,8 +79,15 @@
 
 #include <boost/locale.hpp>
 
+
+#if !defined(ORTHANC_ENABLE_MD5) || ORTHANC_ENABLE_MD5 == 1
 #include "../Resources/ThirdParty/md5/md5.h"
+#endif
+
+
+#if !defined(ORTHANC_ENABLE_BASE64) || ORTHANC_ENABLE_BASE64 == 1
 #include "../Resources/ThirdParty/base64/base64.h"
+#endif
 
 
 #if defined(_MSC_VER) && (_MSC_VER < 1800)
@@ -116,7 +131,7 @@
   {
 #if defined(_WIN32)
     ::Sleep(static_cast<DWORD>(microSeconds / static_cast<uint64_t>(1000)));
-#elif defined(__linux) || defined(__APPLE__) || defined(__FreeBSD_kernel__)
+#elif defined(__linux) || defined(__APPLE__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
     usleep(microSeconds);
 #else
 #error Support your platform here
@@ -193,6 +208,12 @@
   void Toolbox::ReadFile(std::string& content,
                          const std::string& path) 
   {
+    if (!boost::filesystem::is_regular_file(path))
+    {
+      LOG(ERROR) << "The path does not point to a regular file: " << path;
+      throw OrthancException(ErrorCode_RegularFileExpected);
+    }
+
     boost::filesystem::ifstream f;
     f.open(path, std::ifstream::in | std::ifstream::binary);
     if (!f.good())
@@ -215,7 +236,8 @@
   }
 
 
-  void Toolbox::WriteFile(const std::string& content,
+  void Toolbox::WriteFile(const void* content,
+                          size_t size,
                           const std::string& path)
   {
     boost::filesystem::ofstream f;
@@ -225,24 +247,35 @@
       throw OrthancException(ErrorCode_CannotWriteFile);
     }
 
-    if (content.size() != 0)
+    if (size != 0)
     {
-      f.write(content.c_str(), content.size());
+      f.write(reinterpret_cast<const char*>(content), size);
     }
 
     f.close();
   }
 
 
+  void Toolbox::WriteFile(const std::string& content,
+                          const std::string& path)
+  {
+    WriteFile(content.size() > 0 ? content.c_str() : NULL,
+              content.size(), path);
+  }
+
 
   void Toolbox::RemoveFile(const std::string& path)
   {
     if (boost::filesystem::exists(path))
     {
       if (boost::filesystem::is_regular_file(path))
+      {
         boost::filesystem::remove(path);
+      }
       else
-        throw OrthancException("The path is not a regular file: " + path);
+      {
+        throw OrthancException(ErrorCode_RegularFileExpected);
+      }
     }
   }
 
@@ -422,21 +455,26 @@
     {
       return static_cast<uint64_t>(boost::filesystem::file_size(path));
     }
-    catch (boost::filesystem::filesystem_error)
+    catch (boost::filesystem::filesystem_error&)
     {
       throw OrthancException(ErrorCode_InexistentFile);
     }
   }
 
 
+#if !defined(ORTHANC_ENABLE_MD5) || ORTHANC_ENABLE_MD5 == 1
   static char GetHexadecimalCharacter(uint8_t value)
   {
     assert(value < 16);
 
     if (value < 10)
+    {
       return value + '0';
+    }
     else
+    {
       return (value - 10) + 'a';
+    }
   }
 
 
@@ -474,12 +512,14 @@
     result.resize(32);
     for (unsigned int i = 0; i < 16; i++)
     {
-      result[2 * i] = GetHexadecimalCharacter(actualHash[i] / 16);
-      result[2 * i + 1] = GetHexadecimalCharacter(actualHash[i] % 16);
+      result[2 * i] = GetHexadecimalCharacter(static_cast<uint8_t>(actualHash[i] / 16));
+      result[2 * i + 1] = GetHexadecimalCharacter(static_cast<uint8_t>(actualHash[i] % 16));
     }
   }
+#endif
 
 
+#if !defined(ORTHANC_ENABLE_BASE64) || ORTHANC_ENABLE_BASE64 == 1
   void Toolbox::EncodeBase64(std::string& result, 
                              const std::string& data)
   {
@@ -493,6 +533,31 @@
   }
 
 
+#  if BOOST_HAS_REGEX == 1
+  void Toolbox::DecodeDataUriScheme(std::string& mime,
+                                    std::string& content,
+                                    const std::string& source)
+  {
+    boost::regex pattern("data:([^;]+);base64,([a-zA-Z0-9=+/]*)",
+                         boost::regex::icase /* case insensitive search */);
+
+    boost::cmatch what;
+    if (regex_match(source.c_str(), what, pattern))
+    {
+      mime = what[1];
+      DecodeBase64(content, what[2]);
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  }
+#  endif
+
+#endif
+
+
+
 #if defined(_WIN32)
   static std::string GetPathToExecutableInternal()
   {
@@ -503,14 +568,14 @@
     return std::string(&buffer[0]);
   }
 
-#elif defined(__linux) || defined(__FreeBSD_kernel__)
+#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
   static std::string GetPathToExecutableInternal()
   {
     std::vector<char> buffer(PATH_MAX + 1);
     ssize_t bytes = readlink("/proc/self/exe", &buffer[0], buffer.size() - 1);
     if (bytes == 0)
     {
-      throw OrthancException("Unable to get the path to the executable");
+      throw OrthancException(ErrorCode_PathToExecutable);
     }
 
     return std::string(&buffer[0]);
@@ -546,73 +611,89 @@
   }
 
 
-  std::string Toolbox::ConvertToUtf8(const std::string& source,
-                                     const Encoding sourceEncoding)
+  static const char* GetBoostLocaleEncoding(const Encoding sourceEncoding)
   {
-    const char* encoding;
-
-
-    // http://bradleyross.users.sourceforge.net/docs/dicom/doc/src-html/org/dcm4che2/data/SpecificCharacterSet.html
     switch (sourceEncoding)
     {
       case Encoding_Utf8:
-        // Already in UTF-8: No conversion is required
-        return source;
+        return "UTF-8";
 
       case Encoding_Ascii:
-        return ConvertToAscii(source);
+        return "ASCII";
 
       case Encoding_Latin1:
-        encoding = "ISO-8859-1";
+        return "ISO-8859-1";
         break;
 
       case Encoding_Latin2:
-        encoding = "ISO-8859-2";
+        return "ISO-8859-2";
         break;
 
       case Encoding_Latin3:
-        encoding = "ISO-8859-3";
+        return "ISO-8859-3";
         break;
 
       case Encoding_Latin4:
-        encoding = "ISO-8859-4";
+        return "ISO-8859-4";
         break;
 
       case Encoding_Latin5:
-        encoding = "ISO-8859-9";
+        return "ISO-8859-9";
         break;
 
       case Encoding_Cyrillic:
-        encoding = "ISO-8859-5";
+        return "ISO-8859-5";
+        break;
+
+      case Encoding_Windows1251:
+        return "WINDOWS-1251";
         break;
 
       case Encoding_Arabic:
-        encoding = "ISO-8859-6";
+        return "ISO-8859-6";
         break;
 
       case Encoding_Greek:
-        encoding = "ISO-8859-7";
+        return "ISO-8859-7";
         break;
 
       case Encoding_Hebrew:
-        encoding = "ISO-8859-8";
+        return "ISO-8859-8";
         break;
         
       case Encoding_Japanese:
-        encoding = "SHIFT-JIS";
+        return "SHIFT-JIS";
         break;
 
       case Encoding_Chinese:
-        encoding = "GB18030";
+        return "GB18030";
         break;
 
       case Encoding_Thai:
-        encoding = "TIS620.2533-0";
+        return "TIS620.2533-0";
         break;
 
       default:
         throw OrthancException(ErrorCode_NotImplemented);
     }
+  }
+
+
+  std::string Toolbox::ConvertToUtf8(const std::string& source,
+                                     Encoding sourceEncoding)
+  {
+    if (sourceEncoding == Encoding_Utf8)
+    {
+      // Already in UTF-8: No conversion is required
+      return source;
+    }
+
+    if (sourceEncoding == Encoding_Ascii)
+    {
+      return ConvertToAscii(source);
+    }
+
+    const char* encoding = GetBoostLocaleEncoding(sourceEncoding);
 
     try
     {
@@ -626,6 +707,34 @@
   }
 
 
+  std::string Toolbox::ConvertFromUtf8(const std::string& source,
+                                       Encoding targetEncoding)
+  {
+    if (targetEncoding == Encoding_Utf8)
+    {
+      // Already in UTF-8: No conversion is required
+      return source;
+    }
+
+    if (targetEncoding == Encoding_Ascii)
+    {
+      return ConvertToAscii(source);
+    }
+
+    const char* encoding = GetBoostLocaleEncoding(targetEncoding);
+
+    try
+    {
+      return boost::locale::conv::from_utf<char>(source, encoding);
+    }
+    catch (std::runtime_error&)
+    {
+      // Bad input string or bad encoding
+      return ConvertToAscii(source);
+    }
+  }
+
+
   std::string Toolbox::ConvertToAscii(const std::string& source)
   {
     std::string result;
@@ -633,7 +742,7 @@
     result.reserve(source.size() + 1);
     for (size_t i = 0; i < source.size(); i++)
     {
-      if (source[i] < 128 && source[i] >= 0 && !iscntrl(source[i]))
+      if (source[i] <= 127 && source[i] >= 0 && !iscntrl(source[i]))
       {
         result.push_back(source[i]);
       }
@@ -668,9 +777,46 @@
             digest[4]);
   }
 
-  bool Toolbox::IsSHA1(const std::string& str)
+  bool Toolbox::IsSHA1(const char* str,
+                       size_t size)
   {
-    if (str.size() != 44)
+    if (size == 0)
+    {
+      return false;
+    }
+
+    const char* start = str;
+    const char* end = str + size;
+
+    // Trim the beginning of the string
+    while (start < end)
+    {
+      if (*start == '\0' ||
+          isspace(*start))
+      {
+        start++;
+      }
+      else
+      {
+        break;
+      }
+    }
+
+    // Trim the trailing of the string
+    while (start < end)
+    {
+      if (*(end - 1) == '\0' ||
+          isspace(*(end - 1)))
+      {
+        end--;
+      }
+      else
+      {
+        break;
+      }
+    }
+
+    if (end - start != 44)
     {
       return false;
     }
@@ -682,12 +828,12 @@
           i == 26 ||
           i == 35)
       {
-        if (str[i] != '-')
+        if (start[i] != '-')
           return false;
       }
       else
       {
-        if (!isalnum(str[i]))
+        if (!isalnum(start[i]))
           return false;
       }
     }
@@ -695,12 +841,44 @@
     return true;
   }
 
+
+  bool Toolbox::IsSHA1(const std::string& s)
+  {
+    if (s.size() == 0)
+    {
+      return false;
+    }
+    else
+    {
+      return IsSHA1(s.c_str(), s.size());
+    }
+  }
+
+
+#if BOOST_HAS_DATE_TIME == 1
   std::string Toolbox::GetNowIsoString()
   {
     boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
     return boost::posix_time::to_iso_string(now);
   }
 
+  void Toolbox::GetNowDicom(std::string& date,
+                            std::string& time)
+  {
+    boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
+    tm tm = boost::posix_time::to_tm(now);
+
+    char s[32];
+    sprintf(s, "%04d%02d%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
+    date.assign(s);
+
+    // TODO milliseconds
+    sprintf(s, "%02d%02d%02d.%06d", tm.tm_hour, tm.tm_min, tm.tm_sec, 0);
+    time.assign(s);
+  }
+#endif
+
+
   std::string Toolbox::StripSpaces(const std::string& source)
   {
     size_t first = 0;
@@ -801,6 +979,7 @@
   }
 
 
+#if BOOST_HAS_REGEX == 1
   std::string Toolbox::WildcardToRegularExpression(const std::string& source)
   {
     // TODO - Speed up this with a regular expression
@@ -828,6 +1007,7 @@
 
     return result;
   }
+#endif
 
 
 
@@ -856,40 +1036,20 @@
   }
 
 
-  void Toolbox::DecodeDataUriScheme(std::string& mime,
-                                    std::string& content,
-                                    const std::string& source)
-  {
-    boost::regex pattern("data:([^;]+);base64,([a-zA-Z0-9=+/]*)",
-                         boost::regex::icase /* case insensitive search */);
-
-    boost::cmatch what;
-    if (regex_match(source.c_str(), what, pattern))
-    {
-      mime = what[1];
-      content = what[2];
-    }
-    else
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-  }
-
-
-  void Toolbox::CreateDirectory(const std::string& path)
+  void Toolbox::MakeDirectory(const std::string& path)
   {
     if (boost::filesystem::exists(path))
     {
       if (!boost::filesystem::is_directory(path))
       {
-        throw OrthancException("Cannot create the directory over an existing file: " + path);
+        throw OrthancException(ErrorCode_DirectoryOverFile);
       }
     }
     else
     {
       if (!boost::filesystem::create_directories(path))
       {
-        throw OrthancException("Unable to create the directory: " + path);
+        throw OrthancException(ErrorCode_MakeDirectory);
       }
     }
   }
@@ -1050,7 +1210,10 @@
     if (pid == -1)
     {
       // Error in fork()
+#if ORTHANC_ENABLE_LOGGING == 1
       LOG(ERROR) << "Cannot fork a child process";
+#endif
+
       throw OrthancException(ErrorCode_SystemCommand);
     }
     else if (pid == 0)
@@ -1070,7 +1233,10 @@
 
     if (status != 0)
     {
+#if ORTHANC_ENABLE_LOGGING == 1
       LOG(ERROR) << "System command failed with status code " << status;
+#endif
+
       throw OrthancException(ErrorCode_SystemCommand);
     }
   }
@@ -1108,5 +1274,89 @@
 
     return true;
   }
+
+
+  void Toolbox::CopyJsonWithoutComments(Json::Value& target,
+                                        const Json::Value& source)
+  {
+    switch (source.type())
+    {
+      case Json::nullValue:
+        target = Json::nullValue;
+        break;
+
+      case Json::intValue:
+        target = source.asInt64();
+        break;
+
+      case Json::uintValue:
+        target = source.asUInt64();
+        break;
+
+      case Json::realValue:
+        target = source.asDouble();
+        break;
+
+      case Json::stringValue:
+        target = source.asString();
+        break;
+
+      case Json::booleanValue:
+        target = source.asBool();
+        break;
+
+      case Json::arrayValue:
+      {
+        target = Json::arrayValue;
+        for (Json::Value::ArrayIndex i = 0; i < source.size(); i++)
+        {
+          Json::Value& item = target.append(Json::nullValue);
+          CopyJsonWithoutComments(item, source[i]);
+        }
+
+        break;
+      }
+
+      case Json::objectValue:
+      {
+        target = Json::objectValue;
+        Json::Value::Members members = source.getMemberNames();
+        for (Json::Value::ArrayIndex i = 0; i < members.size(); i++)
+        {
+          const std::string item = members[i];
+          CopyJsonWithoutComments(target[item], source[item]);
+        }
+
+        break;
+      }
+
+      default:
+        break;
+    }
+  }
+
+
+  bool Toolbox::StartsWith(const std::string& str,
+                           const std::string& prefix)
+  {
+    if (str.size() < prefix.size())
+    {
+      return false;
+    }
+    else
+    {
+      return str.compare(0, prefix.size(), prefix) == 0;
+    }
+  }
+
+
+  int Toolbox::GetProcessId()
+  {
+#if defined(_WIN32)
+    return static_cast<int>(_getpid());
+#else
+    return static_cast<int>(getpid());
+#endif
+  }
 }
 
--- a/Core/Toolbox.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Core/Toolbox.h	Wed Sep 30 13:23:31 2015 +0200
@@ -69,6 +69,10 @@
     void WriteFile(const std::string& content,
                    const std::string& path);
 
+    void WriteFile(const void* content,
+                   size_t size,
+                   const std::string& path);
+
     void USleep(uint64_t microSeconds);
 
     void RemoveFile(const std::string& path);
@@ -90,53 +94,72 @@
 
     uint64_t GetFileSize(const std::string& path);
 
+#if !defined(ORTHANC_ENABLE_MD5) || ORTHANC_ENABLE_MD5 == 1
     void ComputeMD5(std::string& result,
                     const std::string& data);
 
     void ComputeMD5(std::string& result,
                     const void* data,
                     size_t length);
+#endif
 
     void ComputeSHA1(std::string& result,
                      const std::string& data);
 
-    bool IsSHA1(const std::string& str);
+    bool IsSHA1(const char* str,
+                size_t size);
 
+    bool IsSHA1(const std::string& s);
+
+#if !defined(ORTHANC_ENABLE_BASE64) || ORTHANC_ENABLE_BASE64 == 1
     void DecodeBase64(std::string& result, 
                       const std::string& data);
 
     void EncodeBase64(std::string& result, 
                       const std::string& data);
 
+#  if BOOST_HAS_REGEX == 1
+    void DecodeDataUriScheme(std::string& mime,
+                             std::string& content,
+                             const std::string& source);
+#  endif
+#endif
+
     std::string GetPathToExecutable();
 
     std::string GetDirectoryOfExecutable();
 
     std::string ConvertToUtf8(const std::string& source,
-                              const Encoding sourceEncoding);
+                              Encoding sourceEncoding);
+
+    std::string ConvertFromUtf8(const std::string& source,
+                                Encoding targetEncoding);
 
     std::string ConvertToAscii(const std::string& source);
 
     std::string StripSpaces(const std::string& source);
 
+#if BOOST_HAS_DATE_TIME == 1
     std::string GetNowIsoString();
 
+    void GetNowDicom(std::string& date,
+                     std::string& time);
+#endif
+
     // In-place percent-decoding for URL
     void UrlDecode(std::string& s);
 
     Endianness DetectEndianness();
 
+#if BOOST_HAS_REGEX == 1
     std::string WildcardToRegularExpression(const std::string& s);
+#endif
 
     void TokenizeString(std::vector<std::string>& result,
                         const std::string& source,
                         char separator);
 
-    void DecodeDataUriScheme(std::string& mime,
-                             std::string& content,
-                             const std::string& source);
-
-    void CreateDirectory(const std::string& path);
+    void MakeDirectory(const std::string& path);
 
     bool IsExistingFile(const std::string& path);
 
@@ -151,5 +174,13 @@
                               const std::vector<std::string>& arguments);
 
     bool IsInteger(const std::string& str);
+
+    void CopyJsonWithoutComments(Json::Value& target,
+                                 const Json::Value& source);
+
+    bool StartsWith(const std::string& str,
+                    const std::string& prefix);
+
+    int GetProcessId();
   }
 }
--- a/INSTALL	Wed Feb 11 10:40:08 2015 +0100
+++ b/INSTALL	Wed Sep 30 13:23:31 2015 +0200
@@ -32,12 +32,24 @@
 To build Orthanc, you must:
 
 1) Download the source code (either using Mercurial, or through the
-   released versions). For the examples below, we assume the source
+   official releases). For the examples below, we assume the source
    directory is "~/Orthanc".
 
 2) Create a build directory. For the examples below, we assume the
    build directory is "~/OrthancBuild".
 
+3) Depending on your platform, follow the build instructions below.
+
+
+WARNING 1: If you do not create a fresh "~/OrthancBuild" directory
+after upgrading the source code (i.e. if you reuse the build directory
+that was used to build a different version of Orthanc), the build
+might fail because of changes in the compilation/linking flags. Always
+prefer to force a re-build in a new directory.
+
+WARNING 2: If cmake complains about not being able to uncompress
+third-party dependencies, delete the "~/Orthanc/ThirdPartyDownloads/"
+folder, then restart cmake.
 
 
 Native Linux Compilation
--- a/LinuxCompilation.txt	Wed Feb 11 10:40:08 2015 +0100
+++ b/LinuxCompilation.txt	Wed Sep 30 13:23:31 2015 +0200
@@ -74,50 +74,12 @@
 Orthanc users.
 
 
-SUPPORTED - Debian Squeeze (6.x)
---------------------------------
-
-# sudo apt-get install build-essential unzip cmake mercurial \
-       	       	       uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \
-       	       	       libgoogle-glog-dev libpng-dev libgtest-dev \
-       	       	       libsqlite3-dev libssl-dev zlib1g-dev
-
-# cmake -DALLOW_DOWNLOADS=ON \
-  	-DUSE_SYSTEM_BOOST=OFF \
-	-DUSE_SYSTEM_DCMTK=OFF \
-	-DUSE_SYSTEM_MONGOOSE=OFF \
-	-DUSE_SYSTEM_JSONCPP=OFF \
-	-DUSE_SYSTEM_PUGIXML=OFF \
-        -DENABLE_JPEG=OFF \
-        -DENABLE_JPEG_LOSSLESS=OFF \
-	~/Orthanc 
-
-
-SUPPORTED - Debian Wheezy (7.x)
--------------------------------
-
-# sudo apt-get install build-essential unzip cmake mercurial \
-       	       	       uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \
-       	       	       libgtest-dev libpng-dev libsqlite3-dev \
-       	       	       libssl-dev zlib1g-dev libdcmtk2-dev \
-       	       	       libboost-all-dev libwrap0-dev libjsoncpp-dev
-
-# cmake -DALLOW_DOWNLOADS=ON \
-        -DUSE_SYSTEM_GOOGLE_LOG=OFF \
-	-DUSE_SYSTEM_MONGOOSE=OFF \
-        -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \
-	-DUSE_SYSTEM_PUGIXML=OFF \
-        -DENABLE_JPEG=OFF \
-        -DENABLE_JPEG_LOSSLESS=OFF \
-	~/Orthanc
-
-
 SUPPORTED - Debian Jessie/Sid
 -----------------------------
 
 # sudo apt-get install build-essential unzip cmake mercurial \
        	       	       uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \
-       	       	       libgoogle-glog-dev libgtest-dev libpng-dev \
+       	       	       libgoogle-glog-dev libgtest-dev libpng-dev libjpeg-dev \
        	       	       libsqlite3-dev libssl-dev zlib1g-dev libdcmtk2-dev \
                        libboost-all-dev libwrap0-dev libjsoncpp-dev libpugixml-dev
 
@@ -131,15 +93,16 @@
 http://anonscm.debian.org/viewvc/debian-med/trunk/packages/orthanc/trunk/debian/
 
 
-SUPPORTED - Ubuntu 12.04 LTS
-----------------------------
+SUPPORTED - Ubuntu 12.04.5 LTS
+------------------------------
 
 # sudo apt-get install build-essential unzip cmake mercurial \
        	       	       uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \
-       	       	       libgtest-dev libpng-dev libsqlite3-dev libssl-dev \
-		       zlib1g-dev libdcmtk2-dev libboost-all-dev libwrap0-dev
+       	       	       libgtest-dev libpng-dev libsqlite3-dev libssl-dev libjpeg-dev \
+		       zlib1g-dev libdcmtk2-dev libboost1.48-all-dev libwrap0-dev \
+                       libcharls-dev
 
-# cmake "-DDCMTK_LIBRARIES=wrap;oflog" \
+# cmake "-DDCMTK_LIBRARIES=boost_locale;CharLS;dcmjpls;wrap;oflog" \
         -DALLOW_DOWNLOADS=ON \
 	-DUSE_SYSTEM_MONGOOSE=OFF \
 	-DUSE_SYSTEM_JSONCPP=OFF \
@@ -149,67 +112,36 @@
 	~/Orthanc
 
 
-SUPPORTED - Ubuntu 12.10
-------------------------
+
+SUPPORTED - Ubuntu 14.04 LTS
+----------------------------
 
 # sudo apt-get install build-essential unzip cmake mercurial \
        	       	       uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \
-       	       	       libgoogle-glog-dev libgtest-dev libpng-dev \
-       	       	       libsqlite3-dev libssl-dev zlib1g-dev \
-       	       	       libdcmtk2-dev libboost-all-dev libwrap0-dev libcharls-dev
-
-With JPEG:
+       	       	       libgtest-dev libpng-dev libsqlite3-dev libssl-dev libjpeg-dev \
+		       zlib1g-dev libdcmtk2-dev libboost-all-dev libwrap0-dev \
+                       libcharls-dev libjsoncpp-dev libpugixml-dev
 
-# cmake "-DDCMTK_LIBRARIES=CharLS;dcmjpls;wrap;oflog" \
-        -DALLOW_DOWNLOADS=ON \
-	-DUSE_SYSTEM_MONGOOSE=OFF \
-	-DUSE_SYSTEM_JSONCPP=OFF \
-        -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \
-	-DUSE_SYSTEM_PUGIXML=OFF \
-	~/Orthanc
-
-
-Without JPEG:
-
-# cmake "-DDCMTK_LIBRARIES=wrap;oflog" \
-        -DALLOW_DOWNLOADS=ON \
-	-DUSE_SYSTEM_MONGOOSE=OFF \
-	-DUSE_SYSTEM_JSONCPP=OFF \
+# cmake -DALLOW_DOWNLOADS=ON \
         -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \
-	-DUSE_SYSTEM_PUGIXML=OFF \
-        -DENABLE_JPEG=OFF \
-        -DENABLE_JPEG_LOSSLESS=OFF \
-	~/Orthanc
-
-
-SUPPORTED - Ubuntu 13.10
-------------------------
-
-# sudo apt-get install build-essential unzip cmake mercurial \
-       	       	       uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \
-       	       	       libgoogle-glog-dev libgtest-dev libpng-dev \
-       	       	       libsqlite3-dev libssl-dev zlib1g-dev \
-       	       	       libdcmtk2-dev libboost-all-dev libwrap0-dev libjsoncpp-dev
-
-# cmake "-DDCMTK_LIBRARIES=wrap;oflog" \
-        -DALLOW_DOWNLOADS=ON \
-	-DUSE_SYSTEM_MONGOOSE=OFF \
-        -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \
-	-DUSE_SYSTEM_PUGIXML=OFF \
-        -DENABLE_JPEG=OFF \
-        -DENABLE_JPEG_LOSSLESS=OFF \
-	~/Orthanc
+        -DUSE_SYSTEM_MONGOOSE=OFF \
+        -DDCMTK_LIBRARIES=dcmjpls \
+        ~/Orthanc
 
 
 
-SUPPORTED - Fedora 19/20
+SUPPORTED - Fedora 20-22
 ------------------------
 
-# sudo yum install make automake gcc gcc-c++ python cmake \
+# sudo yum install unzip make automake gcc gcc-c++ python cmake \
                    boost-devel curl-devel dcmtk-devel glog-devel \
-                   gtest-devel libpng-devel libsqlite3x-devel libuuid-devel \
+                   gtest-devel libpng-devel libsqlite3x-devel libuuid-devel jpeg-devel \
                    mongoose-devel openssl-devel jsoncpp-devel lua-devel pugixml-devel
 
+You will also have to install "gflags-devel" on Fedora 21&22:
+
+# sudo yum install gflags-devel
+
 # cmake  "-DDCMTK_LIBRARIES=CharLS" \
          -DSYSTEM_MONGOOSE_USE_CALLBACKS=OFF \
          ~/Orthanc
@@ -219,6 +151,37 @@
 
 
 
+SUPPORTED - FreeBSD 10.1
+------------------------
+
+# pkg install jsoncpp pugixml lua51 curl googletest dcmtk cmake jpeg \
+              e2fsprogs-libuuid glog boost-libs sqlite3 python libiconv
+
+# cmake -DALLOW_DOWNLOADS=ON \
+        -DUSE_SYSTEM_MONGOOSE=OFF \
+        -DDCMTK_LIBRARIES="dcmdsig;charls;dcmjpls" \
+	~/Orthanc
+
+
+
+SUPPORTED - CentOS 6
+--------------------
+
+# yum install unzip make automake gcc gcc-c++ python cmake curl-devel \
+              libpng-devel sqlite-devel libuuid-devel openssl-devel \
+              lua-devel mercurial patch tar
+
+# cmake -DALLOW_DOWNLOADS=ON \
+        -DUSE_SYSTEM_JSONCPP=OFF \
+        -DUSE_SYSTEM_MONGOOSE=OFF \
+        -DUSE_SYSTEM_PUGIXML=OFF \
+        -DUSE_SYSTEM_SQLITE=OFF \
+        -DUSE_SYSTEM_BOOST=OFF \
+        -DUSE_SYSTEM_DCMTK=OFF \
+        -DUSE_SYSTEM_GOOGLE_TEST=OFF \
+        -DUSE_SYSTEM_LIBJPEG=OFF \
+        ~/Orthanc
+
 
 
 Other Linux distributions?
--- a/NEWS	Wed Feb 11 10:40:08 2015 +0100
+++ b/NEWS	Wed Sep 30 13:23:31 2015 +0200
@@ -1,6 +1,160 @@
 Pending changes in the mainline
 ===============================
 
+* Add ".dcm" suffix to files in ZIP archives (cf. URI ".../archive")
+* "/tools/create-dicom": Support of binary tags encoded using data URI scheme
+
+Plugins
+-------
+
+* New function "OrthancPluginRegisterErrorCode()" to declare custom error codes
+* New function "OrthancPluginRegisterDictionaryTag()" to declare DICOM tags
+
+Lua
+---
+
+* Optional argument "keepStrings" in "DumpJson()"
+
+Maintenance
+-----------
+
+* "/system" URI gives information about the plugins used for storage area and DB back-end
+* Plugin callbacks should now return explicit "OrthancPluginErrorCode" instead of integers
+* "/tools/create-dicom" can create tags with unknown VR
+
+
+Version 0.9.4 (2015/09/16)
+==========================
+
+* Preview of PDF files encapsulated in DICOM from Orthanc Explorer
+* Creation of DICOM files with encapsulated PDF through "/tools/create-dicom"
+* "limit" and "since" arguments while retrieving DICOM resources in the REST API
+* Support of "deflate" and "gzip" content-types in HTTP requests
+* Options to validate peers against CA certificates in HTTPS requests
+* New configuration option: "HttpTimeout" to set the default timeout for HTTP requests
+
+Lua
+---
+
+* More information about the origin request in the "OnStoredInstance()" and
+  "ReceivedInstanceFilter()" callbacks. WARNING: This can result in
+  incompatibilities wrt. previous versions of Orthanc.
+* New function "GetOrthancConfiguration()" to get the Orthanc configuration
+
+Plugins
+-------
+
+* New functions to compress/uncompress images using PNG and JPEG
+* New functions to issue HTTP requests from plugins
+* New function "OrthancPluginBufferCompression()" to (un)compress memory buffers
+* New function "OrthancPluginReadFile()" to read files from the filesystem
+* New function "OrthancPluginWriteFile()" to write files to the filesystem
+* New function "OrthancPluginGetErrorDescription()" to convert error codes to strings
+* New function "OrthancPluginSendHttpStatus()" to send HTTP status with a body
+* New function "OrthancPluginRegisterRestCallbackNoLock()" for high-performance plugins
+* Plugins have access to explicit error codes 
+* Improvements to the sample "ServeFolders" plugin
+* Primitives to upgrade the database version in plugins
+
+Maintenance
+-----------
+
+* Many code refactorings
+* Improved error codes (no more custom descriptions in exceptions)
+* If error while calling the REST API, the answer body contains description of the error
+  (this feature can be disabled with the "HttpDescribeErrors" option)
+* Upgrade to curl 7.44.0 for static and Windows builds
+* Upgrade to libcurl 1.0.2d for static and Windows builds
+* Depends on libjpeg 9a
+* Bypass zlib uncompression if "StorageCompression" is enabled and HTTP client supports deflate
+
+
+Version 0.9.3 (2015/08/07)
+==========================
+
+* C-Echo testing can be triggered from Orthanc Explorer (in the query/retrieve page)
+* Removal of the dependency upon Google Log, Orthanc now uses its internal logger 
+  (use -DENABLE_GOOGLE_LOG=ON to re-enable Google Log)
+* Upgrade to JsonCpp 0.10.5 for static and Windows builds
+
+
+Version 0.9.2 (2015/08/02)
+==========================
+
+* Upgrade to Boost 1.58.0 for static and Windows builds
+* Source code repository moved from Google Code to BitBucket
+* Inject version information into Windows binaries
+* Fix access to binary data in HTTP/REST requests by Lua scripts
+* Fix potential deadlock in the callbacks of plugins
+
+
+Version 0.9.1 (2015/07/02)
+==========================
+
+General
+-------
+
+* The configuration can be splitted into several files stored inside the same folder
+* Custom setting of the local AET during C-Store SCU (both in Lua and in the REST API)
+* Many code refactorings
+
+Lua
+---
+
+* Access to the REST API of Orthanc (RestApiGet, RestApiPost, RestApiPut, RestApiDelete)
+* Functions to convert between Lua values and JSON strings: "ParseJson" and "DumpJson"
+* New events: "OnStablePatient", "OnStableStudy", "OnStableSeries", "Initialize", "Finalize"
+
+Plugins
+-------
+
+* Plugins can retrieve the configuration file directly as a JSON string
+* Plugins can send answers as multipart messages
+
+Fixes
+-----
+
+* Fix compatibility issues for C-Find SCU to Siemens Syngo.Via modalities SCP
+* Fix issue 15 (Lua scripts making HTTP requests)
+* Fix issue 35 (Characters in PatientID string are not protected for C-Find)
+* Fix issue 37 (Hyphens trigger range query even if datatype does not support ranges)
+
+
+Version 0.9.0 (2015/06/03)
+==========================
+
+Major
+-----
+
+* DICOM Query/Retrieve available from Orthanc Explorer
+* C-Move SCU and C-Find SCU are accessible through the REST API
+* "?expand" flag for URIs "/patients", "/studies" and "/series"
+* "/tools/find" URI to search for DICOM resources from REST
+* Support of FreeBSD
+* The "Orthanc Client" SDK is now a separate project
+
+Minor
+-----
+
+* Speed-up in Orthanc Explorer for large amount of images
+* Speed-up of the C-Find SCP server of Orthanc
+* Allow replacing PatientID/StudyInstanceUID/SeriesInstanceUID from Lua scripts
+* Option "CaseSensitivePN" to enable case-insensitive C-Find SCP
+
+Fixes
+-----
+
+* Prevent freeze on C-FIND if no DICOM tag is to be returned
+* Fix slow C-Store SCP on recent versions of Linux, if
+  USE_SYSTEM_DCMTK is set to OFF (http://forum.dcmtk.org/viewtopic.php?f=1&t=4009)
+* Fix issue 30 (QR response missing "Query/Retrieve Level" (008,0052))
+* Fix issue 32 (Cyrillic symbols): Introduction of the "Windows1251" encoding
+* Plugins now receive duplicated GET arguments in their REST callbacks
+
+
+Version 0.8.6 (2015/02/12)
+==========================
+
 Major
 -----
 
@@ -15,6 +169,7 @@
 * More flexible "/modify" and "/anonymize" for single instance
 * Access to called AET and remote AET from Lua scripts ("OnStoredInstance")
 * Option "DicomAssociationCloseDelay" to set delay before closing DICOM association
+* ZIP archives now display the accession number of the studies
 
 Plugins
 -------
@@ -32,6 +187,7 @@
 * Code refactorings
 * Fix issue 25 (AET with underscore not allowed)
 * Fix replacement and insertion of private DICOM tags
+* Fix anonymization generating non-portable DICOM files
 
 
 Version 0.8.5 (2014/11/04)
--- a/OrthancCppClient/Instance.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,286 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "../Core/PrecompiledHeaders.h"
-#include "Instance.h"
-
-#include "OrthancConnection.h"
-
-#include <boost/lexical_cast.hpp>
-
-namespace OrthancClient
-{
-  void Instance::DownloadImage()
-  {
-    if (reader_.get() == NULL)
-    {
-      const char* suffix;
-      switch (mode_)
-      {
-        case Orthanc::ImageExtractionMode_Preview:
-          suffix = "preview";
-          break;
-          
-        case Orthanc::ImageExtractionMode_UInt8:
-          suffix = "image-uint8";
-          break;
-          
-        case Orthanc::ImageExtractionMode_UInt16:
-          suffix = "image-uint16";
-          break;
-          
-        case Orthanc::ImageExtractionMode_Int16:
-          suffix = "image-int16";
-          break;
-          
-        default:
-          throw OrthancClientException(Orthanc::ErrorCode_NotImplemented);
-      }
-
-      Orthanc::HttpClient client(connection_.GetHttpClient());
-      client.SetUrl(std::string(connection_.GetOrthancUrl()) +  "/instances/" + id_ + "/" + suffix);
-      std::string png;
-
-      if (!client.Apply(png))
-      {
-        throw OrthancClientException(Orthanc::ErrorCode_NotImplemented);
-      }
-     
-      reader_.reset(new Orthanc::PngReader);
-      reader_->ReadFromMemory(png);
-    }
-  }
-
-  void Instance::DownloadDicom()
-  {
-    if (dicom_.get() == NULL)
-    {
-      Orthanc::HttpClient client(connection_.GetHttpClient());
-      client.SetUrl(std::string(connection_.GetOrthancUrl()) +  "/instances/" + id_ + "/file");
-
-      dicom_.reset(new std::string);
-
-      if (!client.Apply(*dicom_))
-      {
-        throw OrthancClientException(Orthanc::ErrorCode_NetworkProtocol);
-      }
-    }
-  }
-
-  Instance::Instance(const OrthancConnection& connection,
-                     const char* id) :
-    connection_(connection),
-    id_(id),
-    mode_(Orthanc::ImageExtractionMode_Int16)
-  {
-    Orthanc::HttpClient client(connection_.GetHttpClient());
-            
-    client.SetUrl(std::string(connection_.GetOrthancUrl()) + "/instances/" + id_ + "/simplified-tags");
-    Json::Value v;
-    if (!client.Apply(tags_))
-    {
-      throw OrthancClientException(Orthanc::ErrorCode_NetworkProtocol);
-    }
-  }
-
-  const char* Instance::GetTagAsString(const char* tag) const
-  {
-    if (tags_.isMember(tag))
-    {
-      return tags_[tag].asCString();
-    }
-    else
-    {
-      throw OrthancClientException(Orthanc::ErrorCode_InexistentItem);
-    }
-  }
-
-  float Instance::GetTagAsFloat(const char* tag) const
-  {
-    std::string value = GetTagAsString(tag);
-
-    try
-    {
-      return boost::lexical_cast<float>(value);
-    }
-    catch (boost::bad_lexical_cast)
-    {
-      throw OrthancClientException(Orthanc::ErrorCode_BadFileFormat);
-    }
-  }
-
-  int Instance::GetTagAsInt(const char* tag) const
-  {
-    std::string value = GetTagAsString(tag);
-
-    try
-    {
-      return boost::lexical_cast<int>(value);
-    }
-    catch (boost::bad_lexical_cast)
-    {
-      throw OrthancClientException(Orthanc::ErrorCode_BadFileFormat);
-    }
-  }
-
-  unsigned int Instance::GetWidth()
-  {
-    DownloadImage();
-    return reader_->GetWidth();
-  }
-
-  unsigned int Instance::GetHeight() 
-  {
-    DownloadImage();
-    return reader_->GetHeight();
-  }
-
-  unsigned int Instance::GetPitch()
-  {
-    DownloadImage();
-    return reader_->GetPitch();
-  }
-
-  Orthanc::PixelFormat Instance::GetPixelFormat()
-  {
-    DownloadImage();
-    return reader_->GetFormat();
-  }
-
-  const void* Instance::GetBuffer()
-  {
-    DownloadImage();
-    return reader_->GetConstBuffer();
-  }
-
-  const void* Instance::GetBuffer(unsigned int y)
-  {
-    DownloadImage();
-    return reader_->GetConstRow(y);
-  }
-
-  void Instance::DiscardImage()
-  {
-    reader_.reset();
-  }
-
-  void Instance::DiscardDicom()
-  {
-    dicom_.reset();
-  }
-
-
-  void Instance::SetImageExtractionMode(Orthanc::ImageExtractionMode mode)
-  {
-    if (mode_ == mode)
-    {
-      return;
-    }
-
-    DiscardImage();
-    mode_ = mode;
-  }
-
-
-  void Instance::SplitVectorOfFloats(std::vector<float>& target,
-                                     const char* tag)
-  {
-    const std::string value = GetTagAsString(tag);
-
-    target.clear();
-
-    try
-    {
-      std::string tmp;
-      for (size_t i = 0; i < value.size(); i++)
-      {
-        if (value[i] == '\\')
-        {
-          target.push_back(boost::lexical_cast<float>(tmp));
-          tmp.clear();
-        }
-        else
-        {
-          tmp.push_back(value[i]);
-        }
-      }
-
-      target.push_back(boost::lexical_cast<float>(tmp));
-    }
-    catch (boost::bad_lexical_cast)
-    {
-      // Unable to parse the Image Orientation Patient.
-      throw OrthancClientException(Orthanc::ErrorCode_BadFileFormat);
-    }
-  }
-
-
-  const uint64_t Instance::GetDicomSize()
-  {
-    DownloadDicom();
-    assert(dicom_.get() != NULL);
-    return dicom_->size();
-  }
-
-  const void* Instance::GetDicom()
-  {
-    DownloadDicom();
-    assert(dicom_.get() != NULL);
-
-    if (dicom_->size() == 0)
-    {
-      return NULL;
-    }
-    else
-    {
-      return &((*dicom_) [0]);
-    }
-  }
-
-
-  void Instance::LoadTagContent(const char* path)
-  {
-    Orthanc::HttpClient client(connection_.GetHttpClient());
-    client.SetUrl(std::string(connection_.GetOrthancUrl()) + "/instances/" + id_ + "/content/" + path);
-
-    if (!client.Apply(content_))
-    {
-      throw OrthancClientException(Orthanc::ErrorCode_UnknownResource);
-    }
-  }
-
-
-  const char* Instance::GetLoadedTagContent() const
-  {
-    return content_.c_str();
-  }
-}
--- a/OrthancCppClient/Instance.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,202 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include <string>
-#include <json/value.h>
-
-#include "OrthancClientException.h"
-#include "../Core/IDynamicObject.h"
-#include "../Core/ImageFormats/PngReader.h"
-
-namespace OrthancClient
-{
-  class OrthancConnection;
-
-  /**
-   * {summary}{Connection to an instance stored in %Orthanc.}
-   * {description}{This class encapsulates a connection to an image instance
-   * from a remote instance of %Orthanc.}
-   **/
-  class LAAW_API Instance : public Orthanc::IDynamicObject
-  {
-  private:
-    const OrthancConnection& connection_;
-    std::string id_;
-    Json::Value tags_;
-    std::auto_ptr<Orthanc::PngReader> reader_;
-    Orthanc::ImageExtractionMode mode_;
-    std::auto_ptr<std::string> dicom_;
-    std::string content_;
-
-    void DownloadImage();
-
-    void DownloadDicom();
-
-  public:
-     /**
-     * {summary}{Create a connection to some image instance.}
-     * {param}{connection The remote instance of %Orthanc.}
-     * {param}{id The %Orthanc identifier of the image instance.}
-     **/
-    Instance(const OrthancConnection& connection,
-             const char* id);
-
-    
-    /**
-     * {summary}{Get the %Orthanc identifier of this identifier.}
-     * {returns}{The identifier.}
-     **/
-    const char* GetId() const
-    {
-      return id_.c_str();
-    }
-
-
-    /**
-     * {summary}{Set the extraction mode for the 2D image corresponding to this instance.}
-     * {param}{mode The extraction mode.}
-     **/
-    void SetImageExtractionMode(Orthanc::ImageExtractionMode mode);
-
-    /**
-     * {summary}{Get the extraction mode for the 2D image corresponding to this instance.}
-     * {returns}{The extraction mode.}
-     **/
-    Orthanc::ImageExtractionMode GetImageExtractionMode() const
-    {
-      return mode_;
-    }
-
-    
-    /**
-     * {summary}{Get the string value of some DICOM tag of this instance.}
-     * {param}{tag The name of the tag of interest.}
-     * {returns}{The value of the tag.}
-     **/
-    const char* GetTagAsString(const char* tag) const;
-
-    /**
-     * {summary}{Get the floating point value that is stored in some DICOM tag of this instance.}
-     * {param}{tag The name of the tag of interest.}
-     * {returns}{The value of the tag.}
-     **/
-    float GetTagAsFloat(const char* tag) const;
-
-    /**
-     * {summary}{Get the integer value that is stored in some DICOM tag of this instance.}
-     * {param}{tag The name of the tag of interest.}
-     * {returns}{The value of the tag.}
-     **/
-    int32_t GetTagAsInt(const char* tag) const;
-
-    
-    /**
-     * {summary}{Get the width of the 2D image.}
-     * {description}{Get the width of the 2D image that is encoded by this DICOM instance.}
-     * {returns}{The width.}
-     **/
-    uint32_t GetWidth();
-
-    /**
-     * {summary}{Get the height of the 2D image.}
-     * {description}{Get the height of the 2D image that is encoded by this DICOM instance.}
-     * {returns}{The height.}
-     **/
-    uint32_t GetHeight();
-
-    /**
-     * {summary}{Get the number of bytes between two lines of the image (pitch).}
-     * {description}{Get the number of bytes between two lines of the image in the memory buffer returned by GetBuffer(). This value depends on the extraction mode for the image.}
-     * {returns}{The pitch.}
-     **/
-    uint32_t GetPitch();
-
-    /**
-     * {summary}{Get the format of the pixels of the 2D image.}
-     * {description}{Return the memory layout that is used for the 2D image that is encoded by this DICOM instance. This value depends on the extraction mode for the image.}
-     * {returns}{The pixel format.}
-     **/
-    Orthanc::PixelFormat GetPixelFormat();
-
-    /**
-     * {summary}{Access the memory buffer in which the raw pixels of the 2D image are stored.}
-     * {returns}{A pointer to the memory buffer.}
-     **/
-    const void* GetBuffer();
-
-    /**
-     * {summary}{Access the memory buffer in which the raw pixels of some line of the 2D image are stored.}
-     * {param}{y The line of interest.}
-     * {returns}{A pointer to the memory buffer.}
-     **/
-    const void* GetBuffer(uint32_t y);
-
-    /**
-     * {summary}{Get the size of the DICOM file corresponding to this instance.}
-     * {returns}{The file size.}
-     **/
-    const uint64_t GetDicomSize();
-
-    /**
-     * {summary}{Get a pointer to the content of the DICOM file corresponding to this instance.}
-     * {returns}{The DICOM file.}
-     **/
-    const void* GetDicom();
-
-    /**
-     * {summary}{Discard the downloaded 2D image, so as to make room in memory.}
-     **/
-    void DiscardImage();
-
-    /**
-     * {summary}{Discard the downloaded DICOM file, so as to make room in memory.}
-     **/
-    void DiscardDicom();
-
-    LAAW_API_INTERNAL void SplitVectorOfFloats(std::vector<float>& target,
-                                               const char* tag);
-
-    /**
-     * {summary}{Load a raw tag from the DICOM file.}
-     * {param}{path The path to the tag of interest (e.g. "0020-000d").}
-     **/
-    void LoadTagContent(const char* path);
-
-    /**
-     * {summary}{Return the value of the raw tag that was loaded by LoadContent.}
-     * {returns}{The tag value.}
-     **/
-    const char* GetLoadedTagContent() const;
-  };
-}
--- a/OrthancCppClient/OrthancClientException.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../Core/OrthancException.h"
-#include <laaw/laaw.h>
-
-namespace OrthancClient
-{
-  class OrthancClientException : public ::Laaw::LaawException
-  {
-  public:
-    OrthancClientException(Orthanc::ErrorCode code) :
-      LaawException(Orthanc::OrthancException::GetDescription(code))
-    { 
-    }
-
-    OrthancClientException(const char* message) : 
-      LaawException(message)
-    {    
-    }
-
-    OrthancClientException(const std::string& message) : 
-      LaawException(message)
-    {    
-    }
-  };
-}
--- a/OrthancCppClient/OrthancConnection.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "../Core/PrecompiledHeaders.h"
-#include "OrthancConnection.h"
-
-#include "../Core/Toolbox.h"
-
-namespace OrthancClient
-{
-  void OrthancConnection::ReadPatients()
-  {
-    client_.SetMethod(Orthanc::HttpMethod_Get);
-    client_.SetUrl(orthancUrl_ + "/patients");
-
-    Json::Value v;
-    if (!client_.Apply(content_))
-    {
-      throw OrthancClientException(Orthanc::ErrorCode_NetworkProtocol);
-    }
-  }
-
-  Orthanc::IDynamicObject* OrthancConnection::GetFillerItem(size_t index)
-  {
-    Json::Value::ArrayIndex tmp = static_cast<Json::Value::ArrayIndex>(index);
-    std::string id = content_[tmp].asString();
-    return new Patient(*this, id.c_str());
-  }
-
-  Patient& OrthancConnection::GetPatient(unsigned int index)
-  {
-    return dynamic_cast<Patient&>(patients_.GetItem(index));
-  }
-
-  OrthancConnection::OrthancConnection(const char* orthancUrl) : 
-    orthancUrl_(orthancUrl), patients_(*this)
-  {
-    ReadPatients();
-  }
-  
-  OrthancConnection::OrthancConnection(const char* orthancUrl,
-                                       const char* username, 
-                                       const char* password) : 
-    orthancUrl_(orthancUrl), patients_(*this)
-  {
-    client_.SetCredentials(username, password);
-    ReadPatients();
-  }
-
-
-  void OrthancConnection::Store(const void* dicom, uint64_t size)
-  {
-    if (size == 0)
-    {
-      return;
-    }
-
-    client_.SetMethod(Orthanc::HttpMethod_Post);
-    client_.SetUrl(orthancUrl_ + "/instances");
-
-    // Copy the DICOM file in the POST body. TODO - Avoid memory copy
-    client_.AccessPostData().resize(static_cast<size_t>(size));
-    memcpy(&client_.AccessPostData()[0], dicom, static_cast<size_t>(size));
-
-    Json::Value v;
-    if (!client_.Apply(v))
-    {
-      throw OrthancClientException(Orthanc::ErrorCode_NetworkProtocol);
-    }
-    
-    Reload();
-  }
-
-
-  void  OrthancConnection::StoreFile(const char* filename)
-  {
-    std::string content;
-    Orthanc::Toolbox::ReadFile(content, filename);
-
-    if (content.size() != 0)
-    {
-      Store(&content[0], content.size());
-    }
-  }
-
-}
--- a/OrthancCppClient/OrthancConnection.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,180 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../Core/HttpClient.h"
-
-#include "Patient.h"
-
-namespace OrthancClient
-{
-  /**
-   * {summary}{Connection to an instance of %Orthanc.}
-   * {description}{This class encapsulates a connection to a remote instance
-   * of %Orthanc through its REST API.}
-   **/  
-  class LAAW_API OrthancConnection : 
-    public boost::noncopyable,
-    private Orthanc::ArrayFilledByThreads::IFiller
-  {
-  private:
-    Orthanc::HttpClient client_;
-    std::string orthancUrl_;
-    Orthanc::ArrayFilledByThreads  patients_;
-    Json::Value content_;
-
-    void ReadPatients();
-
-    virtual size_t GetFillerSize()
-    {
-      return content_.size();
-    }
-
-    virtual Orthanc::IDynamicObject* GetFillerItem(size_t index);
-
-  public:
-    /**
-     * {summary}{Create a connection to an instance of %Orthanc.}
-     * {param}{orthancUrl URL to which the REST API of %Orthanc is listening.}
-     **/
-    OrthancConnection(const char* orthancUrl);
-
-    /**
-     * {summary}{Create a connection to an instance of %Orthanc, with authentication.}
-     * {param}{orthancUrl URL to which the REST API of %Orthanc is listening.}
-     * {param}{username The username.}
-     * {param}{password The password.}
-     **/
-    OrthancConnection(const char* orthancUrl,
-                      const char* username, 
-                      const char* password);
-
-    virtual ~OrthancConnection()
-    {
-    }
-
-    /**
-     * {summary}{Returns the number of threads for this connection.}
-     * {description}{Returns the number of simultaneous connections
-     * that are used when downloading information from this instance
-     * of %Orthanc.} 
-     * {returns}{The number of threads.}
-     **/
-    uint32_t GetThreadCount() const
-    {
-      return patients_.GetThreadCount();
-    }
-
-    /**
-     * {summary}{Sets the number of threads for this connection.}
-     * {description}{Sets  the number of simultaneous connections
-     * that are used when downloading information from this instance
-     * of %Orthanc.} 
-     * {param}{threadCount The number of threads.}
-     **/
-    void SetThreadCount(uint32_t threadCount)
-    {
-      patients_.SetThreadCount(threadCount);
-    }
-
-    /**
-     * {summary}{Reload the list of the patients.}
-     * {description}{This method will reload the list of the patients from the remote instance of %Orthanc. Pay attention to the fact that the patients that have been previously returned by GetPatient() will be invalidated.}
-     **/
-    void Reload()
-    {
-      ReadPatients();
-      patients_.Invalidate();
-    }
-
-    LAAW_API_INTERNAL const Orthanc::HttpClient& GetHttpClient() const
-    {
-      return client_;
-    }
-
-    /**
-     * {summary}{Returns the URL of this instance of %Orthanc.}
-     * {description}{Returns the URL of the remote %Orthanc instance to which this object is connected.}
-     * {returns}{The URL.}
-     **/
-    const char* GetOrthancUrl() const
-    {
-      return orthancUrl_.c_str();
-    }
-
-    /**
-     * {summary}{Returns the number of patients.}
-     * {description}{Returns the number of patients that are stored in the remote instance of %Orthanc.}
-     * {returns}{The number of patients.}
-     **/
-    uint32_t GetPatientCount()
-    {
-      return patients_.GetSize();
-    }
-
-    /**
-     * {summary}{Get some patient.}
-     * {description}{This method will return an object that contains information about some patient. The patients are indexed by a number between 0 (inclusive) and the result of GetPatientCount() (exclusive).}
-     * {param}{index The index of the patient of interest.}
-     * {returns}{The patient.}
-     **/
-    Patient& GetPatient(uint32_t index);
-
-    /**
-     * {summary}{Delete some patient.}
-     * {description}{Delete some patient from the remote instance of %Orthanc. Pay attention to the fact that the patients that have been previously returned by GetPatient() will be invalidated.}
-     * {param}{index The index of the patient of interest.}
-     * {returns}{The patient.}
-     **/
-    void DeletePatient(uint32_t index)
-    {
-      GetPatient(index).Delete();
-      Reload();
-    }
-
-    /**
-     * {summary}{Send a DICOM file.}
-     * {description}{This method will store a DICOM file in the remote instance of %Orthanc. Pay attention to the fact that the patients that have been previously returned by GetPatient() will be invalidated.}
-     * {param}{filename Path to the DICOM file}
-     **/
-    void StoreFile(const char* filename);
-
-    /**
-     * {summary}{Send a DICOM file that is contained inside a memory buffer.}
-     * {description}{This method will store a DICOM file in the remote instance of %Orthanc. Pay attention to the fact that the patients that have been previously returned by GetPatient() will be invalidated.}
-     * {param}{dicom The memory buffer containing the DICOM file.}
-     * {param}{size The size of the DICOM file.}
-     **/    
-    void Store(const void* dicom, uint64_t size);
-  };
-}
--- a/OrthancCppClient/OrthancCppClient.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-/**
- * The sources of the C++ client library must be put in this file to
- * avoid problems with precompiled headers.
- **/
-
-#include "../Core/ChunkedBuffer.cpp"
-#include "../Core/Enumerations.cpp"
-#include "../Core/HttpClient.cpp"
-#include "../Core/ImageFormats/ImageAccessor.cpp"
-#include "../Core/ImageFormats/ImageBuffer.cpp"
-#include "../Core/ImageFormats/PngReader.cpp"
-#include "../Core/MultiThreading/ArrayFilledByThreads.cpp"
-#include "../Core/MultiThreading/SharedMessageQueue.cpp"
-#include "../Core/MultiThreading/ThreadedCommandProcessor.cpp"
-#include "../Core/OrthancException.cpp"
-#include "../Core/Toolbox.cpp"
-#include "../OrthancCppClient/Instance.cpp"
-#include "../OrthancCppClient/OrthancConnection.cpp"
-#include "../OrthancCppClient/Patient.cpp"
-#include "../OrthancCppClient/Series.cpp"
-#include "../OrthancCppClient/Study.cpp"
--- a/OrthancCppClient/Patient.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "../Core/PrecompiledHeaders.h"
-#include "Patient.h"
-
-#include "OrthancConnection.h"
-
-namespace OrthancClient
-{
-  void Patient::ReadPatient()
-  {
-    Orthanc::HttpClient client(connection_.GetHttpClient());
-    client.SetUrl(std::string(connection_.GetOrthancUrl()) + "/patients/" + id_);
-
-    Json::Value v;
-    if (!client.Apply(patient_))
-    {
-      throw OrthancClientException(Orthanc::ErrorCode_NetworkProtocol);
-    }
-  }
-
-  Orthanc::IDynamicObject* Patient::GetFillerItem(size_t index)
-  {
-    Json::Value::ArrayIndex tmp = static_cast<Json::Value::ArrayIndex>(index);
-    std::string id = patient_["Studies"][tmp].asString();
-    return new Study(connection_, id.c_str());
-  }
-
-  Patient::Patient(const OrthancConnection& connection,
-                   const char* id) :
-    connection_(connection),
-    id_(id),
-    studies_(*this)
-  {
-    studies_.SetThreadCount(connection.GetThreadCount());
-    ReadPatient();
-  }
-
-  const char* Patient::GetMainDicomTag(const char* tag, const char* defaultValue) const
-  {
-    if (patient_["MainDicomTags"].isMember(tag))
-    {
-      return patient_["MainDicomTags"][tag].asCString();
-    }
-    else
-    {
-      return defaultValue;
-    }
-  }
-  
-  void Patient::Delete()
-  {
-    Orthanc::HttpClient client(connection_.GetHttpClient());
-    client.SetMethod(Orthanc::HttpMethod_Delete);
-    client.SetUrl(std::string(connection_.GetOrthancUrl()) + "/patients/" + id_);
-    
-    std::string s;
-    if (!client.Apply(s))
-    {
-      throw OrthancClientException(Orthanc::ErrorCode_NetworkProtocol);
-    }
-  }
-}
--- a/OrthancCppClient/Patient.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,121 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "Study.h"
-
-namespace OrthancClient
-{
-  /**
-   * {summary}{Connection to a patient stored in %Orthanc.}
-   * {description}{This class encapsulates a connection to a patient
-   * from a remote instance of %Orthanc.}
-   **/
-  class LAAW_API Patient : 
-    public Orthanc::IDynamicObject, 
-    private Orthanc::ArrayFilledByThreads::IFiller
-  {
-  private:
-    const OrthancConnection& connection_;
-    std::string id_;
-    Json::Value patient_;
-    Orthanc::ArrayFilledByThreads  studies_;
-
-    void ReadPatient();
-
-    virtual size_t GetFillerSize()
-    {
-      return patient_["Studies"].size();
-    }
-
-    virtual Orthanc::IDynamicObject* GetFillerItem(size_t index);
-
-  public:
-    /**
-     * {summary}{Create a connection to some patient.}
-     * {param}{connection The remote instance of %Orthanc.}
-     * {param}{id The %Orthanc identifier of the patient.}
-     **/
-    Patient(const OrthancConnection& connection,
-            const char* id);
-
-    /**
-     * {summary}{Reload the studies of this patient.}
-     * {description}{This method will reload the list of the studies of this patient. Pay attention to the fact that the studies that have been previously returned by GetStudy() will be invalidated.}
-     **/
-    void Reload()
-    {
-      studies_.Reload();
-    }
-
-    /**
-     * {summary}{Return the number of studies for this patient.}
-     * {returns}{The number of studies.}
-     **/
-    uint32_t GetStudyCount()
-    {
-      return studies_.GetSize();
-    }
-
-    /**
-     * {summary}{Get some study of this patient.}
-     * {description}{This method will return an object that contains information about some study. The studies are indexed by a number between 0 (inclusive) and the result of GetStudyCount() (exclusive).}
-     * {param}{index The index of the study of interest.}
-     * {returns}{The study.}
-     **/
-    Study& GetStudy(uint32_t index)
-    {
-      return dynamic_cast<Study&>(studies_.GetItem(index));
-    }
-
-    /**
-     * {summary}{Get the %Orthanc identifier of this patient.}
-     * {returns}{The identifier.}
-     **/
-    const char* GetId() const
-    {
-      return id_.c_str();
-    }
-
-    /**
-     * {summary}{Get the value of one of the main DICOM tags for this patient.}
-     * {param}{tag The name of the tag of interest ("PatientName", "PatientID", "PatientSex" or "PatientBirthDate").}
-     * {param}{defaultValue The default value to be returned if this tag does not exist.}
-     * {returns}{The value of the tag.}
-     **/
-    const char* GetMainDicomTag(const char* tag, 
-                                const char* defaultValue) const;
-
-    LAAW_API_INTERNAL void Delete();
-  };
-}
--- a/OrthancCppClient/Series.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,527 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "../Core/PrecompiledHeaders.h"
-#include "Series.h"
-
-#include "OrthancConnection.h"
-
-#include <set>
-#include <boost/lexical_cast.hpp>
-
-namespace OrthancClient
-{
-  namespace
-  {
-    class SliceLocator
-    {
-    private:
-      float normal_[3];
-
-    public:
-      SliceLocator(Instance& someSlice)
-      {
-        /**
-         * Compute the slice normal from Image Orientation Patient.
-         * http://nipy.sourceforge.net/nibabel/dicom/dicom_orientation.html#dicom-z-from-slice
-         * http://dicomiseasy.blogspot.be/2013/06/getting-oriented-using-image-plane.html
-         * http://www.itk.org/pipermail/insight-users/2003-September/004762.html
-         **/
-
-        std::vector<float> cosines;
-        someSlice.SplitVectorOfFloats(cosines, "ImageOrientationPatient");  // 0020-0037
-
-        if (cosines.size() != 6)
-        {
-          throw OrthancClientException(Orthanc::ErrorCode_BadFileFormat);
-        }
-
-        normal_[0] = cosines[1] * cosines[5] - cosines[2] * cosines[4];
-        normal_[1] = cosines[2] * cosines[3] - cosines[0] * cosines[5];
-        normal_[2] = cosines[0] * cosines[4] - cosines[1] * cosines[3];
-      }
-
-
-      /**
-       * Compute the distance of some slice along the slice normal.
-       **/
-      float ComputeSliceLocation(Instance& instance) const
-      {
-        std::vector<float> ipp;
-        instance.SplitVectorOfFloats(ipp, "ImagePositionPatient");  // 0020-0032
-        if (ipp.size() != 3)
-        {
-          throw OrthancClientException(Orthanc::ErrorCode_BadFileFormat);
-        }
-
-        float dist = 0;
-
-        for (int i = 0; i < 3; i++)
-        {
-          dist += normal_[i] * ipp[i];
-        }
-
-        return dist;
-      }
-    };
-
-    class ImageDownloadCommand : public Orthanc::ICommand
-    {
-    private:
-      Orthanc::PixelFormat format_;
-      Orthanc::ImageExtractionMode mode_;
-      Instance& instance_;
-      void* target_;
-      size_t lineStride_;
-
-    public:
-      ImageDownloadCommand(Instance& instance, 
-                           Orthanc::PixelFormat format,
-                           Orthanc::ImageExtractionMode mode,
-                           void* target,
-                           size_t lineStride) :
-        format_(format),
-        mode_(mode),
-        instance_(instance),
-        target_(target),
-        lineStride_(lineStride)
-      {
-        instance_.SetImageExtractionMode(mode);
-      }
-
-      virtual bool Execute()
-      {
-        using namespace Orthanc;
-
-        unsigned int width = instance_.GetHeight();
-
-        for (unsigned int y = 0; y < instance_.GetHeight(); y++)
-        {
-          uint8_t* p = reinterpret_cast<uint8_t*>(target_) + y * lineStride_;
-
-          if (instance_.GetPixelFormat() == format_)
-          {
-            memcpy(p, instance_.GetBuffer(y), GetBytesPerPixel(instance_.GetPixelFormat()) * instance_.GetWidth());
-          }
-          else if (instance_.GetPixelFormat() == PixelFormat_Grayscale8 &&
-                   format_ == PixelFormat_RGB24)
-          {
-            const uint8_t* s = reinterpret_cast<const uint8_t*>(instance_.GetBuffer(y));
-            for (unsigned int x = 0; x < width; x++, s++, p += 3)
-            {
-              p[0] = *s;
-              p[1] = *s;
-              p[2] = *s;
-            }
-          }
-          else
-          {
-            throw OrthancClientException(ErrorCode_NotImplemented);
-          }
-        }
-
-        // Do not keep the image in memory, as we are loading 3D images
-        instance_.DiscardImage();
-
-        return true;
-      }
-    };
-
-
-    class ProgressToFloatListener : public Orthanc::ThreadedCommandProcessor::IListener
-    {
-    private:
-      float* target_;
-
-    public:
-      ProgressToFloatListener(float* target) : target_(target)
-      {
-      }
-
-      virtual void SignalProgress(unsigned int current,
-                                  unsigned int total)
-      {
-        if (total == 0)
-        {
-          *target_ = 0;
-        }
-        else
-        {
-          *target_ = static_cast<float>(current) / static_cast<float>(total);
-        }
-      }
-
-      virtual void SignalSuccess(unsigned int total)
-      {
-        *target_ = 1;
-      }
-
-      virtual void SignalFailure()
-      {
-        *target_ = 0;
-      }
-
-      virtual void SignalCancel()
-      {
-        *target_ = 0;
-      }
-    };
-
-  }
-
-
-  void Series::Check3DImage()
-  {
-    if (!Is3DImage())
-    {
-      throw OrthancClientException(Orthanc::ErrorCode_NotImplemented);
-    }
-  }
-
-  bool Series::Is3DImageInternal()
-  {
-    try
-    {
-      if (GetInstanceCount() == 0)
-      {
-        // Empty image, use some default value (should never happen)
-        voxelSizeX_ = 1;
-        voxelSizeY_ = 1;
-        voxelSizeZ_ = 1;
-        sliceThickness_ = 1;
-
-        return true;
-      }
-
-      // Choose a reference slice
-      Instance& reference = GetInstance(0);
-
-      // Check that all the child instances share the same 3D parameters
-      for (unsigned int i = 0; i < GetInstanceCount(); i++)
-      {
-        Instance& i2 = GetInstance(i);
-
-        if (std::string(reference.GetTagAsString("Columns")) != std::string(i2.GetTagAsString("Columns")) ||
-            std::string(reference.GetTagAsString("Rows")) != std::string(i2.GetTagAsString("Rows")) ||
-            std::string(reference.GetTagAsString("ImageOrientationPatient")) != std::string(i2.GetTagAsString("ImageOrientationPatient")) ||
-            std::string(reference.GetTagAsString("SliceThickness")) != std::string(i2.GetTagAsString("SliceThickness")) ||
-            std::string(reference.GetTagAsString("PixelSpacing")) != std::string(i2.GetTagAsString("PixelSpacing")))
-        {
-          return false;
-        }              
-      }
-
-
-      // Extract X/Y voxel size and slice thickness
-      std::string s = GetInstance(0).GetTagAsString("PixelSpacing");  // 0028-0030
-      size_t pos = s.find('\\');
-      assert(pos != std::string::npos);
-      std::string sy = s.substr(0, pos);
-      std::string sx = s.substr(pos + 1);
-
-      try
-      {
-        voxelSizeX_ = boost::lexical_cast<float>(sx);
-        voxelSizeY_ = boost::lexical_cast<float>(sy);
-      }
-      catch (boost::bad_lexical_cast)
-      {
-        throw OrthancClientException(Orthanc::ErrorCode_BadFileFormat);
-      }
-
-      sliceThickness_ = GetInstance(0).GetTagAsFloat("SliceThickness");  // 0018-0050
-
-
-      // Compute the location of each slice to extract the voxel size along Z
-      voxelSizeZ_ = std::numeric_limits<float>::infinity();
-
-      SliceLocator locator(reference);
-      float referenceSliceLocation = locator.ComputeSliceLocation(reference);
-
-      std::set<float> l;
-      for (unsigned int i = 0; i < GetInstanceCount(); i++)
-      {
-        float location = locator.ComputeSliceLocation(GetInstance(i));
-        float distanceToReferenceSlice = fabs(location - referenceSliceLocation);
-
-        l.insert(location);
-
-        if (distanceToReferenceSlice > std::numeric_limits<float>::epsilon() &&
-            distanceToReferenceSlice < voxelSizeZ_)
-        {
-          voxelSizeZ_ = distanceToReferenceSlice;
-        }
-      }
-
-
-      // Make sure that 2 slices do not share the same Z location
-      return l.size() == GetInstanceCount();
-    }
-    catch (OrthancClientException)
-    {
-      return false;
-    }
-  }
-
-  void Series::ReadSeries()
-  {
-    Orthanc::HttpClient client(connection_.GetHttpClient());
-
-    client.SetUrl(std::string(connection_.GetOrthancUrl()) + "/series/" + id_);
-    Json::Value v;
-    if (!client.Apply(series_))
-    {
-      throw OrthancClientException(Orthanc::ErrorCode_NetworkProtocol);
-    }
-  }
-
-  Orthanc::IDynamicObject* Series::GetFillerItem(size_t index)
-  {
-    Json::Value::ArrayIndex tmp = static_cast<Json::Value::ArrayIndex>(index);
-    std::string id = series_["Instances"][tmp].asString();
-    return new Instance(connection_, id.c_str());
-  }
-
-  Series::Series(const OrthancConnection& connection,
-                 const char* id) :
-    connection_(connection),
-    id_(id),
-    instances_(*this)
-  {
-    ReadSeries();
-    status_ = Status3DImage_NotTested;
-    url_ = std::string(connection_.GetOrthancUrl()) + "/series/" + id_;
-
-    voxelSizeX_ = 0;
-    voxelSizeY_ = 0;
-    voxelSizeZ_ = 0;
-    sliceThickness_ = 0;
-
-    instances_.SetThreadCount(connection.GetThreadCount());
-  }
-
-
-  bool Series::Is3DImage()
-  {
-    if (status_ == Status3DImage_NotTested)
-    {
-      status_ = Is3DImageInternal() ? Status3DImage_True : Status3DImage_False;
-    }
-
-    return status_ == Status3DImage_True;
-  }
-
-  unsigned int Series::GetInstanceCount()
-  {
-    return instances_.GetSize();
-  }
-
-  Instance& Series::GetInstance(unsigned int index)
-  {
-    return dynamic_cast<Instance&>(instances_.GetItem(index));
-  }
-
-  unsigned int Series::GetWidth()
-  {
-    Check3DImage();
-
-    if (GetInstanceCount() == 0)
-      return 0;
-    else
-      return GetInstance(0).GetTagAsInt("Columns");
-  }
-
-  unsigned int Series::GetHeight()
-  {
-    Check3DImage();
-
-    if (GetInstanceCount() == 0)
-      return 0;
-    else
-      return GetInstance(0).GetTagAsInt("Rows");
-  }
-
-  const char* Series::GetMainDicomTag(const char* tag, const char* defaultValue) const
-  {
-    if (series_["MainDicomTags"].isMember(tag))
-    {
-      return series_["MainDicomTags"][tag].asCString();
-    }
-    else
-    {
-      return defaultValue;
-    }
-  }
-
-
-  
-  void Series::Load3DImageInternal(void* target,
-                                   Orthanc::PixelFormat format,
-                                   size_t lineStride,
-                                   size_t stackStride,
-                                   Orthanc::ThreadedCommandProcessor::IListener* listener)
-  {
-    using namespace Orthanc;
-
-    // Choose the extraction mode, depending on the format of the
-    // target image.
-
-    uint8_t bytesPerPixel;
-    ImageExtractionMode mode;
-
-    switch (format)
-    {
-      case PixelFormat_RGB24:
-        bytesPerPixel = 3;
-        mode = ImageExtractionMode_Preview;
-        break;
-
-      case PixelFormat_Grayscale8:
-        bytesPerPixel = 1;
-        mode = ImageExtractionMode_UInt8;  // Preview ???
-        break; 
-
-      case PixelFormat_Grayscale16:
-        bytesPerPixel = 2;
-        mode = ImageExtractionMode_UInt16;
-        break;
-
-      case PixelFormat_SignedGrayscale16:
-        bytesPerPixel = 2;
-        mode = ImageExtractionMode_UInt16;
-        format = PixelFormat_Grayscale16;
-        break;
-
-      default:
-        throw OrthancClientException(ErrorCode_NotImplemented);
-    }
-
-
-    // Check that the target image is properly sized
-    unsigned int sx = GetWidth();
-    unsigned int sy = GetHeight();
-
-    if (lineStride < sx * bytesPerPixel ||
-        stackStride < sx * sy * bytesPerPixel)
-    {
-      throw OrthancClientException(ErrorCode_BadRequest);
-    }
-
-    if (sx == 0 || sy == 0 || GetInstanceCount() == 0)
-    {
-      // Empty image, nothing to do
-      if (listener)
-        listener->SignalSuccess(0);
-      return;
-    }
-
-
-    /**
-     * Order the stacks according to their distance along the slice
-     * normal (using the "Image Position Patient" tag). This works
-     * even if the "SliceLocation" tag is absent.
-     **/
-    SliceLocator locator(GetInstance(0));
-
-    typedef std::map<float, Instance*> Instances;
-    Instances instances;
-    for (unsigned int i = 0; i < GetInstanceCount(); i++)
-    {
-      float dist = locator.ComputeSliceLocation(GetInstance(i));
-      instances[dist] = &GetInstance(i);
-    }
-
-    if (instances.size() != GetInstanceCount())
-    {
-      // Several instances have the same Z coordinate
-      throw OrthancClientException(ErrorCode_NotImplemented);
-    }
-
-
-    // Submit the download of each stack as a set of commands
-    ThreadedCommandProcessor processor(connection_.GetThreadCount());
-
-    if (listener != NULL)
-    {
-      processor.SetListener(*listener);
-    }
-
-    uint8_t* stackTarget = reinterpret_cast<uint8_t*>(target);
-    for (Instances::iterator it = instances.begin(); it != instances.end(); ++it)
-    {
-      processor.Post(new ImageDownloadCommand(*it->second, format, mode, stackTarget, lineStride));
-      stackTarget += stackStride;
-    }
-
-
-    // Wait for all the stacks to be downloaded
-    if (!processor.Join())
-    {
-      throw OrthancClientException(ErrorCode_NetworkProtocol);
-    }
-  }
-
-  float Series::GetVoxelSizeX()
-  {
-    Check3DImage();   // Is3DImageInternal() will compute the voxel sizes
-    return voxelSizeX_;
-  }
-
-  float Series::GetVoxelSizeY()
-  {
-    Check3DImage();   // Is3DImageInternal() will compute the voxel sizes
-    return voxelSizeY_;
-  }
-
-  float Series::GetVoxelSizeZ()
-  {
-    Check3DImage();   // Is3DImageInternal() will compute the voxel sizes
-    return voxelSizeZ_;
-  }
-
-  float Series::GetSliceThickness()
-  {
-    Check3DImage();   // Is3DImageInternal() will compute the voxel sizes
-    return sliceThickness_;
-  }
-
-  void Series::Load3DImage(void* target,
-                           Orthanc::PixelFormat format,
-                           int64_t lineStride,
-                           int64_t stackStride,
-                           float* progress)
-  {
-    ProgressToFloatListener listener(progress);
-    Load3DImageInternal(target, format, static_cast<size_t>(lineStride), 
-                        static_cast<size_t>(stackStride), &listener);
-  }
-}
--- a/OrthancCppClient/Series.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,239 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "Instance.h"
-
-#include "../Core/MultiThreading/ArrayFilledByThreads.h"
-#include "../Core/MultiThreading/ThreadedCommandProcessor.h"
-
-namespace OrthancClient
-{
-  /**
-   * {summary}{Connection to a series stored in %Orthanc.}
-   * {description}{This class encapsulates a connection to a series
-   * from a remote instance of %Orthanc.}
-   **/
-  class LAAW_API Series :
-    public Orthanc::IDynamicObject, 
-    private Orthanc::ArrayFilledByThreads::IFiller
-  {
-  private:
-    enum Status3DImage
-    {
-      Status3DImage_NotTested,
-      Status3DImage_True,
-      Status3DImage_False
-    };
-
-    const OrthancConnection& connection_;
-    std::string id_, url_;
-    Json::Value series_;
-    Orthanc::ArrayFilledByThreads  instances_;
-    Status3DImage status_;
-
-    float voxelSizeX_;
-    float voxelSizeY_;
-    float voxelSizeZ_;
-    float sliceThickness_;
-
-    void Check3DImage();
-
-    bool Is3DImageInternal();
-
-    void ReadSeries();
-
-    virtual size_t GetFillerSize()
-    {
-      return series_["Instances"].size();
-    }
-
-    virtual Orthanc::IDynamicObject* GetFillerItem(size_t index);
-
-    void Load3DImageInternal(void* target,
-                             Orthanc::PixelFormat format,
-                             size_t lineStride,
-                             size_t stackStride,
-                             Orthanc::ThreadedCommandProcessor::IListener* listener);
-
-  public:
-    /**
-     * {summary}{Create a connection to some series.}
-     * {param}{connection The remote instance of %Orthanc.}
-     * {param}{id The %Orthanc identifier of the series.}
-     **/
-    Series(const OrthancConnection& connection,
-           const char* id);
-
-     /**
-     * {summary}{Reload the instances of this series.}
-     * {description}{This method will reload the list of the instances of this series. Pay attention to the fact that the instances that have been previously returned by GetInstance() will be invalidated.}
-     **/
-    void Reload()
-    {
-      instances_.Reload();
-    }
-
-    /**
-     * {summary}{Return the number of instances for this series.}
-     * {returns}{The number of instances.}
-     **/
-    uint32_t GetInstanceCount();
-    
-    /**
-     * {summary}{Get some instance of this series.}
-     * {description}{This method will return an object that contains information about some instance. The instances are indexed by a number between 0 (inclusive) and the result of GetInstanceCount() (exclusive).}
-     * {param}{index The index of the instance of interest.}
-     * {returns}{The instance.}
-     **/
-    Instance& GetInstance(uint32_t index);
-
-    /**
-     * {summary}{Get the %Orthanc identifier of this series.}
-     * {returns}{The identifier.}
-     **/
-    const char* GetId() const
-    {
-      return id_.c_str();
-    }
-
-    /**
-     * {summary}{Returns the URL to this series.}
-     * {returns}{The URL.}
-     **/
-    const char* GetUrl() const
-    {
-      return url_.c_str();
-    }
-
-   
-    /**
-     * {summary}{Get the value of one of the main DICOM tags for this series.}
-     * {param}{tag The name of the tag of interest ("Modality", "Manufacturer", "SeriesDate", "SeriesDescription", "SeriesInstanceUID"...).}
-     * {param}{defaultValue The default value to be returned if this tag does not exist.}
-     * {returns}{The value of the tag.}
-     **/
-    const char* GetMainDicomTag(const char* tag, 
-                                const char* defaultValue) const;
-
-    /**
-     * {summary}{Test whether this series encodes a 3D image that can be downloaded from %Orthanc.}
-     * {returns}{"true" if and only if this is a 3D image.}
-     **/
-    bool Is3DImage();
-
-    /**
-     * {summary}{Get the width of the 3D image.}
-     * {description}{Get the width of the 3D image (i.e. along the X-axis). This call is only valid if this series corresponds to a 3D image.}
-     * {returns}{The width.}
-     **/
-    uint32_t GetWidth();
-
-    /**
-     * {summary}{Get the height of the 3D image.}
-     * {description}{Get the height of the 3D image (i.e. along the Y-axis). This call is only valid if this series corresponds to a 3D image.}
-     * {returns}{The height.}
-     **/
-    uint32_t GetHeight();
-
-    /**
-     * {summary}{Get the physical size of a voxel along the X-axis.}
-     * {description}{Get the physical size of a voxel along the X-axis. This call is only valid if this series corresponds to a 3D image.}
-     * {returns}{The voxel size.}
-     **/
-    float GetVoxelSizeX();
-
-    /**
-     * {summary}{Get the physical size of a voxel along the Y-axis.}
-     * {description}{Get the physical size of a voxel along the Y-axis. This call is only valid if this series corresponds to a 3D image.}
-     * {returns}{The voxel size.}
-     **/
-    float GetVoxelSizeY();
-
-    /**
-     * {summary}{Get the physical size of a voxel along the Z-axis.}
-     * {description}{Get the physical size of a voxel along the Z-axis. This call is only valid if this series corresponds to a 3D image.}
-     * {returns}{The voxel size.}
-     **/
-    float GetVoxelSizeZ();
-
-    /**
-     * {summary}{Get the slice thickness.}
-     * {description}{Get the slice thickness. This call is only valid if this series corresponds to a 3D image.}
-     * {returns}{The slice thickness.}
-     **/
-    float GetSliceThickness();
-
-    LAAW_API_INTERNAL void Load3DImage(void* target,
-                                       Orthanc::PixelFormat format,
-                                       int64_t lineStride,
-                                       int64_t stackStride,
-                                       Orthanc::ThreadedCommandProcessor::IListener& listener)
-    {
-      Load3DImageInternal(target, format, static_cast<size_t>(lineStride), 
-                          static_cast<size_t>(stackStride), &listener);
-    }
-
-    /**
-     * {summary}{Load the 3D image into a memory buffer.}
-     * {description}{Load the 3D image into a memory buffer. This call is only valid if this series corresponds to a 3D image. The "target" buffer must be wide enough to store all the voxels of the image.}
-     * {param}{target The target memory buffer.}
-     * {param}{format The memory layout of the voxels.}
-     * {param}{lineStride The number of bytes between two lines in the target memory buffer.}
-     * {param}{stackStride The number of bytes between two 2D slices in the target memory buffer.}
-     **/
-    void Load3DImage(void* target,
-                     Orthanc::PixelFormat format,
-                     int64_t lineStride,
-                     int64_t stackStride)
-    {
-      Load3DImageInternal(target, format, static_cast<size_t>(lineStride),
-                          static_cast<size_t>(stackStride), NULL);
-    }
-
-    /**
-     * {summary}{Load the 3D image into a memory buffer.}
-     * {description}{Load the 3D image into a memory buffer. This call is only valid if this series corresponds to a 3D image. The "target" buffer must be wide enough to store all the voxels of the image. This method will also update a progress indicator to monitor the loading of the image.}
-     * {param}{target The target memory buffer.}
-     * {param}{format The memory layout of the voxels.}
-     * {param}{lineStride The number of bytes between two lines in the target memory buffer.}
-     * {param}{stackStride The number of bytes between two 2D slices in the target memory buffer.}
-     * {param}{progress A pointer to a floating-point number that is continuously updated by the download threads to reflect the percentage of completion (between 0 and 1). This value can be read from a separate thread.}
-     **/
-    void Load3DImage(void* target,
-                     Orthanc::PixelFormat format,
-                     int64_t lineStride,
-                     int64_t stackStride,
-                     float* progress);
-  };
-}
--- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/ExternC.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1525 +0,0 @@
-/**
- * Laaw - Lightweight, Automated API Wrapper
- * Copyright (C) 2010-2013 Jomago - Alain Mazy, Benjamin Golinvaux,
- * Sebastien Jodogne
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include <laaw/laaw.h>
-#include <string.h>  // For strcpy() and strlen()
-#include <stdlib.h>  // For free()
-
-static char* LAAW_EXTERNC_CopyString(const char* str)
-{
-  char* copy = reinterpret_cast<char*>(malloc(strlen(str) + 1));
-  strcpy(copy, str);
-  return copy;
-}
-
-extern "C"
-{
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_1f1acb322ea4d0aad65172824607673c(void** newObject, const char* arg0)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          *newObject = new OrthancClient::OrthancConnection(reinterpret_cast< const char* >(arg0));
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_f3fd272e4636f6a531aabb72ee01cd5b(void** newObject, const char* arg0, const char* arg1, const char* arg2)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          *newObject = new OrthancClient::OrthancConnection(reinterpret_cast< const char* >(arg0), reinterpret_cast< const char* >(arg1), reinterpret_cast< const char* >(arg2));
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_12d3de0a96e9efb11136a9811bb9ed38(void* thisObject)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          delete static_cast<OrthancClient::OrthancConnection*>(thisObject);
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_557aee7b61817292a0f31269d3c35db7(const void* thisObject, uint32_t* result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          const OrthancClient::OrthancConnection* this_ = static_cast<const OrthancClient::OrthancConnection*>(thisObject);
-*result = this_->GetThreadCount();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_0b8dff0ce67f10954a49b059e348837e(void* thisObject, uint32_t arg0)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::OrthancConnection* this_ = static_cast<OrthancClient::OrthancConnection*>(thisObject);
-this_->SetThreadCount(arg0);
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_e05097c153f676e5a5ee54dcfc78256f(void* thisObject)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::OrthancConnection* this_ = static_cast<OrthancClient::OrthancConnection*>(thisObject);
-this_->Reload();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_e840242bf58d17d3c1d722da09ce88e0(const void* thisObject, const char** result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          const OrthancClient::OrthancConnection* this_ = static_cast<const OrthancClient::OrthancConnection*>(thisObject);
-*result = this_->GetOrthancUrl();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_c9af31433001b5dfc012a552dc6d0050(void* thisObject, uint32_t* result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::OrthancConnection* this_ = static_cast<OrthancClient::OrthancConnection*>(thisObject);
-*result = this_->GetPatientCount();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_3fba4d6b818180a44cd1cae6046334dc(void* thisObject, void** result, uint32_t arg0)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::OrthancConnection* this_ = static_cast<OrthancClient::OrthancConnection*>(thisObject);
-*result = &this_->GetPatient(arg0);
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_aeb20dc75b9246188db857317e5e0ce7(void* thisObject, uint32_t arg0)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::OrthancConnection* this_ = static_cast<OrthancClient::OrthancConnection*>(thisObject);
-this_->DeletePatient(arg0);
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_62689803d9871e4d9c51a648640b320b(void* thisObject, const char* arg0)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::OrthancConnection* this_ = static_cast<OrthancClient::OrthancConnection*>(thisObject);
-this_->StoreFile(reinterpret_cast< const char* >(arg0));
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_2fb64c9e5a67eccd413b0e913469a421(void* thisObject, const void* arg0, uint64_t arg1)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::OrthancConnection* this_ = static_cast<OrthancClient::OrthancConnection*>(thisObject);
-this_->Store(reinterpret_cast< const void* >(arg0), arg1);
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_6cf0d7268667f9b0aa4511bacf184919(void** newObject, void* arg0, const char* arg1)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          *newObject = new OrthancClient::Patient(*reinterpret_cast< ::OrthancClient::OrthancConnection* >(arg0), reinterpret_cast< const char* >(arg1));
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_7d81cd502ee27e859735d0ea7112b5a1(void* thisObject)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          delete static_cast<OrthancClient::Patient*>(thisObject);
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_f756172daf04516eec3a566adabb4335(void* thisObject)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Patient* this_ = static_cast<OrthancClient::Patient*>(thisObject);
-this_->Reload();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_ddb68763ec902a97d579666a73a20118(void* thisObject, uint32_t* result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Patient* this_ = static_cast<OrthancClient::Patient*>(thisObject);
-*result = this_->GetStudyCount();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_fba3c68b4be7558dbc65f7ce1ab57d63(void* thisObject, void** result, uint32_t arg0)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Patient* this_ = static_cast<OrthancClient::Patient*>(thisObject);
-*result = &this_->GetStudy(arg0);
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_b4ca99d958f843493e58d1ef967340e1(const void* thisObject, const char** result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          const OrthancClient::Patient* this_ = static_cast<const OrthancClient::Patient*>(thisObject);
-*result = this_->GetId();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_78d5cc76d282437b6f93ec3b82c35701(const void* thisObject, const char** result, const char* arg0, const char* arg1)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          const OrthancClient::Patient* this_ = static_cast<const OrthancClient::Patient*>(thisObject);
-*result = this_->GetMainDicomTag(reinterpret_cast< const char* >(arg0), reinterpret_cast< const char* >(arg1));
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_193599b9e345384fcdfcd47c29c55342(void** newObject, void* arg0, const char* arg1)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          *newObject = new OrthancClient::Series(*reinterpret_cast< ::OrthancClient::OrthancConnection* >(arg0), reinterpret_cast< const char* >(arg1));
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_7c97f17063a357d38c5fab1136ad12a0(void* thisObject)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          delete static_cast<OrthancClient::Series*>(thisObject);
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_48a2a1a9d68c047e22bfba23014643d2(void* thisObject)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Series* this_ = static_cast<OrthancClient::Series*>(thisObject);
-this_->Reload();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_852bf8296ca21c5fde5ec565cc10721d(void* thisObject, uint32_t* result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Series* this_ = static_cast<OrthancClient::Series*>(thisObject);
-*result = this_->GetInstanceCount();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_efd04574e0779faa83df1f2d8f9888db(void* thisObject, void** result, uint32_t arg0)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Series* this_ = static_cast<OrthancClient::Series*>(thisObject);
-*result = &this_->GetInstance(arg0);
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_736247ff5e8036dac38163da6f666ed5(const void* thisObject, const char** result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          const OrthancClient::Series* this_ = static_cast<const OrthancClient::Series*>(thisObject);
-*result = this_->GetId();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_d82d2598a7a73f4b6fcc0c09c25b08ca(const void* thisObject, const char** result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          const OrthancClient::Series* this_ = static_cast<const OrthancClient::Series*>(thisObject);
-*result = this_->GetUrl();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_88134b978f9acb2aecdadf54aeab3c64(const void* thisObject, const char** result, const char* arg0, const char* arg1)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          const OrthancClient::Series* this_ = static_cast<const OrthancClient::Series*>(thisObject);
-*result = this_->GetMainDicomTag(reinterpret_cast< const char* >(arg0), reinterpret_cast< const char* >(arg1));
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_152cb1b704c053d24b0dab7461ba6ea3(void* thisObject, int32_t* result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Series* this_ = static_cast<OrthancClient::Series*>(thisObject);
-*result = this_->Is3DImage();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_eee03f337ec81d9f1783cd41e5238757(void* thisObject, uint32_t* result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Series* this_ = static_cast<OrthancClient::Series*>(thisObject);
-*result = this_->GetWidth();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_006f08237bd7611636fc721baebfb4c5(void* thisObject, uint32_t* result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Series* this_ = static_cast<OrthancClient::Series*>(thisObject);
-*result = this_->GetHeight();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_b794f5cd3dad7d7b575dd1fd902afdd0(void* thisObject, float* result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Series* this_ = static_cast<OrthancClient::Series*>(thisObject);
-*result = this_->GetVoxelSizeX();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_8ee2e50dd9df8f66a3c1766090dd03ab(void* thisObject, float* result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Series* this_ = static_cast<OrthancClient::Series*>(thisObject);
-*result = this_->GetVoxelSizeY();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_046aed35bbe4751691f4c34cc249a61d(void* thisObject, float* result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Series* this_ = static_cast<OrthancClient::Series*>(thisObject);
-*result = this_->GetVoxelSizeZ();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_2be452e7af5bf7dfd8c5021842674497(void* thisObject, float* result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Series* this_ = static_cast<OrthancClient::Series*>(thisObject);
-*result = this_->GetSliceThickness();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_4dcc7a0fd025efba251ac6e9b701c2c5(void* thisObject, void* arg0, int32_t arg1, int64_t arg2, int64_t arg3)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Series* this_ = static_cast<OrthancClient::Series*>(thisObject);
-this_->Load3DImage(reinterpret_cast< void* >(arg0), static_cast< ::Orthanc::PixelFormat >(arg1), arg2, arg3);
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_b2601a161c24ad0a1d3586246f87452c(void* thisObject, void* arg0, int32_t arg1, int64_t arg2, int64_t arg3, float* arg4)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Series* this_ = static_cast<OrthancClient::Series*>(thisObject);
-this_->Load3DImage(reinterpret_cast< void* >(arg0), static_cast< ::Orthanc::PixelFormat >(arg1), arg2, arg3, reinterpret_cast< float* >(arg4));
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_b01c6003238eb46c8db5dc823d7ca678(void** newObject, void* arg0, const char* arg1)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          *newObject = new OrthancClient::Study(*reinterpret_cast< ::OrthancClient::OrthancConnection* >(arg0), reinterpret_cast< const char* >(arg1));
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_0147007fb99bad8cd95a139ec8795376(void* thisObject)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          delete static_cast<OrthancClient::Study*>(thisObject);
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_e65b20b7e0170b67544cd6664a4639b7(void* thisObject)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Study* this_ = static_cast<OrthancClient::Study*>(thisObject);
-this_->Reload();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_470e981b0e41f17231ba0ae6f3033321(void* thisObject, uint32_t* result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Study* this_ = static_cast<OrthancClient::Study*>(thisObject);
-*result = this_->GetSeriesCount();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_04cefd138b6ea15ad909858f2a0a8f05(void* thisObject, void** result, uint32_t arg0)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Study* this_ = static_cast<OrthancClient::Study*>(thisObject);
-*result = &this_->GetSeries(arg0);
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_aee5b1f6f0c082f2c3b0986f9f6a18c7(const void* thisObject, const char** result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          const OrthancClient::Study* this_ = static_cast<const OrthancClient::Study*>(thisObject);
-*result = this_->GetId();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_93965682bace75491413e1f0b8d5a654(const void* thisObject, const char** result, const char* arg0, const char* arg1)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          const OrthancClient::Study* this_ = static_cast<const OrthancClient::Study*>(thisObject);
-*result = this_->GetMainDicomTag(reinterpret_cast< const char* >(arg0), reinterpret_cast< const char* >(arg1));
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_6c5ad02f91b583e29cebd0bd319ce21d(void** newObject, void* arg0, const char* arg1)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          *newObject = new OrthancClient::Instance(*reinterpret_cast< ::OrthancClient::OrthancConnection* >(arg0), reinterpret_cast< const char* >(arg1));
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_4068241c44a9c1367fe0e57be523f207(void* thisObject)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          delete static_cast<OrthancClient::Instance*>(thisObject);
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_236ee8b403bc99535a8a4695c0cd45cb(const void* thisObject, const char** result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          const OrthancClient::Instance* this_ = static_cast<const OrthancClient::Instance*>(thisObject);
-*result = this_->GetId();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_2a437b7aba6bb01e81113835be8f0146(void* thisObject, int32_t arg0)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Instance* this_ = static_cast<OrthancClient::Instance*>(thisObject);
-this_->SetImageExtractionMode(static_cast< ::Orthanc::ImageExtractionMode >(arg0));
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_2bcbcb850934ae0bb4c6f0cc940e6cda(const void* thisObject, int32_t* result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          const OrthancClient::Instance* this_ = static_cast<const OrthancClient::Instance*>(thisObject);
-*result = this_->GetImageExtractionMode();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_8d415c3a78a48e7e61d9fd24e7c79484(const void* thisObject, const char** result, const char* arg0)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          const OrthancClient::Instance* this_ = static_cast<const OrthancClient::Instance*>(thisObject);
-*result = this_->GetTagAsString(reinterpret_cast< const char* >(arg0));
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_70d2f8398bbc63b5f792b69b4ad5fecb(const void* thisObject, float* result, const char* arg0)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          const OrthancClient::Instance* this_ = static_cast<const OrthancClient::Instance*>(thisObject);
-*result = this_->GetTagAsFloat(reinterpret_cast< const char* >(arg0));
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_1729a067d902771517388eedd7346b23(const void* thisObject, int32_t* result, const char* arg0)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          const OrthancClient::Instance* this_ = static_cast<const OrthancClient::Instance*>(thisObject);
-*result = this_->GetTagAsInt(reinterpret_cast< const char* >(arg0));
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_72e2aeee66cd3abd8ab7e987321c3745(void* thisObject, uint32_t* result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Instance* this_ = static_cast<OrthancClient::Instance*>(thisObject);
-*result = this_->GetWidth();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_1ea3df5a1ac1a1a687fe7325adddb6f0(void* thisObject, uint32_t* result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Instance* this_ = static_cast<OrthancClient::Instance*>(thisObject);
-*result = this_->GetHeight();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_99b4f370e4f532d8b763e2cb49db92f8(void* thisObject, uint32_t* result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Instance* this_ = static_cast<OrthancClient::Instance*>(thisObject);
-*result = this_->GetPitch();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_c41c742b68617f1c0590577a0a5ebc0c(void* thisObject, int32_t* result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Instance* this_ = static_cast<OrthancClient::Instance*>(thisObject);
-*result = this_->GetPixelFormat();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_142dd2feba0fc1d262bbd0baeb441a8b(void* thisObject, const void** result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Instance* this_ = static_cast<OrthancClient::Instance*>(thisObject);
-*result = this_->GetBuffer();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_5f5c9f81a4dff8daa6c359f1d0488fef(void* thisObject, const void** result, uint32_t arg0)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Instance* this_ = static_cast<OrthancClient::Instance*>(thisObject);
-*result = this_->GetBuffer(arg0);
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_9ca979fffd08fa256306d4e68d8b0e91(void* thisObject, uint64_t* result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Instance* this_ = static_cast<OrthancClient::Instance*>(thisObject);
-*result = this_->GetDicomSize();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_6f2d77a26edc91c28d89408dbc3c271e(void* thisObject, const void** result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Instance* this_ = static_cast<OrthancClient::Instance*>(thisObject);
-*result = this_->GetDicom();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_c0f494b80d4ff8b232df7a75baa0700a(void* thisObject)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Instance* this_ = static_cast<OrthancClient::Instance*>(thisObject);
-this_->DiscardImage();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_d604f44bd5195e082e745e9cbc164f4c(void* thisObject)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Instance* this_ = static_cast<OrthancClient::Instance*>(thisObject);
-this_->DiscardDicom();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_1710299d1c5f3b1f2b7cf3962deebbfd(void* thisObject, const char* arg0)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          OrthancClient::Instance* this_ = static_cast<OrthancClient::Instance*>(thisObject);
-this_->LoadTagContent(reinterpret_cast< const char* >(arg0));
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-      LAAW_EXPORT_DLL_API char* LAAW_CALL_CONVENTION LAAW_EXTERNC_bb55aaf772ddceaadee36f4e54136bcb(const void* thisObject, const char** result)
-      {
-        try
-        {
-          #ifdef LAAW_EXTERNC_START_FUNCTION
-          LAAW_EXTERNC_START_FUNCTION;
-          #endif
-
-          const OrthancClient::Instance* this_ = static_cast<const OrthancClient::Instance*>(thisObject);
-*result = this_->GetLoadedTagContent();
-
-          return NULL;
-        }
-        catch (::Laaw::LaawException& e)
-        {
-          return LAAW_EXTERNC_CopyString(e.What());
-        }
-        catch (...)
-        {
-          return LAAW_EXTERNC_CopyString("...");
-        }
-      }
-
-
-  LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetDescription()
-  {
-    return "Native client to the REST API of Orthanc";
-  }
-
-  LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetCompany()
-  {
-    return "University Hospital of Liege";
-  }
-
-  LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetProduct()
-  {
-    return "OrthancClient";
-  }
-
-  LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetCopyright()
-  {
-    return "(c) 2012-2015, Sebastien Jodogne, University Hospital of Liege";
-  }
-
-  LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetVersion()
-  {
-    return "0.8";
-  }
-
-  LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetFileVersion()
-  {
-    return "0.8.0.5";
-  }
-
-  LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetFullVersion()
-  {
-    return "0.8.5";
-  }
-
-  LAAW_EXPORT_DLL_API void LAAW_CALL_CONVENTION LAAW_EXTERNC_FreeString(char* str)
-  {
-    if (str != NULL)
-      free(str);
-  }
-}
--- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/OrthancCppClient.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1817 +0,0 @@
-/**
- * Laaw - Lightweight, Automated API Wrapper
- * Copyright (C) 2010-2013 Jomago - Alain Mazy, Benjamin Golinvaux,
- * Sebastien Jodogne
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-/**
- * @file
- **/
-
-#pragma once
-
-#include <stdexcept>
-#include <memory>
-#include <string>
-#include <string.h>
-
-#if defined(_WIN32)
-
-/********************************************************************
- ** This is the Windows-specific section
- ********************************************************************/
-
-#include <windows.h>
-
-/* cf. http://sourceforge.net/p/predef/wiki/Architectures/ */
-#ifdef _M_X64
-/* 64 bits target */
-#define LAAW_ORTHANC_CLIENT_CALL_CONV  __fastcall
-#define LAAW_ORTHANC_CLIENT_CALL_DECORATION(Name, StdCallSuffix) Name
-#define LAAW_ORTHANC_CLIENT_DEFAULT_PATH  "OrthancClient_Windows64.dll"
-#else
-/* 32 bits target */
-#define LAAW_ORTHANC_CLIENT_CALL_CONV  __stdcall
-#define LAAW_ORTHANC_CLIENT_CALL_DECORATION(Name, StdCallSuffix) "_" Name "@" StdCallSuffix
-#define LAAW_ORTHANC_CLIENT_DEFAULT_PATH  "OrthancClient_Windows32.dll"
-#endif
-
-#define LAAW_ORTHANC_CLIENT_HANDLE_TYPE  HINSTANCE
-#define LAAW_ORTHANC_CLIENT_HANDLE_NULL  0
-#define LAAW_ORTHANC_CLIENT_FUNCTION_TYPE  FARPROC
-#define LAAW_ORTHANC_CLIENT_LOADER(path) LoadLibraryA(path)
-#define LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle, name, decoration) GetProcAddress(handle, LAAW_ORTHANC_CLIENT_CALL_DECORATION(name, decoration))
-#define LAAW_ORTHANC_CLIENT_CLOSER(handle) FreeLibrary(handle)
-
-
-/********************************************************************
- ** This is the Linux-specific section
- ********************************************************************/
-
-#elif defined (__linux)
-
-#include <stdlib.h>
-#include <dlfcn.h>
-
-/* cf. http://sourceforge.net/p/predef/wiki/Architectures/ */
-#ifdef __amd64__
-#define LAAW_ORTHANC_CLIENT_DEFAULT_PATH  "libOrthancClient.so.0.8"
-#else
-#define LAAW_ORTHANC_CLIENT_DEFAULT_PATH  "libOrthancClient.so.0.8"
-#endif
-
-#define LAAW_ORTHANC_CLIENT_CALL_CONV
-#define LAAW_ORTHANC_CLIENT_HANDLE_TYPE  void*
-#define LAAW_ORTHANC_CLIENT_HANDLE_NULL  NULL
-#define LAAW_ORTHANC_CLIENT_FUNCTION_TYPE  intptr_t
-#define LAAW_ORTHANC_CLIENT_LOADER(path) dlopen(path, RTLD_LAZY)
-#define LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle, name, decoration) (LAAW_ORTHANC_CLIENT_FUNCTION_TYPE) dlsym(handle, name)
-#define LAAW_ORTHANC_CLIENT_CLOSER(handle) dlclose(handle)
-
-
-#else
-#error Please support your platform here
-#endif
-
-
-/********************************************************************
- ** Definition of the integer types
- ********************************************************************/
-
-#ifndef LAAW_INT8  // Only define the integer types once
-
-#if defined(__GNUC__)
-
-// Under GCC (including MinGW), the stdint.h standard header is used.
-
-#include <stdint.h>
-
-#define LAAW_INT8  int8_t
-#define LAAW_UINT8  uint8_t
-#define LAAW_INT16  int16_t
-#define LAAW_UINT16  uint16_t
-#define LAAW_INT32  int32_t
-#define LAAW_UINT32  uint32_t
-#define LAAW_INT64  int64_t
-#define LAAW_UINT64  uint64_t
-
-#elif defined(_MSC_VER)
-
-// Under Visual Studio, it is required to define the various integer
-// types by hand.
-
-#if (_MSC_VER < 1300)
-typedef signed char       LAAW_INT8;
-typedef signed short      LAAW_INT16;
-typedef signed int        LAAW_INT32;
-typedef unsigned char     LAAW_UINT8;
-typedef unsigned short    LAAW_UINT16;
-typedef unsigned int      LAAW_UINT32;
-#else
-typedef signed __int8     LAAW_INT8;
-typedef signed __int16    LAAW_INT16;
-typedef signed __int32    LAAW_INT32;
-typedef unsigned __int8   LAAW_UINT8;
-typedef unsigned __int16  LAAW_UINT16;
-typedef unsigned __int32  LAAW_UINT32;
-#endif
-
-typedef signed __int64   LAAW_INT64;
-typedef unsigned __int64 LAAW_UINT64;
-
-#else
-#error "Please support your compiler here"
-#endif
-
-#endif
-
-
-
-
-
-/********************************************************************
- ** This is a shared section between Windows and Linux
- ********************************************************************/
-
-namespace OrthancClient { 
-/**
- * @brief Exception class that is thrown by the functions of this shared library.
- **/
-class OrthancClientException : public std::exception
-  {
-  private:
-    std::string message_;
-
-  public:
-    /**
-     * @brief Constructs an exception.
-     * @param message The error message.
-     **/
-    OrthancClientException(std::string message) : message_(message) 
-    {
-    }
-
-    ~OrthancClientException() throw()
-    {
-    }
-
-    /**
-     * @brief Get the error message associated with this exception.
-     * @returns The error message.
-     **/
-    const std::string& What() const throw()
-    {
-      return message_; 
-    }
-};
-}
-
-
-namespace OrthancClient { namespace Internals { 
-/**
- * This internal class implements a Singleton design pattern that will
- * store a reference to the shared library handle, together with a
- * pointer to each function in the shared library.
- **/
-class Library
-  {
-  private:
-    LAAW_ORTHANC_CLIENT_HANDLE_TYPE  handle_;
-    LAAW_ORTHANC_CLIENT_FUNCTION_TYPE  functionsIndex_[63 + 1];
-
-
-
-    void Load(const char* sharedLibraryPath)
-    {
-
-      if (handle_ != LAAW_ORTHANC_CLIENT_HANDLE_NULL)
-      {
-        // Do nothing if the library is already loaded
-        return;
-      }
-
-      /* Setup the path to the default shared library if not provided */
-      if (sharedLibraryPath == NULL)
-      {
-        sharedLibraryPath = LAAW_ORTHANC_CLIENT_DEFAULT_PATH;
-      }
-
-      /* Load the shared library */
-      handle_ = LAAW_ORTHANC_CLIENT_LOADER(sharedLibraryPath);
-
-
-      if (handle_ == LAAW_ORTHANC_CLIENT_HANDLE_NULL)
-      {
-        throw ::OrthancClient::OrthancClientException("Error loading shared library");
-      }
-
-      LoadFunctions();
-    }
-
-    inline void LoadFunctions();
-
-    void FreeString(char* str)
-    {
-      typedef void (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (char*);
-      Function function = (Function) GetFunction(63);
-      function(str);
-    }
-
-    Library()
-    {
-      handle_ = LAAW_ORTHANC_CLIENT_HANDLE_NULL;
-    }
-
-    ~Library()
-    {
-      Finalize();
-    }
-
-  public:
-    LAAW_ORTHANC_CLIENT_FUNCTION_TYPE  GetFunction(unsigned int index)
-    {
-      /**
-       * If the library has not been manually initialized by a call to
-       * ::OrthancClient::Initialize(), it is loaded from
-       * the default location (lazy initialization).
-       **/
-      if (handle_ == NULL)
-      {
-        Load(NULL);
-      }
-
-      return functionsIndex_[index];
-    }
-
-    void ThrowExceptionIfNeeded(char* message)
-    {
-      if (message != NULL)
-      {
-        std::string tmp(message);
-        FreeString(message);
-        throw ::OrthancClient::OrthancClientException(tmp);
-      }
-    }
-
-    static inline Library& GetInstance()
-    {
-      /**
-       * This function defines a "static variable" inside a "static
-       * inline method" of a class.  This ensures that a single
-       * instance of this variable will be used across all the
-       * compilation modules of the software.
-       * http://stackoverflow.com/a/1389403/881731
-       **/
-
-      static Library singleton;
-      return singleton;
-    }
-
-    static void Initialize(const char* sharedLibraryPath)
-    {
-      GetInstance().Load(sharedLibraryPath);
-    }
-
-    void Finalize()
-    {
-      if (handle_ != LAAW_ORTHANC_CLIENT_HANDLE_NULL)
-      {
-#if 0
-        /**
-         * Do not explicitly unload the shared library, as it might
-         * interfere with the destruction of static objects declared
-         * inside the library (e.g. this is the case of gflags that is
-         * internally used by googlelog).
-         **/
-        LAAW_ORTHANC_CLIENT_CLOSER(handle_);
-#endif
-        handle_ = LAAW_ORTHANC_CLIENT_HANDLE_NULL;
-      }
-    }
-};
-}}
-
-
-/*!
- * \addtogroup Global Global definitions.
- * @{
- * @}
- */
-
-
-namespace OrthancClient { 
-/*!
- * \addtogroup Initialization Initialization of the shared library.
- * @{
- */
-
-/**
- * @brief Manually initialize the shared library, using the default library name.
- * 
- * Call this method before using the library to ensure correct
- * behaviour in multi-threaded applications.  This method is also
- * useful to control the time at which the shared library is
- * loaded (e.g. for real-time applications).
- **/
-inline void Initialize()
-{
-  ::OrthancClient::Internals::Library::Initialize(NULL);
-}
-
-/**
- * @brief Manually initialize the shared library.
- * 
- * Call this method before using the library to ensure correct
- * behaviour in multi-threaded applications.  This method is also
- * useful to control the time at which the shared library is
- * loaded (e.g. for real-time applications).
- *
- * @param sharedLibraryPath The path to the shared library that
- * contains the module.
- **/
-inline void Initialize(const std::string& sharedLibraryPath)
-{
-  ::OrthancClient::Internals::Library::Initialize(sharedLibraryPath.c_str());
-}
-
-/**
- * @brief Manually finalize the shared library.
- * 
- * Calling explicitly this function is not mandatory. It is useful to
- * force the release of the resources acquired by the shared library,
- * or to manually control the order in which the global variables get
- * deleted.
- **/
-inline void Finalize()
-{
-  ::OrthancClient::Internals::Library::GetInstance().Finalize();
-}
-
-
-/**
- * @}
- */
-}
-
-
-namespace OrthancClient { namespace Internals { 
-inline void Library::LoadFunctions()
-{
-  typedef const char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) ();
-  Function getVersion = (Function) LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_GetVersion", "0");
-  if (getVersion == NULL)
-  {
-    throw ::OrthancClient::OrthancClientException("Unable to get the library version");
-  }
-
-  /**
-   * It is assumed that the API does not change when the revision
-   * number (MAJOR.MINOR.REVISION) changes.
-   **/
-  if (strcmp(getVersion(), "0.8"))
-  {
-    throw ::OrthancClient::OrthancClientException("Mismatch between the C++ header and the library version");
-  }
-
-  functionsIndex_[63] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_FreeString", "4");
-  functionsIndex_[3] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_557aee7b61817292a0f31269d3c35db7", "8");
-  functionsIndex_[4] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_0b8dff0ce67f10954a49b059e348837e", "8");
-  functionsIndex_[5] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_e05097c153f676e5a5ee54dcfc78256f", "4");
-  functionsIndex_[6] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_e840242bf58d17d3c1d722da09ce88e0", "8");
-  functionsIndex_[7] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_c9af31433001b5dfc012a552dc6d0050", "8");
-  functionsIndex_[8] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_3fba4d6b818180a44cd1cae6046334dc", "12");
-  functionsIndex_[9] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_aeb20dc75b9246188db857317e5e0ce7", "8");
-  functionsIndex_[10] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_62689803d9871e4d9c51a648640b320b", "8");
-  functionsIndex_[11] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_2fb64c9e5a67eccd413b0e913469a421", "16");
-  functionsIndex_[0] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_1f1acb322ea4d0aad65172824607673c", "8");
-  functionsIndex_[1] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_f3fd272e4636f6a531aabb72ee01cd5b", "16");
-  functionsIndex_[2] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_12d3de0a96e9efb11136a9811bb9ed38", "4");
-  functionsIndex_[14] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_f756172daf04516eec3a566adabb4335", "4");
-  functionsIndex_[15] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_ddb68763ec902a97d579666a73a20118", "8");
-  functionsIndex_[16] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_fba3c68b4be7558dbc65f7ce1ab57d63", "12");
-  functionsIndex_[17] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_b4ca99d958f843493e58d1ef967340e1", "8");
-  functionsIndex_[18] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_78d5cc76d282437b6f93ec3b82c35701", "16");
-  functionsIndex_[12] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_6cf0d7268667f9b0aa4511bacf184919", "12");
-  functionsIndex_[13] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_7d81cd502ee27e859735d0ea7112b5a1", "4");
-  functionsIndex_[21] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_48a2a1a9d68c047e22bfba23014643d2", "4");
-  functionsIndex_[22] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_852bf8296ca21c5fde5ec565cc10721d", "8");
-  functionsIndex_[23] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_efd04574e0779faa83df1f2d8f9888db", "12");
-  functionsIndex_[24] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_736247ff5e8036dac38163da6f666ed5", "8");
-  functionsIndex_[25] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_d82d2598a7a73f4b6fcc0c09c25b08ca", "8");
-  functionsIndex_[26] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_88134b978f9acb2aecdadf54aeab3c64", "16");
-  functionsIndex_[27] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_152cb1b704c053d24b0dab7461ba6ea3", "8");
-  functionsIndex_[28] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_eee03f337ec81d9f1783cd41e5238757", "8");
-  functionsIndex_[29] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_006f08237bd7611636fc721baebfb4c5", "8");
-  functionsIndex_[30] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_b794f5cd3dad7d7b575dd1fd902afdd0", "8");
-  functionsIndex_[31] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_8ee2e50dd9df8f66a3c1766090dd03ab", "8");
-  functionsIndex_[32] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_046aed35bbe4751691f4c34cc249a61d", "8");
-  functionsIndex_[33] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_2be452e7af5bf7dfd8c5021842674497", "8");
-  functionsIndex_[34] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_4dcc7a0fd025efba251ac6e9b701c2c5", "28");
-  functionsIndex_[35] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_b2601a161c24ad0a1d3586246f87452c", "32");
-  functionsIndex_[19] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_193599b9e345384fcdfcd47c29c55342", "12");
-  functionsIndex_[20] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_7c97f17063a357d38c5fab1136ad12a0", "4");
-  functionsIndex_[38] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_e65b20b7e0170b67544cd6664a4639b7", "4");
-  functionsIndex_[39] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_470e981b0e41f17231ba0ae6f3033321", "8");
-  functionsIndex_[40] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_04cefd138b6ea15ad909858f2a0a8f05", "12");
-  functionsIndex_[41] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_aee5b1f6f0c082f2c3b0986f9f6a18c7", "8");
-  functionsIndex_[42] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_93965682bace75491413e1f0b8d5a654", "16");
-  functionsIndex_[36] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_b01c6003238eb46c8db5dc823d7ca678", "12");
-  functionsIndex_[37] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_0147007fb99bad8cd95a139ec8795376", "4");
-  functionsIndex_[45] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_236ee8b403bc99535a8a4695c0cd45cb", "8");
-  functionsIndex_[46] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_2a437b7aba6bb01e81113835be8f0146", "8");
-  functionsIndex_[47] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_2bcbcb850934ae0bb4c6f0cc940e6cda", "8");
-  functionsIndex_[48] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_8d415c3a78a48e7e61d9fd24e7c79484", "12");
-  functionsIndex_[49] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_70d2f8398bbc63b5f792b69b4ad5fecb", "12");
-  functionsIndex_[50] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_1729a067d902771517388eedd7346b23", "12");
-  functionsIndex_[51] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_72e2aeee66cd3abd8ab7e987321c3745", "8");
-  functionsIndex_[52] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_1ea3df5a1ac1a1a687fe7325adddb6f0", "8");
-  functionsIndex_[53] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_99b4f370e4f532d8b763e2cb49db92f8", "8");
-  functionsIndex_[54] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_c41c742b68617f1c0590577a0a5ebc0c", "8");
-  functionsIndex_[55] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_142dd2feba0fc1d262bbd0baeb441a8b", "8");
-  functionsIndex_[56] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_5f5c9f81a4dff8daa6c359f1d0488fef", "12");
-  functionsIndex_[57] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_9ca979fffd08fa256306d4e68d8b0e91", "8");
-  functionsIndex_[58] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_6f2d77a26edc91c28d89408dbc3c271e", "8");
-  functionsIndex_[59] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_c0f494b80d4ff8b232df7a75baa0700a", "4");
-  functionsIndex_[60] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_d604f44bd5195e082e745e9cbc164f4c", "4");
-  functionsIndex_[61] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_1710299d1c5f3b1f2b7cf3962deebbfd", "8");
-  functionsIndex_[62] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_bb55aaf772ddceaadee36f4e54136bcb", "8");
-  functionsIndex_[43] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_6c5ad02f91b583e29cebd0bd319ce21d", "12");
-  functionsIndex_[44] = LAAW_ORTHANC_CLIENT_GET_FUNCTION(handle_, "LAAW_EXTERNC_4068241c44a9c1367fe0e57be523f207", "4");
-  
-  /* Check whether the functions were properly loaded */
-  for (unsigned int i = 0; i <= 63; i++)
-  {
-    if (functionsIndex_[i] == (LAAW_ORTHANC_CLIENT_FUNCTION_TYPE) NULL)
-    {
-      throw ::OrthancClient::OrthancClientException("Unable to load the functions of the shared library");
-    }
-  }
-}
-}}
-namespace OrthancClient
-{
-  class OrthancConnection;
-}
-
-namespace OrthancClient
-{
-  class Patient;
-}
-
-namespace OrthancClient
-{
-  class Series;
-}
-
-namespace OrthancClient
-{
-  class Study;
-}
-
-namespace OrthancClient
-{
-  class Instance;
-}
-
-namespace Orthanc
-{
-  /**
-  * @brief The memory layout of the pixels (resp. voxels) of a 2D (resp. 3D) image.
-  *
-  * The memory layout of the pixels (resp. voxels) of a 2D (resp. 3D) image.
-  *
-  * @ingroup Global
-  **/
-  enum PixelFormat
-  {
-    /**
-    * @brief Graylevel, signed 16bpp image.
-    *
-    * The image is graylevel. Each pixel is signed and stored in two bytes.
-    *
-    **/
-    PixelFormat_SignedGrayscale16 = 5,
-    /**
-    * @brief Color image in RGB24 format.
-    *
-    * This format describes a color image. The pixels are stored in 3 consecutive bytes. The memory layout is RGB.
-    *
-    **/
-    PixelFormat_RGB24 = 1,
-    /**
-    * @brief Color image in RGBA32 format.
-    *
-    * This format describes a color image. The pixels are stored in 4 consecutive bytes. The memory layout is RGBA.
-    *
-    **/
-    PixelFormat_RGBA32 = 2,
-    /**
-    * @brief Graylevel 8bpp image.
-    *
-    * The image is graylevel. Each pixel is unsigned and stored in one byte.
-    *
-    **/
-    PixelFormat_Grayscale8 = 3,
-    /**
-    * @brief Graylevel, unsigned 16bpp image.
-    *
-    * The image is graylevel. Each pixel is unsigned and stored in two bytes.
-    *
-    **/
-    PixelFormat_Grayscale16 = 4
-  };
-}
-
-namespace Orthanc
-{
-  /**
-  * @brief The extraction mode specifies the way the values of the pixels are scaled when downloading a 2D image.
-  *
-  * The extraction mode specifies the way the values of the pixels are scaled when downloading a 2D image.
-  *
-  * @ingroup Global
-  **/
-  enum ImageExtractionMode
-  {
-    /**
-    * @brief Truncation to the [-32768, 32767] range.
-    *
-    * Truncation to the [-32768, 32767] range.
-    *
-    **/
-    ImageExtractionMode_Int16 = 4,
-    /**
-    * @brief Rescaled to 8bpp.
-    *
-    * The minimum value of the image is set to 0, and its maximum value is set to 255.
-    *
-    **/
-    ImageExtractionMode_Preview = 1,
-    /**
-    * @brief Truncation to the [0, 255] range.
-    *
-    * Truncation to the [0, 255] range.
-    *
-    **/
-    ImageExtractionMode_UInt8 = 2,
-    /**
-    * @brief Truncation to the [0, 65535] range.
-    *
-    * Truncation to the [0, 65535] range.
-    *
-    **/
-    ImageExtractionMode_UInt16 = 3
-  };
-}
-
-namespace OrthancClient
-{
-  /**
-  * @brief Connection to an instance of %Orthanc.
-  *
-  * This class encapsulates a connection to a remote instance of %Orthanc through its REST API.
-  *
-  **/
-  class OrthancConnection
-  {
-    friend class ::OrthancClient::Patient;
-    friend class ::OrthancClient::Series;
-    friend class ::OrthancClient::Study;
-    friend class ::OrthancClient::Instance;
-  private:
-    bool isReference_;
-    OrthancConnection& operator= (const OrthancConnection&); // Assignment is forbidden
-    void* pimpl_;
-    OrthancConnection(void* pimpl) : isReference_(true), pimpl_(pimpl) {}
-  public:
-    /**
-     * @brief Construct a new reference to this object.
-     *
-     * Construct a new reference to this object. Pay attention to the fact that when the referenced object is deleted, the content of this object will be invalid.
-     *
-     * @param other The original object.
-     **/
-    OrthancConnection(const OrthancConnection& other) : isReference_(true), pimpl_(other.pimpl_) { }
-    inline OrthancConnection(const ::std::string& orthancUrl);
-    inline OrthancConnection(const ::std::string& orthancUrl, const ::std::string& username, const ::std::string& password);
-    inline ~OrthancConnection();
-    inline LAAW_UINT32 GetThreadCount() const;
-    inline void SetThreadCount(LAAW_UINT32 threadCount);
-    inline void Reload();
-    inline ::std::string GetOrthancUrl() const;
-    inline LAAW_UINT32 GetPatientCount();
-    inline ::OrthancClient::Patient GetPatient(LAAW_UINT32 index);
-    inline void DeletePatient(LAAW_UINT32 index);
-    inline void StoreFile(const ::std::string& filename);
-    inline void Store(const void* dicom, LAAW_UINT64 size);
-  };
-}
-
-namespace OrthancClient
-{
-  /**
-  * @brief Connection to a patient stored in %Orthanc.
-  *
-  * This class encapsulates a connection to a patient from a remote instance of %Orthanc.
-  *
-  **/
-  class Patient
-  {
-    friend class ::OrthancClient::OrthancConnection;
-    friend class ::OrthancClient::Series;
-    friend class ::OrthancClient::Study;
-    friend class ::OrthancClient::Instance;
-  private:
-    bool isReference_;
-    Patient& operator= (const Patient&); // Assignment is forbidden
-    void* pimpl_;
-    Patient(void* pimpl) : isReference_(true), pimpl_(pimpl) {}
-  public:
-    /**
-     * @brief Construct a new reference to this object.
-     *
-     * Construct a new reference to this object. Pay attention to the fact that when the referenced object is deleted, the content of this object will be invalid.
-     *
-     * @param other The original object.
-     **/
-    Patient(const Patient& other) : isReference_(true), pimpl_(other.pimpl_) { }
-    inline Patient(::OrthancClient::OrthancConnection& connection, const ::std::string& id);
-    inline ~Patient();
-    inline void Reload();
-    inline LAAW_UINT32 GetStudyCount();
-    inline ::OrthancClient::Study GetStudy(LAAW_UINT32 index);
-    inline ::std::string GetId() const;
-    inline ::std::string GetMainDicomTag(const ::std::string& tag, const ::std::string& defaultValue) const;
-  };
-}
-
-namespace OrthancClient
-{
-  /**
-  * @brief Connection to a series stored in %Orthanc.
-  *
-  * This class encapsulates a connection to a series from a remote instance of %Orthanc.
-  *
-  **/
-  class Series
-  {
-    friend class ::OrthancClient::OrthancConnection;
-    friend class ::OrthancClient::Patient;
-    friend class ::OrthancClient::Study;
-    friend class ::OrthancClient::Instance;
-  private:
-    bool isReference_;
-    Series& operator= (const Series&); // Assignment is forbidden
-    void* pimpl_;
-    Series(void* pimpl) : isReference_(true), pimpl_(pimpl) {}
-  public:
-    /**
-     * @brief Construct a new reference to this object.
-     *
-     * Construct a new reference to this object. Pay attention to the fact that when the referenced object is deleted, the content of this object will be invalid.
-     *
-     * @param other The original object.
-     **/
-    Series(const Series& other) : isReference_(true), pimpl_(other.pimpl_) { }
-    inline Series(::OrthancClient::OrthancConnection& connection, const ::std::string& id);
-    inline ~Series();
-    inline void Reload();
-    inline LAAW_UINT32 GetInstanceCount();
-    inline ::OrthancClient::Instance GetInstance(LAAW_UINT32 index);
-    inline ::std::string GetId() const;
-    inline ::std::string GetUrl() const;
-    inline ::std::string GetMainDicomTag(const ::std::string& tag, const ::std::string& defaultValue) const;
-    inline bool Is3DImage();
-    inline LAAW_UINT32 GetWidth();
-    inline LAAW_UINT32 GetHeight();
-    inline float GetVoxelSizeX();
-    inline float GetVoxelSizeY();
-    inline float GetVoxelSizeZ();
-    inline float GetSliceThickness();
-    inline void Load3DImage(void* target, ::Orthanc::PixelFormat format, LAAW_INT64 lineStride, LAAW_INT64 stackStride);
-    inline void Load3DImage(void* target, ::Orthanc::PixelFormat format, LAAW_INT64 lineStride, LAAW_INT64 stackStride, float progress[]);
-  };
-}
-
-namespace OrthancClient
-{
-  /**
-  * @brief Connection to a study stored in %Orthanc.
-  *
-  * This class encapsulates a connection to a study from a remote instance of %Orthanc.
-  *
-  **/
-  class Study
-  {
-    friend class ::OrthancClient::OrthancConnection;
-    friend class ::OrthancClient::Patient;
-    friend class ::OrthancClient::Series;
-    friend class ::OrthancClient::Instance;
-  private:
-    bool isReference_;
-    Study& operator= (const Study&); // Assignment is forbidden
-    void* pimpl_;
-    Study(void* pimpl) : isReference_(true), pimpl_(pimpl) {}
-  public:
-    /**
-     * @brief Construct a new reference to this object.
-     *
-     * Construct a new reference to this object. Pay attention to the fact that when the referenced object is deleted, the content of this object will be invalid.
-     *
-     * @param other The original object.
-     **/
-    Study(const Study& other) : isReference_(true), pimpl_(other.pimpl_) { }
-    inline Study(::OrthancClient::OrthancConnection& connection, const ::std::string& id);
-    inline ~Study();
-    inline void Reload();
-    inline LAAW_UINT32 GetSeriesCount();
-    inline ::OrthancClient::Series GetSeries(LAAW_UINT32 index);
-    inline ::std::string GetId() const;
-    inline ::std::string GetMainDicomTag(const ::std::string& tag, const ::std::string& defaultValue) const;
-  };
-}
-
-namespace OrthancClient
-{
-  /**
-  * @brief Connection to an instance stored in %Orthanc.
-  *
-  * This class encapsulates a connection to an image instance from a remote instance of %Orthanc.
-  *
-  **/
-  class Instance
-  {
-    friend class ::OrthancClient::OrthancConnection;
-    friend class ::OrthancClient::Patient;
-    friend class ::OrthancClient::Series;
-    friend class ::OrthancClient::Study;
-  private:
-    bool isReference_;
-    Instance& operator= (const Instance&); // Assignment is forbidden
-    void* pimpl_;
-    Instance(void* pimpl) : isReference_(true), pimpl_(pimpl) {}
-  public:
-    /**
-     * @brief Construct a new reference to this object.
-     *
-     * Construct a new reference to this object. Pay attention to the fact that when the referenced object is deleted, the content of this object will be invalid.
-     *
-     * @param other The original object.
-     **/
-    Instance(const Instance& other) : isReference_(true), pimpl_(other.pimpl_) { }
-    inline Instance(::OrthancClient::OrthancConnection& connection, const ::std::string& id);
-    inline ~Instance();
-    inline ::std::string GetId() const;
-    inline void SetImageExtractionMode(::Orthanc::ImageExtractionMode mode);
-    inline ::Orthanc::ImageExtractionMode GetImageExtractionMode() const;
-    inline ::std::string GetTagAsString(const ::std::string& tag) const;
-    inline float GetTagAsFloat(const ::std::string& tag) const;
-    inline LAAW_INT32 GetTagAsInt(const ::std::string& tag) const;
-    inline LAAW_UINT32 GetWidth();
-    inline LAAW_UINT32 GetHeight();
-    inline LAAW_UINT32 GetPitch();
-    inline ::Orthanc::PixelFormat GetPixelFormat();
-    inline const void* GetBuffer();
-    inline const void* GetBuffer(LAAW_UINT32 y);
-    inline LAAW_UINT64 GetDicomSize();
-    inline const void* GetDicom();
-    inline void DiscardImage();
-    inline void DiscardDicom();
-    inline void LoadTagContent(const ::std::string& path);
-    inline ::std::string GetLoadedTagContent() const;
-  };
-}
-
-namespace OrthancClient
-{
-  /**
-  * @brief Create a connection to an instance of %Orthanc.
-  *
-  * Create a connection to an instance of %Orthanc.
-  *
-  * @param orthancUrl URL to which the REST API of %Orthanc is listening.
-  **/
-  inline OrthancConnection::OrthancConnection(const ::std::string& orthancUrl)
-  {
-    isReference_ = false;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void**, const char*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(0);
-    char* error = function(&pimpl_, orthancUrl.c_str());
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-  /**
-  * @brief Create a connection to an instance of %Orthanc, with authentication.
-  *
-  * Create a connection to an instance of %Orthanc, with authentication.
-  *
-  * @param orthancUrl URL to which the REST API of %Orthanc is listening.
-  * @param username The username.
-  * @param password The password.
-  **/
-  inline OrthancConnection::OrthancConnection(const ::std::string& orthancUrl, const ::std::string& username, const ::std::string& password)
-  {
-    isReference_ = false;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void**, const char*, const char*, const char*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(1);
-    char* error = function(&pimpl_, orthancUrl.c_str(), username.c_str(), password.c_str());
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-  /**
-  * @brief Destructs the object.
-  *
-  * Destructs the object.
-  *
-  **/
-  inline OrthancConnection::~OrthancConnection()
-  {
-    if (isReference_) return;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(2);
-    char* error = function(pimpl_);
-    error = error;  // Remove warning about unused variable
-  }
-  /**
-  * @brief Returns the number of threads for this connection.
-  *
-  * Returns the number of simultaneous connections that are used when downloading information from this instance of %Orthanc.
-  *
-  * @return The number of threads.
-  **/
-  inline LAAW_UINT32 OrthancConnection::GetThreadCount() const
-  {
-    LAAW_UINT32 result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, LAAW_UINT32*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(3);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return result_;
-  }
-  /**
-  * @brief Sets the number of threads for this connection.
-  *
-  * Sets the number of simultaneous connections that are used when downloading information from this instance of %Orthanc.
-  *
-  * @param threadCount The number of threads.
-  **/
-  inline void OrthancConnection::SetThreadCount(LAAW_UINT32 threadCount)
-  {
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(4);
-    char* error = function(pimpl_, threadCount);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-  /**
-  * @brief Reload the list of the patients.
-  *
-  * This method will reload the list of the patients from the remote instance of %Orthanc. Pay attention to the fact that the patients that have been previously returned by GetPatient() will be invalidated.
-  *
-  **/
-  inline void OrthancConnection::Reload()
-  {
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(5);
-    char* error = function(pimpl_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-  /**
-  * @brief Returns the URL of this instance of %Orthanc.
-  *
-  * Returns the URL of the remote %Orthanc instance to which this object is connected.
-  *
-  * @return The URL.
-  **/
-  inline ::std::string OrthancConnection::GetOrthancUrl() const
-  {
-    const char* result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(6);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return std::string(result_);
-  }
-  /**
-  * @brief Returns the number of patients.
-  *
-  * Returns the number of patients that are stored in the remote instance of %Orthanc.
-  *
-  * @return The number of patients.
-  **/
-  inline LAAW_UINT32 OrthancConnection::GetPatientCount()
-  {
-    LAAW_UINT32 result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(7);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return result_;
-  }
-  /**
-  * @brief Get some patient.
-  *
-  * This method will return an object that contains information about some patient. The patients are indexed by a number between 0 (inclusive) and the result of GetPatientCount() (exclusive).
-  *
-  * @param index The index of the patient of interest.
-  * @return The patient.
-  **/
-  inline ::OrthancClient::Patient OrthancConnection::GetPatient(LAAW_UINT32 index)
-  {
-    void* result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, void**, LAAW_UINT32);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(8);
-    char* error = function(pimpl_, &result_, index);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return ::OrthancClient::Patient(result_);
-  }
-  /**
-  * @brief Delete some patient.
-  *
-  * Delete some patient from the remote instance of %Orthanc. Pay attention to the fact that the patients that have been previously returned by GetPatient() will be invalidated.
-  *
-  * @param index The index of the patient of interest.
-  * @return The patient.
-  **/
-  inline void OrthancConnection::DeletePatient(LAAW_UINT32 index)
-  {
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(9);
-    char* error = function(pimpl_, index);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-  /**
-  * @brief Send a DICOM file.
-  *
-  * This method will store a DICOM file in the remote instance of %Orthanc. Pay attention to the fact that the patients that have been previously returned by GetPatient() will be invalidated.
-  *
-  * @param filename Path to the DICOM file
-  **/
-  inline void OrthancConnection::StoreFile(const ::std::string& filename)
-  {
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, const char*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(10);
-    char* error = function(pimpl_, filename.c_str());
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-  /**
-  * @brief Send a DICOM file that is contained inside a memory buffer.
-  *
-  * This method will store a DICOM file in the remote instance of %Orthanc. Pay attention to the fact that the patients that have been previously returned by GetPatient() will be invalidated.
-  *
-  * @param dicom The memory buffer containing the DICOM file.
-  * @param size The size of the DICOM file.
-  **/
-  inline void OrthancConnection::Store(const void* dicom, LAAW_UINT64 size)
-  {
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, const void*, LAAW_UINT64);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(11);
-    char* error = function(pimpl_, dicom, size);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-}
-
-namespace OrthancClient
-{
-  /**
-  * @brief Create a connection to some patient.
-  *
-  * Create a connection to some patient.
-  *
-  * @param connection The remote instance of %Orthanc.
-  * @param id The %Orthanc identifier of the patient.
-  **/
-  inline Patient::Patient(::OrthancClient::OrthancConnection& connection, const ::std::string& id)
-  {
-    isReference_ = false;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void**, void*, const char*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(12);
-    char* error = function(&pimpl_, connection.pimpl_, id.c_str());
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-  /**
-  * @brief Destructs the object.
-  *
-  * Destructs the object.
-  *
-  **/
-  inline Patient::~Patient()
-  {
-    if (isReference_) return;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(13);
-    char* error = function(pimpl_);
-    error = error;  // Remove warning about unused variable
-  }
-  /**
-  * @brief Reload the studies of this patient.
-  *
-  * This method will reload the list of the studies of this patient. Pay attention to the fact that the studies that have been previously returned by GetStudy() will be invalidated.
-  *
-  **/
-  inline void Patient::Reload()
-  {
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(14);
-    char* error = function(pimpl_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-  /**
-  * @brief Return the number of studies for this patient.
-  *
-  * Return the number of studies for this patient.
-  *
-  * @return The number of studies.
-  **/
-  inline LAAW_UINT32 Patient::GetStudyCount()
-  {
-    LAAW_UINT32 result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(15);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return result_;
-  }
-  /**
-  * @brief Get some study of this patient.
-  *
-  * This method will return an object that contains information about some study. The studies are indexed by a number between 0 (inclusive) and the result of GetStudyCount() (exclusive).
-  *
-  * @param index The index of the study of interest.
-  * @return The study.
-  **/
-  inline ::OrthancClient::Study Patient::GetStudy(LAAW_UINT32 index)
-  {
-    void* result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, void**, LAAW_UINT32);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(16);
-    char* error = function(pimpl_, &result_, index);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return ::OrthancClient::Study(result_);
-  }
-  /**
-  * @brief Get the %Orthanc identifier of this patient.
-  *
-  * Get the %Orthanc identifier of this patient.
-  *
-  * @return The identifier.
-  **/
-  inline ::std::string Patient::GetId() const
-  {
-    const char* result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(17);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return std::string(result_);
-  }
-  /**
-  * @brief Get the value of one of the main DICOM tags for this patient.
-  *
-  * Get the value of one of the main DICOM tags for this patient.
-  *
-  * @param tag The name of the tag of interest ("PatientName", "PatientID", "PatientSex" or "PatientBirthDate").
-  * @param defaultValue The default value to be returned if this tag does not exist.
-  * @return The value of the tag.
-  **/
-  inline ::std::string Patient::GetMainDicomTag(const ::std::string& tag, const ::std::string& defaultValue) const
-  {
-    const char* result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**, const char*, const char*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(18);
-    char* error = function(pimpl_, &result_, tag.c_str(), defaultValue.c_str());
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return std::string(result_);
-  }
-}
-
-namespace OrthancClient
-{
-  /**
-  * @brief Create a connection to some series.
-  *
-  * Create a connection to some series.
-  *
-  * @param connection The remote instance of %Orthanc.
-  * @param id The %Orthanc identifier of the series.
-  **/
-  inline Series::Series(::OrthancClient::OrthancConnection& connection, const ::std::string& id)
-  {
-    isReference_ = false;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void**, void*, const char*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(19);
-    char* error = function(&pimpl_, connection.pimpl_, id.c_str());
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-  /**
-  * @brief Destructs the object.
-  *
-  * Destructs the object.
-  *
-  **/
-  inline Series::~Series()
-  {
-    if (isReference_) return;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(20);
-    char* error = function(pimpl_);
-    error = error;  // Remove warning about unused variable
-  }
-  /**
-  * @brief Reload the instances of this series.
-  *
-  * This method will reload the list of the instances of this series. Pay attention to the fact that the instances that have been previously returned by GetInstance() will be invalidated.
-  *
-  **/
-  inline void Series::Reload()
-  {
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(21);
-    char* error = function(pimpl_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-  /**
-  * @brief Return the number of instances for this series.
-  *
-  * Return the number of instances for this series.
-  *
-  * @return The number of instances.
-  **/
-  inline LAAW_UINT32 Series::GetInstanceCount()
-  {
-    LAAW_UINT32 result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(22);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return result_;
-  }
-  /**
-  * @brief Get some instance of this series.
-  *
-  * This method will return an object that contains information about some instance. The instances are indexed by a number between 0 (inclusive) and the result of GetInstanceCount() (exclusive).
-  *
-  * @param index The index of the instance of interest.
-  * @return The instance.
-  **/
-  inline ::OrthancClient::Instance Series::GetInstance(LAAW_UINT32 index)
-  {
-    void* result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, void**, LAAW_UINT32);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(23);
-    char* error = function(pimpl_, &result_, index);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return ::OrthancClient::Instance(result_);
-  }
-  /**
-  * @brief Get the %Orthanc identifier of this series.
-  *
-  * Get the %Orthanc identifier of this series.
-  *
-  * @return The identifier.
-  **/
-  inline ::std::string Series::GetId() const
-  {
-    const char* result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(24);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return std::string(result_);
-  }
-  /**
-  * @brief Returns the URL to this series.
-  *
-  * Returns the URL to this series.
-  *
-  * @return The URL.
-  **/
-  inline ::std::string Series::GetUrl() const
-  {
-    const char* result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(25);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return std::string(result_);
-  }
-  /**
-  * @brief Get the value of one of the main DICOM tags for this series.
-  *
-  * Get the value of one of the main DICOM tags for this series.
-  *
-  * @param tag The name of the tag of interest ("Modality", "Manufacturer", "SeriesDate", "SeriesDescription", "SeriesInstanceUID"...).
-  * @param defaultValue The default value to be returned if this tag does not exist.
-  * @return The value of the tag.
-  **/
-  inline ::std::string Series::GetMainDicomTag(const ::std::string& tag, const ::std::string& defaultValue) const
-  {
-    const char* result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**, const char*, const char*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(26);
-    char* error = function(pimpl_, &result_, tag.c_str(), defaultValue.c_str());
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return std::string(result_);
-  }
-  /**
-  * @brief Test whether this series encodes a 3D image that can be downloaded from %Orthanc.
-  *
-  * Test whether this series encodes a 3D image that can be downloaded from %Orthanc.
-  *
-  * @return "true" if and only if this is a 3D image.
-  **/
-  inline bool Series::Is3DImage()
-  {
-    LAAW_INT32 result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_INT32*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(27);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return result_ != 0;
-  }
-  /**
-  * @brief Get the width of the 3D image.
-  *
-  * Get the width of the 3D image (i.e. along the X-axis). This call is only valid if this series corresponds to a 3D image.
-  *
-  * @return The width.
-  **/
-  inline LAAW_UINT32 Series::GetWidth()
-  {
-    LAAW_UINT32 result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(28);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return result_;
-  }
-  /**
-  * @brief Get the height of the 3D image.
-  *
-  * Get the height of the 3D image (i.e. along the Y-axis). This call is only valid if this series corresponds to a 3D image.
-  *
-  * @return The height.
-  **/
-  inline LAAW_UINT32 Series::GetHeight()
-  {
-    LAAW_UINT32 result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(29);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return result_;
-  }
-  /**
-  * @brief Get the physical size of a voxel along the X-axis.
-  *
-  * Get the physical size of a voxel along the X-axis. This call is only valid if this series corresponds to a 3D image.
-  *
-  * @return The voxel size.
-  **/
-  inline float Series::GetVoxelSizeX()
-  {
-    float result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, float*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(30);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return result_;
-  }
-  /**
-  * @brief Get the physical size of a voxel along the Y-axis.
-  *
-  * Get the physical size of a voxel along the Y-axis. This call is only valid if this series corresponds to a 3D image.
-  *
-  * @return The voxel size.
-  **/
-  inline float Series::GetVoxelSizeY()
-  {
-    float result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, float*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(31);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return result_;
-  }
-  /**
-  * @brief Get the physical size of a voxel along the Z-axis.
-  *
-  * Get the physical size of a voxel along the Z-axis. This call is only valid if this series corresponds to a 3D image.
-  *
-  * @return The voxel size.
-  **/
-  inline float Series::GetVoxelSizeZ()
-  {
-    float result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, float*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(32);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return result_;
-  }
-  /**
-  * @brief Get the slice thickness.
-  *
-  * Get the slice thickness. This call is only valid if this series corresponds to a 3D image.
-  *
-  * @return The slice thickness.
-  **/
-  inline float Series::GetSliceThickness()
-  {
-    float result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, float*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(33);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return result_;
-  }
-  /**
-  * @brief Load the 3D image into a memory buffer.
-  *
-  * Load the 3D image into a memory buffer. This call is only valid if this series corresponds to a 3D image. The "target" buffer must be wide enough to store all the voxels of the image.
-  *
-  * @param target The target memory buffer.
-  * @param format The memory layout of the voxels.
-  * @param lineStride The number of bytes between two lines in the target memory buffer.
-  * @param stackStride The number of bytes between two 2D slices in the target memory buffer.
-  **/
-  inline void Series::Load3DImage(void* target, ::Orthanc::PixelFormat format, LAAW_INT64 lineStride, LAAW_INT64 stackStride)
-  {
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, void*, LAAW_INT32, LAAW_INT64, LAAW_INT64);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(34);
-    char* error = function(pimpl_, target, format, lineStride, stackStride);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-  /**
-  * @brief Load the 3D image into a memory buffer.
-  *
-  * Load the 3D image into a memory buffer. This call is only valid if this series corresponds to a 3D image. The "target" buffer must be wide enough to store all the voxels of the image. This method will also update a progress indicator to monitor the loading of the image.
-  *
-  * @param target The target memory buffer.
-  * @param format The memory layout of the voxels.
-  * @param lineStride The number of bytes between two lines in the target memory buffer.
-  * @param stackStride The number of bytes between two 2D slices in the target memory buffer.
-  * @param progress A pointer to a floating-point number that is continuously updated by the download threads to reflect the percentage of completion (between 0 and 1). This value can be read from a separate thread.
-  **/
-  inline void Series::Load3DImage(void* target, ::Orthanc::PixelFormat format, LAAW_INT64 lineStride, LAAW_INT64 stackStride, float progress[])
-  {
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, void*, LAAW_INT32, LAAW_INT64, LAAW_INT64, float*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(35);
-    char* error = function(pimpl_, target, format, lineStride, stackStride, progress);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-}
-
-namespace OrthancClient
-{
-  /**
-  * @brief Create a connection to some study.
-  *
-  * Create a connection to some study.
-  *
-  * @param connection The remote instance of %Orthanc.
-  * @param id The %Orthanc identifier of the study.
-  **/
-  inline Study::Study(::OrthancClient::OrthancConnection& connection, const ::std::string& id)
-  {
-    isReference_ = false;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void**, void*, const char*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(36);
-    char* error = function(&pimpl_, connection.pimpl_, id.c_str());
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-  /**
-  * @brief Destructs the object.
-  *
-  * Destructs the object.
-  *
-  **/
-  inline Study::~Study()
-  {
-    if (isReference_) return;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(37);
-    char* error = function(pimpl_);
-    error = error;  // Remove warning about unused variable
-  }
-  /**
-  * @brief Reload the series of this study.
-  *
-  * This method will reload the list of the series of this study. Pay attention to the fact that the series that have been previously returned by GetSeries() will be invalidated.
-  *
-  **/
-  inline void Study::Reload()
-  {
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(38);
-    char* error = function(pimpl_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-  /**
-  * @brief Return the number of series for this study.
-  *
-  * Return the number of series for this study.
-  *
-  * @return The number of series.
-  **/
-  inline LAAW_UINT32 Study::GetSeriesCount()
-  {
-    LAAW_UINT32 result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(39);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return result_;
-  }
-  /**
-  * @brief Get some series of this study.
-  *
-  * This method will return an object that contains information about some series. The series are indexed by a number between 0 (inclusive) and the result of GetSeriesCount() (exclusive).
-  *
-  * @param index The index of the series of interest.
-  * @return The series.
-  **/
-  inline ::OrthancClient::Series Study::GetSeries(LAAW_UINT32 index)
-  {
-    void* result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, void**, LAAW_UINT32);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(40);
-    char* error = function(pimpl_, &result_, index);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return ::OrthancClient::Series(result_);
-  }
-  /**
-  * @brief Get the %Orthanc identifier of this study.
-  *
-  * Get the %Orthanc identifier of this study.
-  *
-  * @return The identifier.
-  **/
-  inline ::std::string Study::GetId() const
-  {
-    const char* result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(41);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return std::string(result_);
-  }
-  /**
-  * @brief Get the value of one of the main DICOM tags for this study.
-  *
-  * Get the value of one of the main DICOM tags for this study.
-  *
-  * @param tag The name of the tag of interest ("StudyDate", "StudyDescription", "StudyInstanceUID" or "StudyTime").
-  * @param defaultValue The default value to be returned if this tag does not exist.
-  * @return The value of the tag.
-  **/
-  inline ::std::string Study::GetMainDicomTag(const ::std::string& tag, const ::std::string& defaultValue) const
-  {
-    const char* result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**, const char*, const char*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(42);
-    char* error = function(pimpl_, &result_, tag.c_str(), defaultValue.c_str());
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return std::string(result_);
-  }
-}
-
-namespace OrthancClient
-{
-  /**
-  * @brief Create a connection to some image instance.
-  *
-  * Create a connection to some image instance.
-  *
-  * @param connection The remote instance of %Orthanc.
-  * @param id The %Orthanc identifier of the image instance.
-  **/
-  inline Instance::Instance(::OrthancClient::OrthancConnection& connection, const ::std::string& id)
-  {
-    isReference_ = false;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void**, void*, const char*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(43);
-    char* error = function(&pimpl_, connection.pimpl_, id.c_str());
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-  /**
-  * @brief Destructs the object.
-  *
-  * Destructs the object.
-  *
-  **/
-  inline Instance::~Instance()
-  {
-    if (isReference_) return;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(44);
-    char* error = function(pimpl_);
-    error = error;  // Remove warning about unused variable
-  }
-  /**
-  * @brief Get the %Orthanc identifier of this identifier.
-  *
-  * Get the %Orthanc identifier of this identifier.
-  *
-  * @return The identifier.
-  **/
-  inline ::std::string Instance::GetId() const
-  {
-    const char* result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(45);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return std::string(result_);
-  }
-  /**
-  * @brief Set the extraction mode for the 2D image corresponding to this instance.
-  *
-  * Set the extraction mode for the 2D image corresponding to this instance.
-  *
-  * @param mode The extraction mode.
-  **/
-  inline void Instance::SetImageExtractionMode(::Orthanc::ImageExtractionMode mode)
-  {
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_INT32);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(46);
-    char* error = function(pimpl_, mode);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-  /**
-  * @brief Get the extraction mode for the 2D image corresponding to this instance.
-  *
-  * Get the extraction mode for the 2D image corresponding to this instance.
-  *
-  * @return The extraction mode.
-  **/
-  inline ::Orthanc::ImageExtractionMode Instance::GetImageExtractionMode() const
-  {
-    LAAW_INT32 result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, LAAW_INT32*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(47);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return static_cast< ::Orthanc::ImageExtractionMode >(result_);
-  }
-  /**
-  * @brief Get the string value of some DICOM tag of this instance.
-  *
-  * Get the string value of some DICOM tag of this instance.
-  *
-  * @param tag The name of the tag of interest.
-  * @return The value of the tag.
-  **/
-  inline ::std::string Instance::GetTagAsString(const ::std::string& tag) const
-  {
-    const char* result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**, const char*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(48);
-    char* error = function(pimpl_, &result_, tag.c_str());
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return std::string(result_);
-  }
-  /**
-  * @brief Get the floating point value that is stored in some DICOM tag of this instance.
-  *
-  * Get the floating point value that is stored in some DICOM tag of this instance.
-  *
-  * @param tag The name of the tag of interest.
-  * @return The value of the tag.
-  **/
-  inline float Instance::GetTagAsFloat(const ::std::string& tag) const
-  {
-    float result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, float*, const char*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(49);
-    char* error = function(pimpl_, &result_, tag.c_str());
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return result_;
-  }
-  /**
-  * @brief Get the integer value that is stored in some DICOM tag of this instance.
-  *
-  * Get the integer value that is stored in some DICOM tag of this instance.
-  *
-  * @param tag The name of the tag of interest.
-  * @return The value of the tag.
-  **/
-  inline LAAW_INT32 Instance::GetTagAsInt(const ::std::string& tag) const
-  {
-    LAAW_INT32 result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, LAAW_INT32*, const char*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(50);
-    char* error = function(pimpl_, &result_, tag.c_str());
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return result_;
-  }
-  /**
-  * @brief Get the width of the 2D image.
-  *
-  * Get the width of the 2D image that is encoded by this DICOM instance.
-  *
-  * @return The width.
-  **/
-  inline LAAW_UINT32 Instance::GetWidth()
-  {
-    LAAW_UINT32 result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(51);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return result_;
-  }
-  /**
-  * @brief Get the height of the 2D image.
-  *
-  * Get the height of the 2D image that is encoded by this DICOM instance.
-  *
-  * @return The height.
-  **/
-  inline LAAW_UINT32 Instance::GetHeight()
-  {
-    LAAW_UINT32 result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(52);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return result_;
-  }
-  /**
-  * @brief Get the number of bytes between two lines of the image (pitch).
-  *
-  * Get the number of bytes between two lines of the image in the memory buffer returned by GetBuffer(). This value depends on the extraction mode for the image.
-  *
-  * @return The pitch.
-  **/
-  inline LAAW_UINT32 Instance::GetPitch()
-  {
-    LAAW_UINT32 result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT32*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(53);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return result_;
-  }
-  /**
-  * @brief Get the format of the pixels of the 2D image.
-  *
-  * Return the memory layout that is used for the 2D image that is encoded by this DICOM instance. This value depends on the extraction mode for the image.
-  *
-  * @return The pixel format.
-  **/
-  inline ::Orthanc::PixelFormat Instance::GetPixelFormat()
-  {
-    LAAW_INT32 result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_INT32*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(54);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return static_cast< ::Orthanc::PixelFormat >(result_);
-  }
-  /**
-  * @brief Access the memory buffer in which the raw pixels of the 2D image are stored.
-  *
-  * Access the memory buffer in which the raw pixels of the 2D image are stored.
-  *
-  * @return A pointer to the memory buffer.
-  **/
-  inline const void* Instance::GetBuffer()
-  {
-    const void* result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, const void**);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(55);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return reinterpret_cast< const void* >(result_);
-  }
-  /**
-  * @brief Access the memory buffer in which the raw pixels of some line of the 2D image are stored.
-  *
-  * Access the memory buffer in which the raw pixels of some line of the 2D image are stored.
-  *
-  * @param y The line of interest.
-  * @return A pointer to the memory buffer.
-  **/
-  inline const void* Instance::GetBuffer(LAAW_UINT32 y)
-  {
-    const void* result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, const void**, LAAW_UINT32);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(56);
-    char* error = function(pimpl_, &result_, y);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return reinterpret_cast< const void* >(result_);
-  }
-  /**
-  * @brief Get the size of the DICOM file corresponding to this instance.
-  *
-  * Get the size of the DICOM file corresponding to this instance.
-  *
-  * @return The file size.
-  **/
-  inline LAAW_UINT64 Instance::GetDicomSize()
-  {
-    LAAW_UINT64 result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, LAAW_UINT64*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(57);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return result_;
-  }
-  /**
-  * @brief Get a pointer to the content of the DICOM file corresponding to this instance.
-  *
-  * Get a pointer to the content of the DICOM file corresponding to this instance.
-  *
-  * @return The DICOM file.
-  **/
-  inline const void* Instance::GetDicom()
-  {
-    const void* result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, const void**);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(58);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return reinterpret_cast< const void* >(result_);
-  }
-  /**
-  * @brief Discard the downloaded 2D image, so as to make room in memory.
-  *
-  * Discard the downloaded 2D image, so as to make room in memory.
-  *
-  **/
-  inline void Instance::DiscardImage()
-  {
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(59);
-    char* error = function(pimpl_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-  /**
-  * @brief Discard the downloaded DICOM file, so as to make room in memory.
-  *
-  * Discard the downloaded DICOM file, so as to make room in memory.
-  *
-  **/
-  inline void Instance::DiscardDicom()
-  {
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(60);
-    char* error = function(pimpl_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-  /**
-  * @brief Load a raw tag from the DICOM file.
-  *
-  * Load a raw tag from the DICOM file.
-  *
-  * @param path The path to the tag of interest (e.g. "0020-000d").
-  **/
-  inline void Instance::LoadTagContent(const ::std::string& path)
-  {
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (void*, const char*);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(61);
-    char* error = function(pimpl_, path.c_str());
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-  }
-  /**
-  * @brief Return the value of the raw tag that was loaded by LoadContent.
-  *
-  * Return the value of the raw tag that was loaded by LoadContent.
-  *
-  * @return The tag value.
-  **/
-  inline ::std::string Instance::GetLoadedTagContent() const
-  {
-    const char* result_;
-    typedef char* (LAAW_ORTHANC_CLIENT_CALL_CONV* Function) (const void*, const char**);
-    Function function = (Function) ::OrthancClient::Internals::Library::GetInstance().GetFunction(62);
-    char* error = function(pimpl_, &result_);
-    ::OrthancClient::Internals::Library::GetInstance().ThrowExceptionIfNeeded(error);
-    return std::string(result_);
-  }
-}
-
--- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows32.def	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-LIBRARY some.dll
-EXPORTS
-  _LAAW_EXTERNC_557aee7b61817292a0f31269d3c35db7@8 = LAAW_EXTERNC_557aee7b61817292a0f31269d3c35db7@8
-  _LAAW_EXTERNC_0b8dff0ce67f10954a49b059e348837e@8 = LAAW_EXTERNC_0b8dff0ce67f10954a49b059e348837e@8
-  _LAAW_EXTERNC_e05097c153f676e5a5ee54dcfc78256f@4 = LAAW_EXTERNC_e05097c153f676e5a5ee54dcfc78256f@4
-  _LAAW_EXTERNC_e840242bf58d17d3c1d722da09ce88e0@8 = LAAW_EXTERNC_e840242bf58d17d3c1d722da09ce88e0@8
-  _LAAW_EXTERNC_c9af31433001b5dfc012a552dc6d0050@8 = LAAW_EXTERNC_c9af31433001b5dfc012a552dc6d0050@8
-  _LAAW_EXTERNC_3fba4d6b818180a44cd1cae6046334dc@12 = LAAW_EXTERNC_3fba4d6b818180a44cd1cae6046334dc@12
-  _LAAW_EXTERNC_aeb20dc75b9246188db857317e5e0ce7@8 = LAAW_EXTERNC_aeb20dc75b9246188db857317e5e0ce7@8
-  _LAAW_EXTERNC_62689803d9871e4d9c51a648640b320b@8 = LAAW_EXTERNC_62689803d9871e4d9c51a648640b320b@8
-  _LAAW_EXTERNC_2fb64c9e5a67eccd413b0e913469a421@16 = LAAW_EXTERNC_2fb64c9e5a67eccd413b0e913469a421@16
-  _LAAW_EXTERNC_1f1acb322ea4d0aad65172824607673c@8 = LAAW_EXTERNC_1f1acb322ea4d0aad65172824607673c@8
-  _LAAW_EXTERNC_f3fd272e4636f6a531aabb72ee01cd5b@16 = LAAW_EXTERNC_f3fd272e4636f6a531aabb72ee01cd5b@16
-  _LAAW_EXTERNC_12d3de0a96e9efb11136a9811bb9ed38@4 = LAAW_EXTERNC_12d3de0a96e9efb11136a9811bb9ed38@4
-  _LAAW_EXTERNC_f756172daf04516eec3a566adabb4335@4 = LAAW_EXTERNC_f756172daf04516eec3a566adabb4335@4
-  _LAAW_EXTERNC_ddb68763ec902a97d579666a73a20118@8 = LAAW_EXTERNC_ddb68763ec902a97d579666a73a20118@8
-  _LAAW_EXTERNC_fba3c68b4be7558dbc65f7ce1ab57d63@12 = LAAW_EXTERNC_fba3c68b4be7558dbc65f7ce1ab57d63@12
-  _LAAW_EXTERNC_b4ca99d958f843493e58d1ef967340e1@8 = LAAW_EXTERNC_b4ca99d958f843493e58d1ef967340e1@8
-  _LAAW_EXTERNC_78d5cc76d282437b6f93ec3b82c35701@16 = LAAW_EXTERNC_78d5cc76d282437b6f93ec3b82c35701@16
-  _LAAW_EXTERNC_6cf0d7268667f9b0aa4511bacf184919@12 = LAAW_EXTERNC_6cf0d7268667f9b0aa4511bacf184919@12
-  _LAAW_EXTERNC_7d81cd502ee27e859735d0ea7112b5a1@4 = LAAW_EXTERNC_7d81cd502ee27e859735d0ea7112b5a1@4
-  _LAAW_EXTERNC_48a2a1a9d68c047e22bfba23014643d2@4 = LAAW_EXTERNC_48a2a1a9d68c047e22bfba23014643d2@4
-  _LAAW_EXTERNC_852bf8296ca21c5fde5ec565cc10721d@8 = LAAW_EXTERNC_852bf8296ca21c5fde5ec565cc10721d@8
-  _LAAW_EXTERNC_efd04574e0779faa83df1f2d8f9888db@12 = LAAW_EXTERNC_efd04574e0779faa83df1f2d8f9888db@12
-  _LAAW_EXTERNC_736247ff5e8036dac38163da6f666ed5@8 = LAAW_EXTERNC_736247ff5e8036dac38163da6f666ed5@8
-  _LAAW_EXTERNC_d82d2598a7a73f4b6fcc0c09c25b08ca@8 = LAAW_EXTERNC_d82d2598a7a73f4b6fcc0c09c25b08ca@8
-  _LAAW_EXTERNC_88134b978f9acb2aecdadf54aeab3c64@16 = LAAW_EXTERNC_88134b978f9acb2aecdadf54aeab3c64@16
-  _LAAW_EXTERNC_152cb1b704c053d24b0dab7461ba6ea3@8 = LAAW_EXTERNC_152cb1b704c053d24b0dab7461ba6ea3@8
-  _LAAW_EXTERNC_eee03f337ec81d9f1783cd41e5238757@8 = LAAW_EXTERNC_eee03f337ec81d9f1783cd41e5238757@8
-  _LAAW_EXTERNC_006f08237bd7611636fc721baebfb4c5@8 = LAAW_EXTERNC_006f08237bd7611636fc721baebfb4c5@8
-  _LAAW_EXTERNC_b794f5cd3dad7d7b575dd1fd902afdd0@8 = LAAW_EXTERNC_b794f5cd3dad7d7b575dd1fd902afdd0@8
-  _LAAW_EXTERNC_8ee2e50dd9df8f66a3c1766090dd03ab@8 = LAAW_EXTERNC_8ee2e50dd9df8f66a3c1766090dd03ab@8
-  _LAAW_EXTERNC_046aed35bbe4751691f4c34cc249a61d@8 = LAAW_EXTERNC_046aed35bbe4751691f4c34cc249a61d@8
-  _LAAW_EXTERNC_2be452e7af5bf7dfd8c5021842674497@8 = LAAW_EXTERNC_2be452e7af5bf7dfd8c5021842674497@8
-  _LAAW_EXTERNC_4dcc7a0fd025efba251ac6e9b701c2c5@28 = LAAW_EXTERNC_4dcc7a0fd025efba251ac6e9b701c2c5@28
-  _LAAW_EXTERNC_b2601a161c24ad0a1d3586246f87452c@32 = LAAW_EXTERNC_b2601a161c24ad0a1d3586246f87452c@32
-  _LAAW_EXTERNC_193599b9e345384fcdfcd47c29c55342@12 = LAAW_EXTERNC_193599b9e345384fcdfcd47c29c55342@12
-  _LAAW_EXTERNC_7c97f17063a357d38c5fab1136ad12a0@4 = LAAW_EXTERNC_7c97f17063a357d38c5fab1136ad12a0@4
-  _LAAW_EXTERNC_e65b20b7e0170b67544cd6664a4639b7@4 = LAAW_EXTERNC_e65b20b7e0170b67544cd6664a4639b7@4
-  _LAAW_EXTERNC_470e981b0e41f17231ba0ae6f3033321@8 = LAAW_EXTERNC_470e981b0e41f17231ba0ae6f3033321@8
-  _LAAW_EXTERNC_04cefd138b6ea15ad909858f2a0a8f05@12 = LAAW_EXTERNC_04cefd138b6ea15ad909858f2a0a8f05@12
-  _LAAW_EXTERNC_aee5b1f6f0c082f2c3b0986f9f6a18c7@8 = LAAW_EXTERNC_aee5b1f6f0c082f2c3b0986f9f6a18c7@8
-  _LAAW_EXTERNC_93965682bace75491413e1f0b8d5a654@16 = LAAW_EXTERNC_93965682bace75491413e1f0b8d5a654@16
-  _LAAW_EXTERNC_b01c6003238eb46c8db5dc823d7ca678@12 = LAAW_EXTERNC_b01c6003238eb46c8db5dc823d7ca678@12
-  _LAAW_EXTERNC_0147007fb99bad8cd95a139ec8795376@4 = LAAW_EXTERNC_0147007fb99bad8cd95a139ec8795376@4
-  _LAAW_EXTERNC_236ee8b403bc99535a8a4695c0cd45cb@8 = LAAW_EXTERNC_236ee8b403bc99535a8a4695c0cd45cb@8
-  _LAAW_EXTERNC_2a437b7aba6bb01e81113835be8f0146@8 = LAAW_EXTERNC_2a437b7aba6bb01e81113835be8f0146@8
-  _LAAW_EXTERNC_2bcbcb850934ae0bb4c6f0cc940e6cda@8 = LAAW_EXTERNC_2bcbcb850934ae0bb4c6f0cc940e6cda@8
-  _LAAW_EXTERNC_8d415c3a78a48e7e61d9fd24e7c79484@12 = LAAW_EXTERNC_8d415c3a78a48e7e61d9fd24e7c79484@12
-  _LAAW_EXTERNC_70d2f8398bbc63b5f792b69b4ad5fecb@12 = LAAW_EXTERNC_70d2f8398bbc63b5f792b69b4ad5fecb@12
-  _LAAW_EXTERNC_1729a067d902771517388eedd7346b23@12 = LAAW_EXTERNC_1729a067d902771517388eedd7346b23@12
-  _LAAW_EXTERNC_72e2aeee66cd3abd8ab7e987321c3745@8 = LAAW_EXTERNC_72e2aeee66cd3abd8ab7e987321c3745@8
-  _LAAW_EXTERNC_1ea3df5a1ac1a1a687fe7325adddb6f0@8 = LAAW_EXTERNC_1ea3df5a1ac1a1a687fe7325adddb6f0@8
-  _LAAW_EXTERNC_99b4f370e4f532d8b763e2cb49db92f8@8 = LAAW_EXTERNC_99b4f370e4f532d8b763e2cb49db92f8@8
-  _LAAW_EXTERNC_c41c742b68617f1c0590577a0a5ebc0c@8 = LAAW_EXTERNC_c41c742b68617f1c0590577a0a5ebc0c@8
-  _LAAW_EXTERNC_142dd2feba0fc1d262bbd0baeb441a8b@8 = LAAW_EXTERNC_142dd2feba0fc1d262bbd0baeb441a8b@8
-  _LAAW_EXTERNC_5f5c9f81a4dff8daa6c359f1d0488fef@12 = LAAW_EXTERNC_5f5c9f81a4dff8daa6c359f1d0488fef@12
-  _LAAW_EXTERNC_9ca979fffd08fa256306d4e68d8b0e91@8 = LAAW_EXTERNC_9ca979fffd08fa256306d4e68d8b0e91@8
-  _LAAW_EXTERNC_6f2d77a26edc91c28d89408dbc3c271e@8 = LAAW_EXTERNC_6f2d77a26edc91c28d89408dbc3c271e@8
-  _LAAW_EXTERNC_c0f494b80d4ff8b232df7a75baa0700a@4 = LAAW_EXTERNC_c0f494b80d4ff8b232df7a75baa0700a@4
-  _LAAW_EXTERNC_d604f44bd5195e082e745e9cbc164f4c@4 = LAAW_EXTERNC_d604f44bd5195e082e745e9cbc164f4c@4
-  _LAAW_EXTERNC_1710299d1c5f3b1f2b7cf3962deebbfd@8 = LAAW_EXTERNC_1710299d1c5f3b1f2b7cf3962deebbfd@8
-  _LAAW_EXTERNC_bb55aaf772ddceaadee36f4e54136bcb@8 = LAAW_EXTERNC_bb55aaf772ddceaadee36f4e54136bcb@8
-  _LAAW_EXTERNC_6c5ad02f91b583e29cebd0bd319ce21d@12 = LAAW_EXTERNC_6c5ad02f91b583e29cebd0bd319ce21d@12
-  _LAAW_EXTERNC_4068241c44a9c1367fe0e57be523f207@4 = LAAW_EXTERNC_4068241c44a9c1367fe0e57be523f207@4
-  _LAAW_EXTERNC_GetDescription@0 = LAAW_EXTERNC_GetDescription@0
-  _LAAW_EXTERNC_GetCompany@0 = LAAW_EXTERNC_GetCompany@0
-  _LAAW_EXTERNC_GetProduct@0 = LAAW_EXTERNC_GetProduct@0
-  _LAAW_EXTERNC_GetCopyright@0 = LAAW_EXTERNC_GetCopyright@0
-  _LAAW_EXTERNC_GetVersion@0 = LAAW_EXTERNC_GetVersion@0
-  _LAAW_EXTERNC_GetFileVersion@0 = LAAW_EXTERNC_GetFileVersion@0
-  _LAAW_EXTERNC_GetFullVersion@0 = LAAW_EXTERNC_GetFullVersion@0
-  _LAAW_EXTERNC_FreeString@4 = LAAW_EXTERNC_FreeString@4
--- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows32.rc	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-#include <winver.h>
-
-VS_VERSION_INFO VERSIONINFO
-   FILEVERSION 0,8,0,5
-   PRODUCTVERSION 0,8,0,0
-   FILEOS VOS_NT_WINDOWS32
-   FILETYPE VFT_DLL
-   BEGIN
-      BLOCK "StringFileInfo"
-      BEGIN
-         BLOCK "040904E4"
-         BEGIN
-            VALUE "Comments", "Release 0.8.5"
-            VALUE "CompanyName", "University Hospital of Liege"
-            VALUE "FileDescription", "Native client to the REST API of Orthanc"
-            VALUE "FileVersion", "0.8.0.5"
-            VALUE "InternalName", "OrthancClient"
-            VALUE "LegalCopyright", "(c) 2012-2015, Sebastien Jodogne, University Hospital of Liege"
-            VALUE "LegalTrademarks", "Licensing information is available on http://www.orthanc-server.com/"
-            VALUE "OriginalFilename", "OrthancClient_Windows32.dll"
-            VALUE "ProductName", "OrthancClient"
-            VALUE "ProductVersion", "0.8"
-         END
-      END
-
-      BLOCK "VarFileInfo"
-      BEGIN
-        VALUE "Translation", 0x409, 1252  // U.S. English
-      END
-   END
--- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows64.def	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-LIBRARY some.dll
-EXPORTS
-  LAAW_EXTERNC_557aee7b61817292a0f31269d3c35db7
-  LAAW_EXTERNC_0b8dff0ce67f10954a49b059e348837e
-  LAAW_EXTERNC_e05097c153f676e5a5ee54dcfc78256f
-  LAAW_EXTERNC_e840242bf58d17d3c1d722da09ce88e0
-  LAAW_EXTERNC_c9af31433001b5dfc012a552dc6d0050
-  LAAW_EXTERNC_3fba4d6b818180a44cd1cae6046334dc
-  LAAW_EXTERNC_aeb20dc75b9246188db857317e5e0ce7
-  LAAW_EXTERNC_62689803d9871e4d9c51a648640b320b
-  LAAW_EXTERNC_2fb64c9e5a67eccd413b0e913469a421
-  LAAW_EXTERNC_1f1acb322ea4d0aad65172824607673c
-  LAAW_EXTERNC_f3fd272e4636f6a531aabb72ee01cd5b
-  LAAW_EXTERNC_12d3de0a96e9efb11136a9811bb9ed38
-  LAAW_EXTERNC_f756172daf04516eec3a566adabb4335
-  LAAW_EXTERNC_ddb68763ec902a97d579666a73a20118
-  LAAW_EXTERNC_fba3c68b4be7558dbc65f7ce1ab57d63
-  LAAW_EXTERNC_b4ca99d958f843493e58d1ef967340e1
-  LAAW_EXTERNC_78d5cc76d282437b6f93ec3b82c35701
-  LAAW_EXTERNC_6cf0d7268667f9b0aa4511bacf184919
-  LAAW_EXTERNC_7d81cd502ee27e859735d0ea7112b5a1
-  LAAW_EXTERNC_48a2a1a9d68c047e22bfba23014643d2
-  LAAW_EXTERNC_852bf8296ca21c5fde5ec565cc10721d
-  LAAW_EXTERNC_efd04574e0779faa83df1f2d8f9888db
-  LAAW_EXTERNC_736247ff5e8036dac38163da6f666ed5
-  LAAW_EXTERNC_d82d2598a7a73f4b6fcc0c09c25b08ca
-  LAAW_EXTERNC_88134b978f9acb2aecdadf54aeab3c64
-  LAAW_EXTERNC_152cb1b704c053d24b0dab7461ba6ea3
-  LAAW_EXTERNC_eee03f337ec81d9f1783cd41e5238757
-  LAAW_EXTERNC_006f08237bd7611636fc721baebfb4c5
-  LAAW_EXTERNC_b794f5cd3dad7d7b575dd1fd902afdd0
-  LAAW_EXTERNC_8ee2e50dd9df8f66a3c1766090dd03ab
-  LAAW_EXTERNC_046aed35bbe4751691f4c34cc249a61d
-  LAAW_EXTERNC_2be452e7af5bf7dfd8c5021842674497
-  LAAW_EXTERNC_4dcc7a0fd025efba251ac6e9b701c2c5
-  LAAW_EXTERNC_b2601a161c24ad0a1d3586246f87452c
-  LAAW_EXTERNC_193599b9e345384fcdfcd47c29c55342
-  LAAW_EXTERNC_7c97f17063a357d38c5fab1136ad12a0
-  LAAW_EXTERNC_e65b20b7e0170b67544cd6664a4639b7
-  LAAW_EXTERNC_470e981b0e41f17231ba0ae6f3033321
-  LAAW_EXTERNC_04cefd138b6ea15ad909858f2a0a8f05
-  LAAW_EXTERNC_aee5b1f6f0c082f2c3b0986f9f6a18c7
-  LAAW_EXTERNC_93965682bace75491413e1f0b8d5a654
-  LAAW_EXTERNC_b01c6003238eb46c8db5dc823d7ca678
-  LAAW_EXTERNC_0147007fb99bad8cd95a139ec8795376
-  LAAW_EXTERNC_236ee8b403bc99535a8a4695c0cd45cb
-  LAAW_EXTERNC_2a437b7aba6bb01e81113835be8f0146
-  LAAW_EXTERNC_2bcbcb850934ae0bb4c6f0cc940e6cda
-  LAAW_EXTERNC_8d415c3a78a48e7e61d9fd24e7c79484
-  LAAW_EXTERNC_70d2f8398bbc63b5f792b69b4ad5fecb
-  LAAW_EXTERNC_1729a067d902771517388eedd7346b23
-  LAAW_EXTERNC_72e2aeee66cd3abd8ab7e987321c3745
-  LAAW_EXTERNC_1ea3df5a1ac1a1a687fe7325adddb6f0
-  LAAW_EXTERNC_99b4f370e4f532d8b763e2cb49db92f8
-  LAAW_EXTERNC_c41c742b68617f1c0590577a0a5ebc0c
-  LAAW_EXTERNC_142dd2feba0fc1d262bbd0baeb441a8b
-  LAAW_EXTERNC_5f5c9f81a4dff8daa6c359f1d0488fef
-  LAAW_EXTERNC_9ca979fffd08fa256306d4e68d8b0e91
-  LAAW_EXTERNC_6f2d77a26edc91c28d89408dbc3c271e
-  LAAW_EXTERNC_c0f494b80d4ff8b232df7a75baa0700a
-  LAAW_EXTERNC_d604f44bd5195e082e745e9cbc164f4c
-  LAAW_EXTERNC_1710299d1c5f3b1f2b7cf3962deebbfd
-  LAAW_EXTERNC_bb55aaf772ddceaadee36f4e54136bcb
-  LAAW_EXTERNC_6c5ad02f91b583e29cebd0bd319ce21d
-  LAAW_EXTERNC_4068241c44a9c1367fe0e57be523f207
-  LAAW_EXTERNC_GetDescription
-  LAAW_EXTERNC_GetCompany
-  LAAW_EXTERNC_GetProduct
-  LAAW_EXTERNC_GetCopyright
-  LAAW_EXTERNC_GetVersion
-  LAAW_EXTERNC_GetFileVersion
-  LAAW_EXTERNC_GetFullVersion
-  LAAW_EXTERNC_FreeString
--- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows64.rc	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-#include <winver.h>
-
-VS_VERSION_INFO VERSIONINFO
-   FILEVERSION 0,8,0,5
-   PRODUCTVERSION 0,8,0,0
-   FILEOS VOS_NT_WINDOWS32
-   FILETYPE VFT_DLL
-   BEGIN
-      BLOCK "StringFileInfo"
-      BEGIN
-         BLOCK "040904E4"
-         BEGIN
-            VALUE "Comments", "Release 0.8.5"
-            VALUE "CompanyName", "University Hospital of Liege"
-            VALUE "FileDescription", "Native client to the REST API of Orthanc"
-            VALUE "FileVersion", "0.8.0.5"
-            VALUE "InternalName", "OrthancClient"
-            VALUE "LegalCopyright", "(c) 2012-2015, Sebastien Jodogne, University Hospital of Liege"
-            VALUE "LegalTrademarks", "Licensing information is available on http://www.orthanc-server.com/"
-            VALUE "OriginalFilename", "OrthancClient_Windows64.dll"
-            VALUE "ProductName", "OrthancClient"
-            VALUE "ProductVersion", "0.8"
-         END
-      END
-
-      BLOCK "VarFileInfo"
-      BEGIN
-        VALUE "Translation", 0x409, 1252  // U.S. English
-      END
-   END
--- a/OrthancCppClient/SharedLibrary/ConfigurationCpp.json	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-{
-  "InternalsNamespace" : [ "OrthancClient", "Internals" ],
-  "PublicNamespace" : [ "OrthancClient" ],
-  "ExceptionClassName" : "OrthancClientException"
-}
--- a/OrthancCppClient/SharedLibrary/Generate.sh	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-#!/bin/bash
-
-set -e
-
-mkdir -p AUTOGENERATED
-LAAW_ROOT=~/Subversion/Jomago/Src/Labo/Laaw
-
-${LAAW_ROOT}/Parser/Build/LaawParser.exe AUTOGENERATED/CodeModelRaw.json ../OrthancConnection.h -I`pwd`/../../s/jsoncpp-src-0.6.0-rc2/include -fms-extensions
-python ${LAAW_ROOT}/Generators/CodeModelPostProcessing.py AUTOGENERATED/CodeModel.json AUTOGENERATED/CodeModelRaw.json Product.json
-python ${LAAW_ROOT}/Generators/GenerateWrapperCpp.py AUTOGENERATED/OrthancCppClient.h AUTOGENERATED/CodeModel.json Product.json ConfigurationCpp.json
-python ${LAAW_ROOT}/Generators/GenerateExternC.py AUTOGENERATED/ExternC.cpp AUTOGENERATED/CodeModel.json Product.json
-python ${LAAW_ROOT}/Generators/GenerateWindows32Def.py AUTOGENERATED/Windows32.def AUTOGENERATED/CodeModel.json
-python ${LAAW_ROOT}/Generators/GenerateWindows64Def.py AUTOGENERATED/Windows64.def AUTOGENERATED/CodeModel.json
-python ${LAAW_ROOT}/Generators/ApplyProductSubstitutions.py AUTOGENERATED/Windows32.rc ${LAAW_ROOT}/Resources/DllResources.rc.mustache Product.json PLATFORM_SUFFIX "_Windows32"
-python ${LAAW_ROOT}/Generators/ApplyProductSubstitutions.py AUTOGENERATED/Windows64.rc ${LAAW_ROOT}/Resources/DllResources.rc.mustache Product.json PLATFORM_SUFFIX "_Windows64"
--- a/OrthancCppClient/SharedLibrary/Laaw/VersionScript.map	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-# This is a version-script
-
-{
-global:
-  LAAW_EXTERNC_*;
-
-local:
-  *;
-};
--- a/OrthancCppClient/SharedLibrary/Laaw/laaw/laaw-exports.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-/**
- * Laaw - Lightweight, Automated API Wrapper
- * Copyright (C) 2010-2013 Jomago - Alain Mazy, Benjamin Golinvaux,
- * Sebastien Jodogne
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-/********************************************************************
- ** Windows target
- ********************************************************************/
-
-#if defined _WIN32
-
-#include <windows.h>
-
-#if defined(__GNUC__)
-// This is Mingw
-#define LAAW_EXPORT_DLL_API  // The exports are handled by the .DEF file
-#else
-// This is MSVC
-#define LAAW_EXPORT_DLL_API __declspec(dllexport)
-#endif
-
-#ifdef _M_X64
-// 64 bits target
-#define LAAW_CALL_CONVENTION
-#else
-// 32 bits target
-#define LAAW_CALL_CONVENTION  __stdcall  // Use the StdCall in Windows32 (for VB6)
-#endif
-
-
-/********************************************************************
- ** Linux target
- ********************************************************************/
-
-#elif defined(__linux)
-
-// Try the gcc visibility support
-// http://gcc.gnu.org/wiki/Visibility
-#if ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
-#define LAAW_EXPORT_DLL_API  __attribute__ ((visibility("default")))
-#define LAAW_CALL_CONVENTION
-#else
-#error No support for visibility in your version of GCC
-#endif
-
-
-/********************************************************************
- ** Max OS X target
- ********************************************************************/
-
-#else
-
-#define LAAW_EXPORT_DLL_API  __attribute__ ((visibility("default")))
-#define LAAW_CALL_CONVENTION
-
-#endif
--- a/OrthancCppClient/SharedLibrary/Laaw/laaw/laaw.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,89 +0,0 @@
-/**
- * Laaw - Lightweight, Automated API Wrapper
- * Copyright (C) 2010-2013 Jomago - Alain Mazy, Benjamin Golinvaux,
- * Sebastien Jodogne
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "laaw-exports.h"
-#include <stddef.h>
-#include <string>
-
-#if (LAAW_PARSING == 1)
-
-#define LAAW_API   __attribute__((deprecated("")))
-#define LAAW_API_INTERNAL  __attribute__((deprecated("")))
-#define LAAW_API_OVERLOAD(name)  __attribute__((deprecated("")))
-#define LAAW_API_PROPERTY  __attribute__((deprecated("")))
-#define LAAW_API_STATIC_CLASS  __attribute__((deprecated("")))
-#define LAAW_API_CUSTOM(name, value)  __attribute__((deprecated("")))
-
-#else
-
-#define LAAW_API
-#define LAAW_API_INTERNAL
-#define LAAW_API_OVERLOAD(name)
-#define LAAW_API_PROPERTY
-#define LAAW_API_STATIC_CLASS
-#define LAAW_API_CUSTOM(name, value)
-
-#endif
-
-
-namespace Laaw
-{
-  /**
-   * This is the base class from which all the public exceptions in
-   * the SDK should derive.
-   **/
-  class LaawException
-  {
-  private:
-    std::string what_;
-
-  public:
-    LaawException()
-    {
-    }
-
-    LaawException(const std::string& what) : what_(what)
-    {
-    }
-
-    LaawException(const char* what) : what_(what)
-    {
-    }
-
-    virtual const char* What() const
-    {
-      return what_.c_str();
-    }
-  };
-}
--- a/OrthancCppClient/SharedLibrary/Product.json	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-{
-  "Product" : "OrthancClient",
-  "Description" : "Native client to the REST API of Orthanc",
-  "Company" : "University Hospital of Liege",
-  "Copyright" : "(c) 2012-2015, Sebastien Jodogne, University Hospital of Liege",
-  "Legal" : "Licensing information is available on http://www.orthanc-server.com/",
-  "Version" : "0.8.5"
-}
--- a/OrthancCppClient/SharedLibrary/SharedLibrary.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-
-#include "../../Core/HttpClient.h"
-#include "../OrthancConnection.h"
-
-
-class SharedLibrarySingleton
-{
-public:
-  SharedLibrarySingleton()
-  {
-    Orthanc::HttpClient::GlobalInitialize();
-  }
-
-  ~SharedLibrarySingleton()
-  {
-    Orthanc::HttpClient::GlobalFinalize();
-  }
-};
-
-
-static SharedLibrarySingleton  singleton_;
-
-
-#include "AUTOGENERATED/ExternC.cpp"
--- a/OrthancCppClient/Study.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "../Core/PrecompiledHeaders.h"
-#include "Study.h"
-
-#include "OrthancConnection.h"
-
-namespace OrthancClient
-{
-  void Study::ReadStudy()
-  {
-    Orthanc::HttpClient client(connection_.GetHttpClient());
-    client.SetUrl(std::string(connection_.GetOrthancUrl()) + "/studies/" + id_);
-
-    Json::Value v;
-    if (!client.Apply(study_))
-    {
-      throw OrthancClientException(Orthanc::ErrorCode_NetworkProtocol);
-    }
-  }
-
-  Orthanc::IDynamicObject* Study::GetFillerItem(size_t index)
-  {
-    Json::Value::ArrayIndex tmp = static_cast<Json::Value::ArrayIndex>(index);
-    std::string id = study_["Series"][tmp].asString();
-    return new Series(connection_, id.c_str());
-  }
-
-  Study::Study(const OrthancConnection& connection,
-               const char* id) :
-    connection_(connection),
-    id_(id),
-    series_(*this)
-  {
-    series_.SetThreadCount(connection.GetThreadCount());
-    ReadStudy();
-  }
-
-  const char* Study::GetMainDicomTag(const char* tag, const char* defaultValue) const
-  {
-    if (study_["MainDicomTags"].isMember(tag))
-    {
-      return study_["MainDicomTags"][tag].asCString();
-    }
-    else
-    {
-      return defaultValue;
-    }
-  }
-}
--- a/OrthancCppClient/Study.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "Series.h"
-
-namespace OrthancClient
-{
-  /**
-   * {summary}{Connection to a study stored in %Orthanc.}
-   * {description}{This class encapsulates a connection to a study
-   * from a remote instance of %Orthanc.}
-   **/
-  class LAAW_API Study : 
-    public Orthanc::IDynamicObject, 
-    private Orthanc::ArrayFilledByThreads::IFiller
-  {
-  private:
-    const OrthancConnection& connection_;
-    std::string id_;
-    Json::Value study_;
-    Orthanc::ArrayFilledByThreads  series_;
-
-    void ReadStudy();
-
-    virtual size_t GetFillerSize()
-    {
-      return study_["Series"].size();
-    }
-
-    virtual Orthanc::IDynamicObject* GetFillerItem(size_t index);
-
-  public:
-    /**
-     * {summary}{Create a connection to some study.}
-     * {param}{connection The remote instance of %Orthanc.}
-     * {param}{id The %Orthanc identifier of the study.}
-     **/
-    Study(const OrthancConnection& connection,
-          const char* id);
-
-    /**
-     * {summary}{Reload the series of this study.}
-     * {description}{This method will reload the list of the series of this study. Pay attention to the fact that the series that have been previously returned by GetSeries() will be invalidated.}
-     **/
-    void Reload()
-    {
-      series_.Reload();
-    }
-
-    /**
-     * {summary}{Return the number of series for this study.}
-     * {returns}{The number of series.}
-     **/
-    uint32_t GetSeriesCount()
-    {
-      return series_.GetSize();
-    }
-
-    /**
-     * {summary}{Get some series of this study.}
-     * {description}{This method will return an object that contains information about some series. The series are indexed by a number between 0 (inclusive) and the result of GetSeriesCount() (exclusive).}
-     * {param}{index The index of the series of interest.}
-     * {returns}{The series.}
-     **/
-    Series& GetSeries(uint32_t index)
-    {
-      return dynamic_cast<Series&>(series_.GetItem(index));
-    }
-    
-    /**
-     * {summary}{Get the %Orthanc identifier of this study.}
-     * {returns}{The identifier.}
-     **/
-    const char* GetId() const
-    {
-      return id_.c_str();
-    }
-
-    /**
-     * {summary}{Get the value of one of the main DICOM tags for this study.}
-     * {param}{tag The name of the tag of interest ("StudyDate", "StudyDescription", "StudyInstanceUID" or "StudyTime").}
-     * {param}{defaultValue The default value to be returned if this tag does not exist.}
-     * {returns}{The value of the tag.}
-     **/
-    const char* GetMainDicomTag(const char* tag, 
-                                const char* defaultValue) const;
-  };
-}
--- a/OrthancExplorer/explorer.html	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancExplorer/explorer.html	Wed Sep 30 13:23:31 2015 +0200
@@ -6,15 +6,15 @@
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <title>Orthanc Explorer</title>
 
-    <link rel="stylesheet" href="libs/jquery.mobile-1.1.0.min.css" />
+    <link rel="stylesheet" href="libs/jquery.mobile.min.css" />
     <link rel="stylesheet" href="libs/jqtree.css" />
     <link rel="stylesheet" href="libs/jquery.mobile.simpledialog.min.css" />
     <link rel="stylesheet" href="libs/jquery-file-upload/css/style.css" />
     <link rel="stylesheet" href="libs/jquery-file-upload/css/jquery.fileupload-ui.css" />
     <link rel="stylesheet" href="libs/slimbox2/slimbox2.css" />
 
-    <script src="libs/jquery-1.7.2.min.js"></script>
-    <script src="libs/jquery.mobile-1.1.0.min.js"></script>
+    <script src="libs/jquery.min.js"></script>
+    <script src="libs/jquery.mobile.min.js"></script>
     <script src="libs/jqm.page.params.js"></script>
     <script src="libs/tree.jquery.js"></script>
     <script src="libs/date.js"></script>
@@ -30,6 +30,7 @@
     <link rel="stylesheet" href="explorer.css" />
     <script src="file-upload.js"></script>
     <script src="explorer.js"></script>
+    <script src="query-retrieve.js"></script>
     <script src="../plugins/explorer.js"></script>
   </head>
   <body>
@@ -37,7 +38,10 @@
       <div data-role="header" >
 	<h1><span class="orthanc-name"></span>Find a patient</h1>
         <a href="#plugins" data-icon="grid" class="ui-btn-left" data-direction="reverse">Plugins</a>
-        <a href="#upload" data-icon="gear" class="ui-btn-right">Upload DICOM</a>
+        <div data-type="horizontal" data-role="controlgroup" class="ui-btn-right"> 
+          <a href="#upload" data-icon="gear" data-role="button">Upload</a>
+          <a href="#query-retrieve" data-icon="search" data-role="button">Query/Retrieve</a>
+        </div>
       </div>
       <div data-role="content">
         <ul id="all-patients" data-role="listview" data-inset="true" data-filter="true">
@@ -75,7 +79,10 @@
       <div data-role="header" >
 	<h1><span class="orthanc-name"></span>Patient</h1>
         <a href="#find-patients" data-icon="home" class="ui-btn-left" data-direction="reverse">Patients</a>
-        <a href="#upload" data-icon="gear" class="ui-btn-right">Upload DICOM</a>
+        <div data-type="horizontal" data-role="controlgroup" class="ui-btn-right"> 
+          <a href="#upload" data-icon="gear" data-role="button">Upload</a>
+          <a href="#query-retrieve" data-icon="search" data-role="button">Query/Retrieve</a>
+        </div>
       </div>
       <div data-role="content">
         <div class="ui-grid-a">
@@ -129,7 +136,10 @@
           Study
         </h1>
         <a href="#find-patients" data-icon="home" class="ui-btn-left" data-direction="reverse">Patients</a>
-        <a href="#upload" data-icon="gear" class="ui-btn-right">Upload DICOM</a>
+        <div data-type="horizontal" data-role="controlgroup" class="ui-btn-right"> 
+          <a href="#upload" data-icon="gear" data-role="button">Upload</a>
+          <a href="#query-retrieve" data-icon="search" data-role="button">Query/Retrieve</a>
+        </div>
       </div>
       <div data-role="content">
         <div class="ui-grid-a">
@@ -178,7 +188,10 @@
         </h1>
 
         <a href="#find-patients" data-icon="home" class="ui-btn-left" data-direction="reverse">Patients</a>
-        <a href="#upload" data-icon="gear" class="ui-btn-right">Upload DICOM</a>
+        <div data-type="horizontal" data-role="controlgroup" class="ui-btn-right"> 
+          <a href="#upload" data-icon="gear" data-role="button">Upload</a>
+          <a href="#query-retrieve" data-icon="search" data-role="button">Query/Retrieve</a>
+        </div>
       </div>
       <div data-role="content">
         <div class="ui-grid-a">
@@ -228,7 +241,10 @@
           Instance
         </h1>
         <a href="#find-patients" data-icon="home" class="ui-btn-left" data-direction="reverse">Patients</a>
-        <a href="#upload" data-icon="gear" class="ui-btn-right">Upload DICOM</a>
+        <div data-type="horizontal" data-role="controlgroup" class="ui-btn-right"> 
+          <a href="#upload" data-icon="gear" data-role="button">Upload</a>
+          <a href="#query-retrieve" data-icon="search" data-role="button">Query/Retrieve</a>
+        </div>
       </div>
       <div data-role="content">
         <div class="ui-grid-a">
@@ -284,15 +300,111 @@
       </div>
     </div>
 
+    <div data-role="page" id="query-retrieve" >
+      <div data-role="header" >
+	<h1><span class="orthanc-name"></span>DICOM Query/Retrieve (1/3)</h1>
+        <a href="#find-patients" data-icon="home" class="ui-btn-left" data-direction="reverse">Patients</a>
+      </div>
+      <div data-role="content">
+        <form data-ajax="false">
+          <div data-role="fieldcontain">
+	    <label for="qr-server">DICOM server:</label>
+            <select name="qr-server" id="qr-server">
+            </select>
+	  </div>
+
+          <div data-role="fieldcontain" id="qr-fields">
+	    <fieldset data-role="controlgroup">
+	      <legend>Field of interest:</legend>
+	      <input type="radio" name="qr-field" id="qr-patient-id" value="PatientID" checked="checked" />
+	      <label for="qr-patient-id">Patient ID</label>
+	      <input type="radio" name="qr-field" id="qr-patient-name" value="PatientName" />
+	      <label for="qr-patient-name">Patient Name</label>
+	      <input type="radio" name="qr-field" id="qr-accession-number" value="AccessionNumber" />
+	      <label for="qr-accession-number">Accession Number</label>
+	      <input type="radio" name="qr-field" id="qr-study-description" value="StudyDescription" />
+	      <label for="qr-study-description">Study Description</label>
+	    </fieldset>
+	  </div>
+
+          <div data-role="fieldcontain">
+	    <label for="qr-value">Value for this field:</label>
+	    <input type="text" name="qr-value" id="qr-value" value="*"  />
+	  </div>
+
+          <div data-role="fieldcontain">
+	    <label for="qr-date">Study date:</label>
+            <select name="qr-date" id="qr-date">
+            </select>
+	  </div>
+
+          <div data-role="fieldcontain" id="qr-modalities">
+            <div data-role="fieldcontain">
+	      <fieldset data-role="controlgroup" data-type="horizontal">
+                <legend>Modalities:</legend>
+	        <input type="checkbox" name="CR" id="qr-cr" class="custom" /> <label for="qr-cr">CR</label>
+	        <input type="checkbox" name="CT" id="qr-ct" class="custom" /> <label for="qr-ct">CT</label>
+	        <input type="checkbox" name="MR" id="qr-mr" class="custom" /> <label for="qr-mr">MR</label>
+	        <input type="checkbox" name="NM" id="qr-nm" class="custom" /> <label for="qr-nm">NM</label>
+	        <input type="checkbox" name="PT" id="qr-pt" class="custom" /> <label for="qr-pt">PT</label>
+	        <input type="checkbox" name="US" id="qr-us" class="custom" /> <label for="qr-us">US</label>
+	        <input type="checkbox" name="XA" id="qr-xa" class="custom" /> <label for="qr-xa">XA</label>
+	      </fieldset>
+            </div>
+          </div>
+
+          <fieldset class="ui-grid-a">
+	    <div class="ui-block-a">
+              <button id="qr-echo" data-theme="a">Test Echo</button>
+            </div>
+	    <div class="ui-block-b">
+              <button id="qr-submit" type="submit" data-theme="b">Search studies</button>
+            </div>
+	  </fieldset>
+        </form>
+      </div>
+    </div>
+
+
+    <div data-role="page" id="query-retrieve-2" >
+      <div data-role="header" >
+	<h1><span class="orthanc-name"></span>DICOM Query/Retrieve (2/3)</h1>
+        <a href="#find-patients" data-icon="home" class="ui-btn-left" data-direction="reverse">Patients</a>
+        <a href="#query-retrieve" data-icon="search" class="ui-btn-right" data-direction="reverse">Query/Retrieve</a>
+      </div>
+      <div data-role="content">
+        <ul data-role="listview" data-inset="true" data-filter="true" data-split-icon="arrow-d" data-split-theme="b">
+        </ul>
+      </div>
+    </div>
+
+
+    <div data-role="page" id="query-retrieve-3" >
+      <div data-role="header" >
+	<h1><span class="orthanc-name"></span>DICOM Query/Retrieve (3/3)</h1>
+        <a href="#find-patients" data-icon="home" class="ui-btn-left" data-direction="reverse">Patients</a>
+        <a href="#query-retrieve" data-icon="search" class="ui-btn-right" data-direction="reverse">Query/Retrieve</a>
+      </div>
+      <div data-role="content">
+        <ul data-role="listview" data-inset="true" data-filter="true" data-split-icon="arrow-d" data-split-theme="b">
+        </ul>
+      </div>
+    </div>
+
 
     <div id="peer-store" style="display:none;" class="ui-body-c">
       <p align="center"><b>Sending to Orthanc peer...</b></p>
-      <p><img src="libs/images/ajax-loader2.gif" alt="" /></p>
+      <p><img src="libs/images/ajax-loader.gif" alt="" /></p>
     </div>
 
     <div id="dicom-store" style="display:none;" class="ui-body-c">
       <p align="center"><b>Sending to DICOM modality...</b></p>
-      <p><img src="libs/images/ajax-loader2.gif" alt="" /></p>
+      <p><img src="libs/images/ajax-loader.gif" alt="" /></p>
+    </div>
+
+    <div id="info-retrieve" style="display:none;" class="ui-body-c">
+      <p align="center"><b>Retrieving images from DICOM modality...</b></p>
+      <p><img src="libs/images/ajax-loader.gif" alt="" /></p>
     </div>
 
     <div id="dialog" style="display:none" >
--- a/OrthancExplorer/explorer.js	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancExplorer/explorer.js	Wed Sep 30 13:23:31 2015 +0200
@@ -30,6 +30,19 @@
 };
 
 
+function Refresh()
+{
+  if (currentPage == 'patient')
+    RefreshPatient();
+  else if (currentPage == 'study')
+    RefreshStudy();
+  else if (currentPage == 'series')
+    RefreshSeries();
+  else if (currentPage == 'instance')
+    RefreshInstance();
+}
+
+
 $(document).ready(function() {
   var $tree = $('#dicom-tree');
   $tree.tree({
@@ -45,6 +58,16 @@
         $tree.tree('openNode', event.node, true);
     }
   );
+  
+  currentPage = $.mobile.pageData.active;
+  currentUuid = $.mobile.pageData.uuid;
+  if (!(typeof currentPage === 'undefined') &&
+      !(typeof currentUuid === 'undefined') &&
+      currentPage.length > 0 && 
+      currentUuid.length > 0)
+  {
+    Refresh();
+  }
 });
 
 
@@ -142,11 +165,10 @@
 
 
 
-function GetSingleResource(type, uuid, callback)
+function GetResource(uri, callback)
 {
-  var resource = null;
   $.ajax({
-    url: '../' + type + '/' + uuid,
+    url: '..' + uri,
     dataType: 'json',
     async: false,
     cache: false,
@@ -157,42 +179,6 @@
 }
 
 
-function GetMultipleResources(type, uuids, callback)
-{
-  if (uuids == null)
-  {
-    $.ajax({
-      url: '../' + type,
-      dataType: 'json',
-      async: false,
-      cache: false,
-      success: function(s) {
-        uuids = s;
-      }
-    });
-  }
-
-  var resources = [];
-  var ajaxRequests = uuids.map(function(uuid) {
-    return $.ajax({
-      url: '../' + type + '/' + uuid,
-      dataType: 'json',
-      async: true,
-      cache: false,
-      success: function(s) {
-        resources.push(s);
-      }
-    });
-  });
-
-  // Wait for all the AJAX requests to end
-  $.when.apply($, ajaxRequests).then(function() {
-    callback(resources);
-  });
-}
-
-
-
 function CompleteFormatting(s, link, isReverse)
 {
   if (link != null)
@@ -344,18 +330,18 @@
 
 
 $('#find-patients').live('pagebeforeshow', function() {
-  GetMultipleResources('patients', null, function(patients) {
-    var target = $('#all-patients');
-    $('li', target).remove();
+  GetResource('/patients?expand', function(patients) {
+      var target = $('#all-patients');
+      $('li', target).remove();
     
-    SortOnDicomTag(patients, 'PatientName', false, false);
+      SortOnDicomTag(patients, 'PatientName', false, false);
 
-    for (var i = 0; i < patients.length; i++) {
-      var p = FormatPatient(patients[i], '#patient?uuid=' + patients[i].ID);
-      target.append(p);
-    }
+      for (var i = 0; i < patients.length; i++) {
+        var p = FormatPatient(patients[i], '#patient?uuid=' + patients[i].ID);
+        target.append(p);
+      }
 
-    target.listview('refresh');
+      target.listview('refresh');
   });
 });
 
@@ -381,8 +367,8 @@
 function RefreshPatient()
 {
   if ($.mobile.pageData) {
-    GetSingleResource('patients', $.mobile.pageData.uuid, function(patient) {
-      GetMultipleResources('studies', patient.Studies, function(studies) {
+    GetResource('/patients/' + $.mobile.pageData.uuid, function(patient) {
+      GetResource('/patients/' + $.mobile.pageData.uuid + '/studies', function(studies) {
         SortOnDicomTag(studies, 'StudyDate', false, true);
 
         $('#patient-info li').remove();
@@ -433,9 +419,9 @@
 function RefreshStudy()
 {
   if ($.mobile.pageData) {
-    GetSingleResource('studies', $.mobile.pageData.uuid, function(study) {
-      GetSingleResource('patients', study.ParentPatient, function(patient) {
-        GetMultipleResources('series', study.Series, function(series) {
+    GetResource('/studies/' + $.mobile.pageData.uuid, function(study) {
+      GetResource('/patients/' + study.ParentPatient, function(patient) {
+        GetResource('/studies/' + $.mobile.pageData.uuid + '/series', function(series) {
           SortOnDicomTag(series, 'SeriesDate', false, true);
 
           $('#study .patient-link').attr('href', '#patient?uuid=' + patient.ID);
@@ -465,7 +451,7 @@
           currentPage = 'study';
           currentUuid = $.mobile.pageData.uuid;
         });
-      });  
+      });
     });
   }
 }
@@ -474,10 +460,10 @@
 function RefreshSeries() 
 {
   if ($.mobile.pageData) {
-    GetSingleResource('series', $.mobile.pageData.uuid, function(series) {
-      GetSingleResource('studies', series.ParentStudy, function(study) {
-        GetSingleResource('patients', study.ParentPatient, function(patient) {
-          GetMultipleResources('instances', series.Instances, function(instances) {
+    GetResource('/series/' + $.mobile.pageData.uuid, function(series) {
+      GetResource('/studies/' + series.ParentStudy, function(study) {
+        GetResource('/patients/' + study.ParentPatient, function(patient) {
+          GetResource('/series/' + $.mobile.pageData.uuid + '/instances', function(instances) {
             Sort(instances, function(x) { return x.IndexInSeries; }, true, false);
 
             $('#series .patient-link').attr('href', '#patient?uuid=' + patient.ID);
@@ -568,10 +554,10 @@
 function RefreshInstance()
 {
   if ($.mobile.pageData) {
-    GetSingleResource('instances', $.mobile.pageData.uuid, function(instance) {
-      GetSingleResource('series', instance.ParentSeries, function(series) {
-        GetSingleResource('studies', series.ParentStudy, function(study) {
-          GetSingleResource('patients', study.ParentPatient, function(patient) {
+    GetResource('/instances/' + $.mobile.pageData.uuid, function(instance) {
+      GetResource('/series/' + instance.ParentSeries, function(series) {
+        GetResource('/studies/' + series.ParentStudy, function(study) {
+          GetResource('/patients/' + study.ParentPatient, function(patient) {
 
             $('#instance .patient-link').attr('href', '#patient?uuid=' + patient.ID);
             $('#instance .study-link').attr('href', '#study?uuid=' + study.ID);
@@ -589,13 +575,8 @@
               .append(FormatInstance(instance))
               .listview('refresh');
 
-            $.ajax({
-              url: '../instances/' + instance.ID + '/tags',
-              cache: false,
-              dataType: 'json',
-              success: function(s) {
-                $('#dicom-tree').tree('loadData', ConvertForTree(s));
-              }
+            GetResource('/instances/' + instance.ID + '/tags', function(s) {
+              $('#dicom-tree').tree('loadData', ConvertForTree(s));
             });
 
             SetupAnonymizedOrModifiedFrom('#instance-anonymized-from', instance, 'instance', 'AnonymizedFrom');
@@ -628,14 +609,7 @@
     if ('uuid' in $.mobile.pageData &&
         currentPage == $.mobile.pageData.active &&
         currentUuid != $.mobile.pageData.uuid) {
-      if (currentPage == 'patient')
-        RefreshPatient();
-      else if (currentPage == 'study')
-        RefreshStudy();
-      else if (currentPage == 'series')
-        RefreshSeries();
-      else if (currentPage == 'instance')
-        RefreshInstance();
+      Refresh();
     }
   });
 });
@@ -728,41 +702,52 @@
 
 $('#instance-preview').live('click', function(e) {
   if ($.mobile.pageData) {
-    GetSingleResource('instances', $.mobile.pageData.uuid + '/frames', function(frames) {
-      if (frames.length == 1)
-      {
-        // Viewing a single-frame image
-        jQuery.slimbox('../instances/' + $.mobile.pageData.uuid + '/preview', '', {
-          overlayFadeDuration : 1,
-          resizeDuration : 1,
-          imageFadeDuration : 1
-        });
-      }
-      else
-      {
-        // Viewing a multi-frame image
+    var pdf = '../instances/' + $.mobile.pageData.uuid + '/pdf';
+    $.ajax({
+      url: pdf,
+      cache: false,
+      success: function(s) {
+        window.location.assign(pdf);
+      },
+      error: function() {
+        GetResource('/instances/' + $.mobile.pageData.uuid + '/frames', function(frames) {
+          if (frames.length == 1)
+          {
+            // Viewing a single-frame image
+            jQuery.slimbox('../instances/' + $.mobile.pageData.uuid + '/preview', '', {
+              overlayFadeDuration : 1,
+              resizeDuration : 1,
+              imageFadeDuration : 1
+            });
+          }
+          else
+          {
+            // Viewing a multi-frame image
 
-        var images = [];
-        for (var i = 0; i < frames.length; i++) {
-          images.push([ '../instances/' + $.mobile.pageData.uuid + '/frames/' + i + '/preview' ]);
-        }
+            var images = [];
+            for (var i = 0; i < frames.length; i++) {
+              images.push([ '../instances/' + $.mobile.pageData.uuid + '/frames/' + i + '/preview' ]);
+            }
 
-        jQuery.slimbox(images, 0, {
-          overlayFadeDuration : 1,
-          resizeDuration : 1,
-          imageFadeDuration : 1,
-          loop : true
+            jQuery.slimbox(images, 0, {
+              overlayFadeDuration : 1,
+              resizeDuration : 1,
+              imageFadeDuration : 1,
+              loop : true
+            });
+          }
         });
       }
     });
-
   }
 });
 
+
+
 $('#series-preview').live('click', function(e) {
   if ($.mobile.pageData) {
-    GetSingleResource('series', $.mobile.pageData.uuid, function(series) {
-      GetMultipleResources('instances', series.Instances, function(instances) {
+    GetResource('/series/' + $.mobile.pageData.uuid, function(series) {
+      GetResource('/series/' + $.mobile.pageData.uuid + '/instances', function(instances) {
         Sort(instances, function(x) { return x.IndexInSeries; }, true, false);
 
         var images = [];
@@ -777,7 +762,7 @@
           imageFadeDuration : 1,
           loop : true
         });
-      })
+      });
     });
   }
 });
@@ -786,7 +771,6 @@
 
 
 
-
 function ChooseDicomModality(callback)
 {
   var clickedModality = '';
Binary file OrthancExplorer/libs/images/ajax-loader.gif has changed
Binary file OrthancExplorer/libs/images/ajax-loader.png has changed
Binary file OrthancExplorer/libs/images/ajax-loader2.gif has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancExplorer/libs/images/notes.txt	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,5 @@
+* The "ajax-loader.gif" was generated by the http://www.ajaxload.info/
+  Web site. "The Generated gifs can be used, modified and distributed
+  under the terms of the ​Do What The Fuck You Want To Public License."
+
+* The images "icons*" come from jQuery Mobile.
--- a/OrthancExplorer/libs/jquery-1.7.2.min.js	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-/*! jQuery v1.7.2 jquery.com | jquery.org/license */
-(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"<!doctype html>":"")+"<html><body>"),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function ca(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function b_(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bD.test(a)?d(a,e):b_(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&f.type(b)==="object")for(var e in b)b_(a+"["+e+"]",b[e],c,d);else d(a,b)}function b$(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function bZ(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bS,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bZ(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bZ(a,c,d,e,"*",g));return l}function bY(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bO),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bB(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?1:0,g=4;if(d>0){if(c!=="border")for(;e<g;e+=2)c||(d-=parseFloat(f.css(a,"padding"+bx[e]))||0),c==="margin"?d+=parseFloat(f.css(a,c+bx[e]))||0:d-=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0;return d+"px"}d=by(a,b);if(d<0||d==null)d=a.style[b];if(bt.test(d))return d;d=parseFloat(d)||0;if(c)for(;e<g;e+=2)d+=parseFloat(f.css(a,"padding"+bx[e]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+bx[e]))||0);return d+"px"}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;b.nodeType===1&&(b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?b.outerHTML=a.outerHTML:c!=="input"||a.type!=="checkbox"&&a.type!=="radio"?c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text):(a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value)),b.removeAttribute(f.expando),b.removeAttribute("_submit_attached"),b.removeAttribute("_change_attached"))}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c,i[c][d])}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h,i){var j,k=d==null,l=0,m=a.length;if(d&&typeof d=="object"){for(l in d)e.access(a,c,l,d[l],1,h,f);g=1}else if(f!==b){j=i===b&&e.isFunction(f),k&&(j?(j=c,c=function(a,b,c){return j.call(e(a),c)}):(c.call(a,f),c=null));if(c)for(;l<m;l++)c(a[l],d,j?f.call(a[l],l,c(a[l],d)):f,i);g=1}return g?a:k?c.call(a):m?c(a[0],d):h},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m,n=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?n(g):h==="function"&&(!a.unique||!p.has(g))&&c.push(g)},o=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,j=!0,m=k||0,k=0,l=c.length;for(;c&&m<l;m++)if(c[m].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}j=!1,c&&(a.once?e===!0?p.disable():c=[]:d&&d.length&&(e=d.shift(),p.fireWith(e[0],e[1])))},p={add:function(){if(c){var a=c.length;n(arguments),j?l=c.length:e&&e!==!0&&(k=a,o(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){j&&f<=l&&(l--,f<=m&&m--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&p.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(j?a.once||d.push([b,c]):(!a.once||!e)&&o(b,c));return this},fire:function(){p.fireWith(this,arguments);return this},fired:function(){return!!i}};return p};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p=c.createElement("div"),q=c.documentElement;p.setAttribute("className","t"),p.innerHTML="   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="<div "+n+"display:block;'><div style='"+t+"0;display:block;overflow:hidden;'></div></div>"+"<table "+n+"' cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="<table><tr><td style='"+t+"0;display:none'></td><td>t</td></tr></table>",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="<div style='width:5px;'></div>",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h,i,j=this[0],k=0,m=null;if(a===b){if(this.length){m=f.data(j);if(j.nodeType===1&&!f._data(j,"parsedAttrs")){g=j.attributes;for(i=g.length;k<i;k++)h=g[k].name,h.indexOf("data-")===0&&(h=f.camelCase(h.substring(5)),l(j,h,m[h]));f._data(j,"parsedAttrs",!0)}}return m}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!";return f.access(this,function(c){if(c===b){m=this.triggerHandler("getData"+e,[d[0]]),m===b&&j&&(m=f.data(j,a),m=l(j,a,m));return m===b&&d[1]?this.data(d[0]):m}d[1]=c,this.each(function(){var b=f(this);b.triggerHandler("setData"+e,d),f.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length<d)return f.queue(this[0],a);return c===b?this:this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise(c)}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,f.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i<g;i++)e=d[i],e&&(c=f.propFix[e]||e,h=u.test(e),h||f.attr(a,e,""),a.removeAttribute(v?e:c),h&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0,coords:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(
-a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:g&&G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=f.event.special[c.type]||{},j=[],k,l,m,n,o,p,q,r,s,t,u;g[0]=c,c.delegateTarget=this;if(!i.preDispatch||i.preDispatch.call(this,c)!==!1){if(e&&(!c.button||c.type!=="click")){n=f(this),n.context=this.ownerDocument||this;for(m=c.target;m!=this;m=m.parentNode||this)if(m.disabled!==!0){p={},r=[],n[0]=m;for(k=0;k<e;k++)s=d[k],t=s.selector,p[t]===b&&(p[t]=s.quick?H(m,s.quick):n.is(t)),p[t]&&r.push(s);r.length&&j.push({elem:m,matches:r})}}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k<j.length&&!c.isPropagationStopped();k++){q=j[k],c.currentTarget=q.elem;for(l=0;l<q.matches.length&&!c.isImmediatePropagationStopped();l++){s=q.matches[l];if(h||!c.namespace&&!s.namespace||c.namespace_re&&c.namespace_re.test(s.namespace))c.data=s.data,c.handleObj=s,o=((f.event.special[s.origType]||{}).handle||s.handler).apply(q.elem,g),o!==b&&(c.result=o,o===!1&&(c.preventDefault(),c.stopPropagation()))}}i.postDispatch&&i.postDispatch.call(this,c);return c.result}},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),d._submit_attached=!0)})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9||d===11){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.globalPOS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")[\\s/>]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f
-.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(f.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(g){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,function(a,b){b.src?f.ajax({type:"GET",global:!1,url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1></$2>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]==="<table>"&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i<u;i++)bn(l[i]);else bn(l);l.nodeType?j.push(l):j=f.merge(j,l)}if(d){g=function(a){return!a.type||be.test(a.type)};for(k=0;j[k];k++){h=j[k];if(e&&f.nodeName(h,"script")&&(!h.type||be.test(h.type)))e.push(h.parentNode?h.parentNode.removeChild(h):h);else{if(h.nodeType===1){var v=f.grep(h.getElementsByTagName("script"),g);j.splice.apply(j,[k+1,0].concat(v))}d.appendChild(h)}}}return j},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bp=/alpha\([^)]*\)/i,bq=/opacity=([^)]*)/,br=/([A-Z]|^ms)/g,bs=/^[\-+]?(?:\d*\.)?\d+$/i,bt=/^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,bu=/^([\-+])=([\-+.\de]+)/,bv=/^margin/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Top","Right","Bottom","Left"],by,bz,bA;f.fn.css=function(a,c){return f.access(this,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)},a,c,arguments.length>1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),(e===""&&f.css(d,"display")==="none"||!f.contains(d.ownerDocument.documentElement,d))&&f._data(d,"olddisplay",cu(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(ct("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(ct("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o,p,q;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]);if((k=f.cssHooks[g])&&"expand"in k){l=k.expand(a[g]),delete a[g];for(i in l)i in a||(a[i]=l[i])}}for(g in a){h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cu(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cm.test(h)?(q=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),q?(f._data(this,"toggle"+i,q==="show"?"hide":"show"),j[q]()):j[h]()):(m=cn.exec(h),n=j.cur(),m?(o=parseFloat(m[2]),p=m[3]||(f.cssNumber[i]?"":"px"),p!=="px"&&(f.style(this,i,(o||1)+p),n=(o||1)/j.cur()*n,f.style(this,i,n+p)),m[1]&&(o=(m[1]==="-="?-1:1)*o+n),j.custom(n,o,p)):j.custom(n,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:ct("show",1),slideUp:ct("hide",1),slideToggle:ct("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a){return a},swing:function(a){return-Math.cos(a*Math.PI)/2+.5}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cq||cr(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){f._data(e.elem,"fxshow"+e.prop)===b&&(e.options.hide?f._data(e.elem,"fxshow"+e.prop,e.start):e.options.show&&f._data(e.elem,"fxshow"+e.prop,e.end))},h()&&f.timers.push(h)&&!co&&(co=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cq||cr(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(co),co=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(cp.concat.apply([],cp),function(a,b){b.indexOf("margin")&&(f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)})}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cv,cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?cv=function(a,b,c,d){try{d=a.getBoundingClientRect()}catch(e){}if(!d||!f.contains(c,a))return d?{top:d.top,left:d.left}:{top:0,left:0};var g=b.body,h=cy(b),i=c.clientTop||g.clientTop||0,j=c.clientLeft||g.clientLeft||0,k=h.pageYOffset||f.support.boxModel&&c.scrollTop||g.scrollTop,l=h.pageXOffset||f.support.boxModel&&c.scrollLeft||g.scrollLeft,m=d.top+k-i,n=d.left+l-j;return{top:m,left:n}}:cv=function(a,b,c){var d,e=a.offsetParent,g=a,h=b.body,i=b.defaultView,j=i?i.getComputedStyle(a,null):a.currentStyle,k=a.offsetTop,l=a.offsetLeft;while((a=a.parentNode)&&a!==h&&a!==c){if(f.support.fixedPosition&&j.position==="fixed")break;d=i?i.getComputedStyle(a,null):a.currentStyle,k-=a.scrollTop,l-=a.scrollLeft,a===e&&(k+=a.offsetTop,l+=a.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(a.nodeName))&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),g=e,e=a.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),j=d}if(j.position==="relative"||j.position==="static")k+=h.offsetTop,l+=h.offsetLeft;f.support.fixedPosition&&j.position==="fixed"&&(k+=Math.max(c.scrollTop,h.scrollTop),l+=Math.max(c.scrollLeft,h.scrollLeft));return{top:k,left:l}},f.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){f.offset.setOffset(this,a,b)});var c=this[0],d=c&&c.ownerDocument;if(!d)return null;if(c===d.body)return f.offset.bodyOffset(c);return cv(c,d,d.documentElement)},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window);
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancExplorer/libs/jquery.min.js	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,4 @@
+/*! jQuery v1.7.2 jquery.com | jquery.org/license */
+(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"<!doctype html>":"")+"<html><body>"),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function ca(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function b_(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bD.test(a)?d(a,e):b_(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&f.type(b)==="object")for(var e in b)b_(a+"["+e+"]",b[e],c,d);else d(a,b)}function b$(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function bZ(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bS,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bZ(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bZ(a,c,d,e,"*",g));return l}function bY(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bO),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bB(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?1:0,g=4;if(d>0){if(c!=="border")for(;e<g;e+=2)c||(d-=parseFloat(f.css(a,"padding"+bx[e]))||0),c==="margin"?d+=parseFloat(f.css(a,c+bx[e]))||0:d-=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0;return d+"px"}d=by(a,b);if(d<0||d==null)d=a.style[b];if(bt.test(d))return d;d=parseFloat(d)||0;if(c)for(;e<g;e+=2)d+=parseFloat(f.css(a,"padding"+bx[e]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+bx[e]))||0);return d+"px"}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;b.nodeType===1&&(b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?b.outerHTML=a.outerHTML:c!=="input"||a.type!=="checkbox"&&a.type!=="radio"?c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text):(a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value)),b.removeAttribute(f.expando),b.removeAttribute("_submit_attached"),b.removeAttribute("_change_attached"))}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c,i[c][d])}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h,i){var j,k=d==null,l=0,m=a.length;if(d&&typeof d=="object"){for(l in d)e.access(a,c,l,d[l],1,h,f);g=1}else if(f!==b){j=i===b&&e.isFunction(f),k&&(j?(j=c,c=function(a,b,c){return j.call(e(a),c)}):(c.call(a,f),c=null));if(c)for(;l<m;l++)c(a[l],d,j?f.call(a[l],l,c(a[l],d)):f,i);g=1}return g?a:k?c.call(a):m?c(a[0],d):h},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m,n=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?n(g):h==="function"&&(!a.unique||!p.has(g))&&c.push(g)},o=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,j=!0,m=k||0,k=0,l=c.length;for(;c&&m<l;m++)if(c[m].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}j=!1,c&&(a.once?e===!0?p.disable():c=[]:d&&d.length&&(e=d.shift(),p.fireWith(e[0],e[1])))},p={add:function(){if(c){var a=c.length;n(arguments),j?l=c.length:e&&e!==!0&&(k=a,o(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){j&&f<=l&&(l--,f<=m&&m--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&p.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(j?a.once||d.push([b,c]):(!a.once||!e)&&o(b,c));return this},fire:function(){p.fireWith(this,arguments);return this},fired:function(){return!!i}};return p};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p=c.createElement("div"),q=c.documentElement;p.setAttribute("className","t"),p.innerHTML="   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="<div "+n+"display:block;'><div style='"+t+"0;display:block;overflow:hidden;'></div></div>"+"<table "+n+"' cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="<table><tr><td style='"+t+"0;display:none'></td><td>t</td></tr></table>",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="<div style='width:5px;'></div>",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h,i,j=this[0],k=0,m=null;if(a===b){if(this.length){m=f.data(j);if(j.nodeType===1&&!f._data(j,"parsedAttrs")){g=j.attributes;for(i=g.length;k<i;k++)h=g[k].name,h.indexOf("data-")===0&&(h=f.camelCase(h.substring(5)),l(j,h,m[h]));f._data(j,"parsedAttrs",!0)}}return m}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!";return f.access(this,function(c){if(c===b){m=this.triggerHandler("getData"+e,[d[0]]),m===b&&j&&(m=f.data(j,a),m=l(j,a,m));return m===b&&d[1]?this.data(d[0]):m}d[1]=c,this.each(function(){var b=f(this);b.triggerHandler("setData"+e,d),f.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length<d)return f.queue(this[0],a);return c===b?this:this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise(c)}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,f.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i<g;i++)e=d[i],e&&(c=f.propFix[e]||e,h=u.test(e),h||f.attr(a,e,""),a.removeAttribute(v?e:c),h&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0,coords:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(
+a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:g&&G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=f.event.special[c.type]||{},j=[],k,l,m,n,o,p,q,r,s,t,u;g[0]=c,c.delegateTarget=this;if(!i.preDispatch||i.preDispatch.call(this,c)!==!1){if(e&&(!c.button||c.type!=="click")){n=f(this),n.context=this.ownerDocument||this;for(m=c.target;m!=this;m=m.parentNode||this)if(m.disabled!==!0){p={},r=[],n[0]=m;for(k=0;k<e;k++)s=d[k],t=s.selector,p[t]===b&&(p[t]=s.quick?H(m,s.quick):n.is(t)),p[t]&&r.push(s);r.length&&j.push({elem:m,matches:r})}}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k<j.length&&!c.isPropagationStopped();k++){q=j[k],c.currentTarget=q.elem;for(l=0;l<q.matches.length&&!c.isImmediatePropagationStopped();l++){s=q.matches[l];if(h||!c.namespace&&!s.namespace||c.namespace_re&&c.namespace_re.test(s.namespace))c.data=s.data,c.handleObj=s,o=((f.event.special[s.origType]||{}).handle||s.handler).apply(q.elem,g),o!==b&&(c.result=o,o===!1&&(c.preventDefault(),c.stopPropagation()))}}i.postDispatch&&i.postDispatch.call(this,c);return c.result}},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),d._submit_attached=!0)})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9||d===11){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.globalPOS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")[\\s/>]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f
+.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(f.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(g){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,function(a,b){b.src?f.ajax({type:"GET",global:!1,url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1></$2>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]==="<table>"&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i<u;i++)bn(l[i]);else bn(l);l.nodeType?j.push(l):j=f.merge(j,l)}if(d){g=function(a){return!a.type||be.test(a.type)};for(k=0;j[k];k++){h=j[k];if(e&&f.nodeName(h,"script")&&(!h.type||be.test(h.type)))e.push(h.parentNode?h.parentNode.removeChild(h):h);else{if(h.nodeType===1){var v=f.grep(h.getElementsByTagName("script"),g);j.splice.apply(j,[k+1,0].concat(v))}d.appendChild(h)}}}return j},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bp=/alpha\([^)]*\)/i,bq=/opacity=([^)]*)/,br=/([A-Z]|^ms)/g,bs=/^[\-+]?(?:\d*\.)?\d+$/i,bt=/^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,bu=/^([\-+])=([\-+.\de]+)/,bv=/^margin/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Top","Right","Bottom","Left"],by,bz,bA;f.fn.css=function(a,c){return f.access(this,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)},a,c,arguments.length>1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),(e===""&&f.css(d,"display")==="none"||!f.contains(d.ownerDocument.documentElement,d))&&f._data(d,"olddisplay",cu(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(ct("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(ct("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o,p,q;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]);if((k=f.cssHooks[g])&&"expand"in k){l=k.expand(a[g]),delete a[g];for(i in l)i in a||(a[i]=l[i])}}for(g in a){h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cu(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cm.test(h)?(q=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),q?(f._data(this,"toggle"+i,q==="show"?"hide":"show"),j[q]()):j[h]()):(m=cn.exec(h),n=j.cur(),m?(o=parseFloat(m[2]),p=m[3]||(f.cssNumber[i]?"":"px"),p!=="px"&&(f.style(this,i,(o||1)+p),n=(o||1)/j.cur()*n,f.style(this,i,n+p)),m[1]&&(o=(m[1]==="-="?-1:1)*o+n),j.custom(n,o,p)):j.custom(n,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:ct("show",1),slideUp:ct("hide",1),slideToggle:ct("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a){return a},swing:function(a){return-Math.cos(a*Math.PI)/2+.5}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cq||cr(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){f._data(e.elem,"fxshow"+e.prop)===b&&(e.options.hide?f._data(e.elem,"fxshow"+e.prop,e.start):e.options.show&&f._data(e.elem,"fxshow"+e.prop,e.end))},h()&&f.timers.push(h)&&!co&&(co=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cq||cr(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(co),co=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(cp.concat.apply([],cp),function(a,b){b.indexOf("margin")&&(f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)})}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cv,cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?cv=function(a,b,c,d){try{d=a.getBoundingClientRect()}catch(e){}if(!d||!f.contains(c,a))return d?{top:d.top,left:d.left}:{top:0,left:0};var g=b.body,h=cy(b),i=c.clientTop||g.clientTop||0,j=c.clientLeft||g.clientLeft||0,k=h.pageYOffset||f.support.boxModel&&c.scrollTop||g.scrollTop,l=h.pageXOffset||f.support.boxModel&&c.scrollLeft||g.scrollLeft,m=d.top+k-i,n=d.left+l-j;return{top:m,left:n}}:cv=function(a,b,c){var d,e=a.offsetParent,g=a,h=b.body,i=b.defaultView,j=i?i.getComputedStyle(a,null):a.currentStyle,k=a.offsetTop,l=a.offsetLeft;while((a=a.parentNode)&&a!==h&&a!==c){if(f.support.fixedPosition&&j.position==="fixed")break;d=i?i.getComputedStyle(a,null):a.currentStyle,k-=a.scrollTop,l-=a.scrollLeft,a===e&&(k+=a.offsetTop,l+=a.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(a.nodeName))&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),g=e,e=a.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),j=d}if(j.position==="relative"||j.position==="static")k+=h.offsetTop,l+=h.offsetLeft;f.support.fixedPosition&&j.position==="fixed"&&(k+=Math.max(c.scrollTop,h.scrollTop),l+=Math.max(c.scrollLeft,h.scrollLeft));return{top:k,left:l}},f.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){f.offset.setOffset(this,a,b)});var c=this[0],d=c&&c.ownerDocument;if(!d)return null;if(c===d.body)return f.offset.bodyOffset(c);return cv(c,d,d.documentElement)},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window);
\ No newline at end of file
--- a/OrthancExplorer/libs/jquery.mobile-1.1.0.min.css	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-/*! jQuery Mobile v1.1.0 db342b1f315c282692791aa870455901fdb46a55 jquerymobile.com | jquery.org/license */
-.ui-bar-a{border:1px solid #333;background:#111;color:#fff;font-weight:bold;text-shadow:0 -1px 1px #000;background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#111));background-image:-webkit-linear-gradient(#3c3c3c,#111);background-image:-moz-linear-gradient(#3c3c3c,#111);background-image:-ms-linear-gradient(#3c3c3c,#111);background-image:-o-linear-gradient(#3c3c3c,#111);background-image:linear-gradient(#3c3c3c,#111)}.ui-bar-a,.ui-bar-a input,.ui-bar-a select,.ui-bar-a textarea,.ui-bar-a button{font-family:Helvetica,Arial,sans-serif}.ui-bar-a .ui-link-inherit{color:#fff}.ui-bar-a .ui-link{color:#7cc4e7;font-weight:bold}.ui-bar-a .ui-link:hover{color:#2489ce}.ui-bar-a .ui-link:active{color:#2489ce}.ui-bar-a .ui-link:visited{color:#2489ce}.ui-body-a,.ui-overlay-a{border:1px solid #444;background:#222;color:#fff;text-shadow:0 1px 1px #111;font-weight:normal;background-image:-webkit-gradient(linear,left top,left bottom,from(#444),to(#222));background-image:-webkit-linear-gradient(#444,#222);background-image:-moz-linear-gradient(#444,#222);background-image:-ms-linear-gradient(#444,#222);background-image:-o-linear-gradient(#444,#222);background-image:linear-gradient(#444,#222)}.ui-overlay-a{background-image:none;border-width:0}.ui-body-a,.ui-body-a input,.ui-body-a select,.ui-body-a textarea,.ui-body-a button{font-family:Helvetica,Arial,sans-serif}.ui-body-a .ui-link-inherit{color:#fff}.ui-body-a .ui-link{color:#2489ce;font-weight:bold}.ui-body-a .ui-link:hover{color:#2489ce}.ui-body-a .ui-link:active{color:#2489ce}.ui-body-a .ui-link:visited{color:#2489ce}.ui-btn-up-a{border:1px solid #111;background:#333;font-weight:bold;color:#fff;text-shadow:0 1px 1px #111;background-image:-webkit-gradient(linear,left top,left bottom,from(#444),to(#2d2d2d));background-image:-webkit-linear-gradient(#444,#2d2d2d);background-image:-moz-linear-gradient(#444,#2d2d2d);background-image:-ms-linear-gradient(#444,#2d2d2d);background-image:-o-linear-gradient(#444,#2d2d2d);background-image:linear-gradient(#444,#2d2d2d)}.ui-btn-up-a a.ui-link-inherit{color:#fff}.ui-btn-hover-a{border:1px solid #000;background:#444;font-weight:bold;color:#fff;text-shadow:0 1px 1px #111;background-image:-webkit-gradient(linear,left top,left bottom,from(#555),to(#383838));background-image:-webkit-linear-gradient(#555,#383838);background-image:-moz-linear-gradient(#555,#383838);background-image:-ms-linear-gradient(#555,#383838);background-image:-o-linear-gradient(#555,#383838);background-image:linear-gradient(#555,#383838)}.ui-btn-hover-a a.ui-link-inherit{color:#fff}.ui-btn-down-a{border:1px solid #000;background:#222;font-weight:bold;color:#fff;text-shadow:0 1px 1px #111;background-image:-webkit-gradient(linear,left top,left bottom,from(#202020),to(#2c2c2c));background-image:-webkit-linear-gradient(#202020,#2c2c2c);background-image:-moz-linear-gradient(#202020,#2c2c2c);background-image:-ms-linear-gradient(#202020,#2c2c2c);background-image:-o-linear-gradient(#202020,#2c2c2c);background-image:linear-gradient(#202020,#2c2c2c)}.ui-btn-down-a a.ui-link-inherit{color:#fff}.ui-btn-up-a,.ui-btn-hover-a,.ui-btn-down-a{font-family:Helvetica,Arial,sans-serif;text-decoration:none}.ui-bar-b{border:1px solid #456f9a;background:#5e87b0;color:#fff;font-weight:bold;text-shadow:0 1px 1px #3e6790;background-image:-webkit-gradient(linear,left top,left bottom,from(#6facd5),to(#497bae));background-image:-webkit-linear-gradient(#6facd5,#497bae);background-image:-moz-linear-gradient(#6facd5,#497bae);background-image:-ms-linear-gradient(#6facd5,#497bae);background-image:-o-linear-gradient(#6facd5,#497bae);background-image:linear-gradient(#6facd5,#497bae)}.ui-bar-b,.ui-bar-b input,.ui-bar-b select,.ui-bar-b textarea,.ui-bar-b button{font-family:Helvetica,Arial,sans-serif}.ui-bar-b .ui-link-inherit{color:#fff}.ui-bar-b .ui-link{color:#ddf0f8;font-weight:bold}.ui-bar-b .ui-link:hover{color:#ddf0f8}.ui-bar-b .ui-link:active{color:#ddf0f8}.ui-bar-b .ui-link:visited{color:#ddf0f8}.ui-body-b,.ui-overlay-b{border:1px solid #999;background:#f3f3f3;color:#222;text-shadow:0 1px 0 #fff;font-weight:normal;background-image:-webkit-gradient(linear,left top,left bottom,from(#ddd),to(#ccc));background-image:-webkit-linear-gradient(#ddd,#ccc);background-image:-moz-linear-gradient(#ddd,#ccc);background-image:-ms-linear-gradient(#ddd,#ccc);background-image:-o-linear-gradient(#ddd,#ccc);background-image:linear-gradient(#ddd,#ccc)}.ui-overlay-b{background-image:none;border-width:0}.ui-body-b,.ui-body-b input,.ui-body-b select,.ui-body-b textarea,.ui-body-b button{font-family:Helvetica,Arial,sans-serif}.ui-body-b .ui-link-inherit{color:#333}.ui-body-b .ui-link{color:#2489ce;font-weight:bold}.ui-body-b .ui-link:hover{color:#2489ce}.ui-body-b .ui-link:active{color:#2489ce}.ui-body-b .ui-link:visited{color:#2489ce}.ui-btn-up-b{border:1px solid #044062;background:#396b9e;font-weight:bold;color:#fff;text-shadow:0 1px 1px #194b7e;background-image:-webkit-gradient(linear,left top,left bottom,from(#5f9cc5),to(#396b9e));background-image:-webkit-linear-gradient(#5f9cc5,#396b9e);background-image:-moz-linear-gradient(#5f9cc5,#396b9e);background-image:-ms-linear-gradient(#5f9cc5,#396b9e);background-image:-o-linear-gradient(#5f9cc5,#396b9e);background-image:linear-gradient(#5f9cc5,#396b9e)}.ui-btn-up-b a.ui-link-inherit{color:#fff}.ui-btn-hover-b{border:1px solid #00415e;background:#4b88b6;font-weight:bold;color:#fff;text-shadow:0 1px 1px #194b7e;background-image:-webkit-gradient(linear,left top,left bottom,from(#6facd5),to(#4272a4));background-image:-webkit-linear-gradient(#6facd5,#4272a4);background-image:-moz-linear-gradient(#6facd5,#4272a4);background-image:-ms-linear-gradient(#6facd5,#4272a4);background-image:-o-linear-gradient(#6facd5,#4272a4);background-image:linear-gradient(#6facd5,#4272a4)}.ui-btn-hover-b a.ui-link-inherit{color:#fff}.ui-btn-down-b{border:1px solid #225377;background:#4e89c5;font-weight:bold;color:#fff;text-shadow:0 1px 1px #194b7e;background-image:-webkit-gradient(linear,left top,left bottom,from(#295b8e),to(#3e79b5));background-image:-webkit-linear-gradient(#295b8e,#3e79b5);background-image:-moz-linear-gradient(#295b8e,#3e79b5);background-image:-ms-linear-gradient(#295b8e,#3e79b5);background-image:-o-linear-gradient(#295b8e,#3e79b5);background-image:linear-gradient(#295b8e,#3e79b5)}.ui-btn-down-b a.ui-link-inherit{color:#fff}.ui-btn-up-b,.ui-btn-hover-b,.ui-btn-down-b{font-family:Helvetica,Arial,sans-serif;text-decoration:none}.ui-bar-c{border:1px solid #b3b3b3;background:#eee;color:#3e3e3e;font-weight:bold;text-shadow:0 1px 1px #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#f0f0f0),to(#ddd));background-image:-webkit-linear-gradient(#f0f0f0,#ddd);background-image:-moz-linear-gradient(#f0f0f0,#ddd);background-image:-ms-linear-gradient(#f0f0f0,#ddd);background-image:-o-linear-gradient(#f0f0f0,#ddd);background-image:linear-gradient(#f0f0f0,#ddd)}.ui-bar-c .ui-link-inherit{color:#3e3e3e}.ui-bar-c .ui-link{color:#7cc4e7;font-weight:bold}.ui-bar-c .ui-link:hover{color:#2489ce}.ui-bar-c .ui-link:active{color:#2489ce}.ui-bar-c .ui-link:visited{color:#2489ce}.ui-bar-c,.ui-bar-c input,.ui-bar-c select,.ui-bar-c textarea,.ui-bar-c button{font-family:Helvetica,Arial,sans-serif}.ui-body-c,.ui-overlay-c{border:1px solid #aaa;color:#333;text-shadow:0 1px 0 #fff;background:#f9f9f9;background-image:-webkit-gradient(linear,left top,left bottom,from(#f9f9f9),to(#eee));background-image:-webkit-linear-gradient(#f9f9f9,#eee);background-image:-moz-linear-gradient(#f9f9f9,#eee);background-image:-ms-linear-gradient(#f9f9f9,#eee);background-image:-o-linear-gradient(#f9f9f9,#eee);background-image:linear-gradient(#f9f9f9,#eee)}.ui-overlay-c{background-image:none;border-width:0}.ui-body-c,.ui-body-c input,.ui-body-c select,.ui-body-c textarea,.ui-body-c button{font-family:Helvetica,Arial,sans-serif}.ui-body-c .ui-link-inherit{color:#333}.ui-body-c .ui-link{color:#2489ce;font-weight:bold}.ui-body-c .ui-link:hover{color:#2489ce}.ui-body-c .ui-link:active{color:#2489ce}.ui-body-c .ui-link:visited{color:#2489ce}.ui-btn-up-c{border:1px solid #ccc;background:#eee;font-weight:bold;color:#222;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f1f1f1));background-image:-webkit-linear-gradient(#fff,#f1f1f1);background-image:-moz-linear-gradient(#fff,#f1f1f1);background-image:-ms-linear-gradient(#fff,#f1f1f1);background-image:-o-linear-gradient(#fff,#f1f1f1);background-image:linear-gradient(#fff,#f1f1f1)}.ui-btn-up-c a.ui-link-inherit{color:#2f3e46}.ui-btn-hover-c{border:1px solid #bbb;background:#dfdfdf;font-weight:bold;color:#222;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#f6f6f6),to(#e0e0e0));background-image:-webkit-linear-gradient(#f9f9f9,#e0e0e0);background-image:-moz-linear-gradient(#f6f6f6,#e0e0e0);background-image:-ms-linear-gradient(#f6f6f6,#e0e0e0);background-image:-o-linear-gradient(#f6f6f6,#e0e0e0);background-image:linear-gradient(#f6f6f6,#e0e0e0)}.ui-btn-hover-c a.ui-link-inherit{color:#2f3e46}.ui-btn-down-c{border:1px solid #bbb;background:#d6d6d6;font-weight:bold;color:#222;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#d0d0d0),to(#dfdfdf));background-image:-webkit-linear-gradient(#d0d0d0,#dfdfdf);background-image:-moz-linear-gradient(#d0d0d0,#dfdfdf);background-image:-ms-linear-gradient(#d0d0d0,#dfdfdf);background-image:-o-linear-gradient(#d0d0d0,#dfdfdf);background-image:linear-gradient(#d0d0d0,#dfdfdf)}.ui-btn-down-c a.ui-link-inherit{color:#2f3e46}.ui-btn-up-c,.ui-btn-hover-c,.ui-btn-down-c{font-family:Helvetica,Arial,sans-serif;text-decoration:none}.ui-bar-d{border:1px solid #bbb;background:#bbb;color:#333;text-shadow:0 1px 0 #eee;background-image:-webkit-gradient(linear,left top,left bottom,from(#ddd),to(#bbb));background-image:-webkit-linear-gradient(#ddd,#bbb);background-image:-moz-linear-gradient(#ddd,#bbb);background-image:-ms-linear-gradient(#ddd,#bbb);background-image:-o-linear-gradient(#ddd,#bbb);background-image:linear-gradient(#ddd,#bbb)}.ui-bar-d,.ui-bar-d input,.ui-bar-d select,.ui-bar-d textarea,.ui-bar-d button{font-family:Helvetica,Arial,sans-serif}.ui-bar-d .ui-link-inherit{color:#333}.ui-bar-d .ui-link{color:#2489ce;font-weight:bold}.ui-bar-d .ui-link:hover{color:#2489ce}.ui-bar-d .ui-link:active{color:#2489ce}.ui-bar-d .ui-link:visited{color:#2489ce}.ui-body-d,.ui-overlay-d{border:1px solid #bbb;color:#333;text-shadow:0 1px 0 #fff;background:#fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#fff));background-image:-webkit-linear-gradient(#fff,#fff);background-image:-moz-linear-gradient(#fff,#fff);background-image:-ms-linear-gradient(#fff,#fff);background-image:-o-linear-gradient(#fff,#fff);background-image:linear-gradient(#fff,#fff)}.ui-overlay-d{background-image:none;border-width:0}.ui-body-d,.ui-body-d input,.ui-body-d select,.ui-body-d textarea,.ui-body-d button{font-family:Helvetica,Arial,sans-serif}.ui-body-d .ui-link-inherit{color:#333}.ui-body-d .ui-link{color:#2489ce;font-weight:bold}.ui-body-d .ui-link:hover{color:#2489ce}.ui-body-d .ui-link:active{color:#2489ce}.ui-body-d .ui-link:visited{color:#2489ce}.ui-btn-up-d{border:1px solid #bbb;background:#fff;font-weight:bold;color:#333;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fafafa),to(#f6f6f6));background-image:-webkit-linear-gradient(#fafafa,#f6f6f6);background-image:-moz-linear-gradient(#fafafa,#f6f6f6);background-image:-ms-linear-gradient(#fafafa,#f6f6f6);background-image:-o-linear-gradient(#fafafa,#f6f6f6);background-image:linear-gradient(#fafafa,#f6f6f6)}.ui-btn-up-d a.ui-link-inherit{color:#333}.ui-btn-hover-d{border:1px solid #aaa;background:#eee;font-weight:bold;color:#333;cursor:pointer;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#eee),to(#fff));background-image:-webkit-linear-gradient(#eee,#fff);background-image:-moz-linear-gradient(#eee,#fff);background-image:-ms-linear-gradient(#eee,#fff);background-image:-o-linear-gradient(#eee,#fff);background-image:linear-gradient(#eee,#fff)}.ui-btn-hover-d a.ui-link-inherit{color:#333}.ui-btn-down-d{border:1px solid #aaa;background:#eee;font-weight:bold;color:#333;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#e5e5e5),to(#f2f2f2));background-image:-webkit-linear-gradient(#e5e5e5,#f2f2f2);background-image:-moz-linear-gradient(#e5e5e5,#f2f2f2);background-image:-ms-linear-gradient(#e5e5e5,#f2f2f2);background-image:-o-linear-gradient(#e5e5e5,#f2f2f2);background-image:linear-gradient(#e5e5e5,#f2f2f2)}.ui-btn-down-d a.ui-link-inherit{color:#333}.ui-btn-up-d,.ui-btn-hover-d,.ui-btn-down-d{font-family:Helvetica,Arial,sans-serif;text-decoration:none}.ui-bar-e{border:1px solid #f7c942;background:#fadb4e;color:#333;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fceda7),to(#fbef7e));background-image:-webkit-linear-gradient(#fceda7,#fbef7e);background-image:-moz-linear-gradient(#fceda7,#fbef7e);background-image:-ms-linear-gradient(#fceda7,#fbef7e);background-image:-o-linear-gradient(#fceda7,#fbef7e);background-image:linear-gradient(#fceda7,#fbef7e)}.ui-bar-e,.ui-bar-e input,.ui-bar-e select,.ui-bar-e textarea,.ui-bar-e button{font-family:Helvetica,Arial,sans-serif}.ui-bar-e .ui-link-inherit{color:#333}.ui-bar-e .ui-link{color:#2489ce;font-weight:bold}.ui-bar-e .ui-link:hover{color:#2489ce}.ui-bar-e .ui-link:active{color:#2489ce}.ui-bar-e .ui-link:visited{color:#2489ce}.ui-body-e,.ui-overlay-e{border:1px solid #f7c942;color:#222;text-shadow:0 1px 0 #fff;background:#fff9df;background-image:-webkit-gradient(linear,left top,left bottom,from(#fffadf),to(#fff3a5));background-image:-webkit-linear-gradient(#fffadf,#fff3a5);background-image:-moz-linear-gradient(#fffadf,#fff3a5);background-image:-ms-linear-gradient(#fffadf,#fff3a5);background-image:-o-linear-gradient(#fffadf,#fff3a5);background-image:linear-gradient(#fffadf,#fff3a5)}.ui-overlay-e{background-image:none;border-width:0}.ui-body-e,.ui-body-e input,.ui-body-e select,.ui-body-e textarea,.ui-body-e button{font-family:Helvetica,Arial,sans-serif}.ui-body-e .ui-link-inherit{color:#333}.ui-body-e .ui-link{color:#2489ce;font-weight:bold}.ui-body-e .ui-link:hover{color:#2489ce}.ui-body-e .ui-link:active{color:#2489ce}.ui-body-e .ui-link:visited{color:#2489ce}.ui-btn-up-e{border:1px solid #f4c63f;background:#fadb4e;font-weight:bold;color:#222;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#ffefaa),to(#ffe155));background-image:-webkit-linear-gradient(#ffefaa,#ffe155);background-image:-moz-linear-gradient(#ffefaa,#ffe155);background-image:-ms-linear-gradient(#ffefaa,#ffe155);background-image:-o-linear-gradient(#ffefaa,#ffe155);background-image:linear-gradient(#ffefaa,#ffe155)}.ui-btn-up-e a.ui-link-inherit{color:#222}.ui-btn-hover-e{border:1px solid #f2c43d;background:#fbe26f;font-weight:bold;color:#111;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fff5ba),to(#fbdd52));background-image:-webkit-linear-gradient(#fff5ba,#fbdd52);background-image:-moz-linear-gradient(#fff5ba,#fbdd52);background-image:-ms-linear-gradient(#fff5ba,#fbdd52);background-image:-o-linear-gradient(#fff5ba,#fbdd52);background-image:linear-gradient(#fff5ba,#fbdd52)}.ui-btn-hover-e a.ui-link-inherit{color:#333}.ui-btn-down-e{border:1px solid #f2c43d;background:#fceda7;font-weight:bold;color:#111;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#f8d94c),to(#fadb4e));background-image:-webkit-linear-gradient(#f8d94c,#fadb4e);background-image:-moz-linear-gradient(#f8d94c,#fadb4e);background-image:-ms-linear-gradient(#f8d94c,#fadb4e);background-image:-o-linear-gradient(#f8d94c,#fadb4e);background-image:linear-gradient(#f8d94c,#fadb4e)}.ui-btn-down-e a.ui-link-inherit{color:#333}.ui-btn-up-e,.ui-btn-hover-e,.ui-btn-down-e{font-family:Helvetica,Arial,sans-serif;text-decoration:none}a.ui-link-inherit{text-decoration:none!important}.ui-btn-active{border:1px solid #2373a5;background:#5393c5;font-weight:bold;color:#fff;cursor:pointer;text-shadow:0 1px 1px #3373a5;text-decoration:none;background-image:-webkit-gradient(linear,left top,left bottom,from(#5393c5),to(#6facd5));background-image:-webkit-linear-gradient(#5393c5,#6facd5);background-image:-moz-linear-gradient(#5393c5,#6facd5);background-image:-ms-linear-gradient(#5393c5,#6facd5);background-image:-o-linear-gradient(#5393c5,#6facd5);background-image:linear-gradient(#5393c5,#6facd5);font-family:Helvetica,Arial,sans-serif}.ui-btn-active a.ui-link-inherit{color:#fff}.ui-btn-inner{border-top:1px solid #fff;border-color:rgba(255,255,255,.3)}.ui-corner-tl{-moz-border-radius-topleft:.6em;-webkit-border-top-left-radius:.6em;border-top-left-radius:.6em}.ui-corner-tr{-moz-border-radius-topright:.6em;-webkit-border-top-right-radius:.6em;border-top-right-radius:.6em}.ui-corner-bl{-moz-border-radius-bottomleft:.6em;-webkit-border-bottom-left-radius:.6em;border-bottom-left-radius:.6em}.ui-corner-br{-moz-border-radius-bottomright:.6em;-webkit-border-bottom-right-radius:.6em;border-bottom-right-radius:.6em}.ui-corner-top{-moz-border-radius-topleft:.6em;-webkit-border-top-left-radius:.6em;border-top-left-radius:.6em;-moz-border-radius-topright:.6em;-webkit-border-top-right-radius:.6em;border-top-right-radius:.6em}.ui-corner-bottom{-moz-border-radius-bottomleft:.6em;-webkit-border-bottom-left-radius:.6em;border-bottom-left-radius:.6em;-moz-border-radius-bottomright:.6em;-webkit-border-bottom-right-radius:.6em;border-bottom-right-radius:.6em}.ui-corner-right{-moz-border-radius-topright:.6em;-webkit-border-top-right-radius:.6em;border-top-right-radius:.6em;-moz-border-radius-bottomright:.6em;-webkit-border-bottom-right-radius:.6em;border-bottom-right-radius:.6em}.ui-corner-left{-moz-border-radius-topleft:.6em;-webkit-border-top-left-radius:.6em;border-top-left-radius:.6em;-moz-border-radius-bottomleft:.6em;-webkit-border-bottom-left-radius:.6em;border-bottom-left-radius:.6em}.ui-corner-all{-moz-border-radius:.6em;-webkit-border-radius:.6em;border-radius:.6em}.ui-corner-none{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.ui-br{border-bottom:#828282;border-bottom:rgba(130,130,130,.3);border-bottom-width:1px;border-bottom-style:solid}.ui-disabled{opacity:.3}.ui-disabled,.ui-disabled a{cursor:default!important;pointer-events:none}.ui-disabled .ui-btn-text{-ms-filter:"alpha(opacity=30)";filter:alpha(opacity=30);zoom:1}.ui-icon,.ui-icon-searchfield:after{background:#666;background:rgba(0,0,0,.4);background-image:url(images/icons-18-white.png);background-repeat:no-repeat;-moz-border-radius:9px;-webkit-border-radius:9px;border-radius:9px}.ui-icon-alt{background:#fff;background:rgba(255,255,255,.3);background-image:url(images/icons-18-black.png);background-repeat:no-repeat}@media only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min--moz-device-pixel-ratio:1.5),only screen and (min-resolution:240dpi){.ui-icon-plus,.ui-icon-minus,.ui-icon-delete,.ui-icon-arrow-r,.ui-icon-arrow-l,.ui-icon-arrow-u,.ui-icon-arrow-d,.ui-icon-check,.ui-icon-gear,.ui-icon-refresh,.ui-icon-forward,.ui-icon-back,.ui-icon-grid,.ui-icon-star,.ui-icon-alert,.ui-icon-info,.ui-icon-home,.ui-icon-search,.ui-icon-searchfield:after,.ui-icon-checkbox-off,.ui-icon-checkbox-on,.ui-icon-radio-off,.ui-icon-radio-on{background-image:url(images/icons-36-white.png);-moz-background-size:776px 18px;-o-background-size:776px 18px;-webkit-background-size:776px 18px;background-size:776px 18px}.ui-icon-alt{background-image:url(images/icons-36-black.png)}}.ui-icon-plus{background-position:-0 50%}.ui-icon-minus{background-position:-36px 50%}.ui-icon-delete{background-position:-72px 50%}.ui-icon-arrow-r{background-position:-108px 50%}.ui-icon-arrow-l{background-position:-144px 50%}.ui-icon-arrow-u{background-position:-180px 50%}.ui-icon-arrow-d{background-position:-216px 50%}.ui-icon-check{background-position:-252px 50%}.ui-icon-gear{background-position:-288px 50%}.ui-icon-refresh{background-position:-324px 50%}.ui-icon-forward{background-position:-360px 50%}.ui-icon-back{background-position:-396px 50%}.ui-icon-grid{background-position:-432px 50%}.ui-icon-star{background-position:-468px 50%}.ui-icon-alert{background-position:-504px 50%}.ui-icon-info{background-position:-540px 50%}.ui-icon-home{background-position:-576px 50%}.ui-icon-search,.ui-icon-searchfield:after{background-position:-612px 50%}.ui-icon-checkbox-off{background-position:-684px 50%}.ui-icon-checkbox-on{background-position:-648px 50%}.ui-icon-radio-off{background-position:-756px 50%}.ui-icon-radio-on{background-position:-720px 50%}.ui-checkbox .ui-icon{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}.ui-icon-checkbox-off,.ui-icon-radio-off{background-color:transparent}.ui-checkbox-on .ui-icon,.ui-radio-on .ui-icon{background-color:#4596ce}.ui-icon-loading{background:url(images/ajax-loader.gif);background-size:46px 46px}.ui-btn-corner-tl{-moz-border-radius-topleft:1em;-webkit-border-top-left-radius:1em;border-top-left-radius:1em}.ui-btn-corner-tr{-moz-border-radius-topright:1em;-webkit-border-top-right-radius:1em;border-top-right-radius:1em}.ui-btn-corner-bl{-moz-border-radius-bottomleft:1em;-webkit-border-bottom-left-radius:1em;border-bottom-left-radius:1em}.ui-btn-corner-br{-moz-border-radius-bottomright:1em;-webkit-border-bottom-right-radius:1em;border-bottom-right-radius:1em}.ui-btn-corner-top{-moz-border-radius-topleft:1em;-webkit-border-top-left-radius:1em;border-top-left-radius:1em;-moz-border-radius-topright:1em;-webkit-border-top-right-radius:1em;border-top-right-radius:1em}.ui-btn-corner-bottom{-moz-border-radius-bottomleft:1em;-webkit-border-bottom-left-radius:1em;border-bottom-left-radius:1em;-moz-border-radius-bottomright:1em;-webkit-border-bottom-right-radius:1em;border-bottom-right-radius:1em}.ui-btn-corner-right{-moz-border-radius-topright:1em;-webkit-border-top-right-radius:1em;border-top-right-radius:1em;-moz-border-radius-bottomright:1em;-webkit-border-bottom-right-radius:1em;border-bottom-right-radius:1em}.ui-btn-corner-left{-moz-border-radius-topleft:1em;-webkit-border-top-left-radius:1em;border-top-left-radius:1em;-moz-border-radius-bottomleft:1em;-webkit-border-bottom-left-radius:1em;border-bottom-left-radius:1em}.ui-btn-corner-all{-moz-border-radius:1em;-webkit-border-radius:1em;border-radius:1em}.ui-corner-tl,.ui-corner-tr,.ui-corner-bl,.ui-corner-br,.ui-corner-top,.ui-corner-bottom,.ui-corner-right,.ui-corner-left,.ui-corner-all,.ui-btn-corner-tl,.ui-btn-corner-tr,.ui-btn-corner-bl,.ui-btn-corner-br,.ui-btn-corner-top,.ui-btn-corner-bottom,.ui-btn-corner-right,.ui-btn-corner-left,.ui-btn-corner-all{-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.ui-overlay{background:#666;opacity:.5;filter:Alpha(Opacity=50);position:absolute;width:100%;height:100%}.ui-overlay-shadow{-moz-box-shadow:0 0 12px rgba(0,0,0,.6);-webkit-box-shadow:0 0 12px rgba(0,0,0,.6);box-shadow:0 0 12px rgba(0,0,0,.6)}.ui-shadow{-moz-box-shadow:0 1px 4px rgba(0,0,0,.3);-webkit-box-shadow:0 1px 4px rgba(0,0,0,.3);box-shadow:0 1px 4px rgba(0,0,0,.3)}.ui-bar-a .ui-shadow,.ui-bar-b .ui-shadow,.ui-bar-c .ui-shadow{-moz-box-shadow:0 1px 0 rgba(255,255,255,.3);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.3);box-shadow:0 1px 0 rgba(255,255,255,.3)}.ui-shadow-inset{-moz-box-shadow:inset 0 1px 4px rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 4px rgba(0,0,0,.2);box-shadow:inset 0 1px 4px rgba(0,0,0,.2)}.ui-icon-shadow{-moz-box-shadow:0 1px 0 rgba(255,255,255,.4);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.4);box-shadow:0 1px 0 rgba(255,255,255,.4)}.ui-btn:focus{outline:0}.ui-focus,.ui-btn:focus{-moz-box-shadow:0 0 12px #387bbe;-webkit-box-shadow:0 0 12px #387bbe;box-shadow:0 0 12px #387bbe}.ui-mobile-nosupport-boxshadow *{-moz-box-shadow:none!important;-webkit-box-shadow:none!important;box-shadow:none!important}.ui-mobile-nosupport-boxshadow .ui-focus,.ui-mobile-nosupport-boxshadow .ui-btn:focus{outline-width:1px;outline-style:dotted}.ui-mobile,.ui-mobile body{height:99.9%}.ui-mobile fieldset,.ui-page{padding:0;margin:0}.ui-mobile a img,.ui-mobile fieldset{border-width:0}.ui-mobile-viewport{margin:0;overflow-x:visible;-webkit-text-size-adjust:none;-ms-text-size-adjust:none;-webkit-tap-highlight-color:rgba(0,0,0,0)}body.ui-mobile-viewport,div.ui-mobile-viewport{overflow-x:hidden}.ui-mobile [data-role=page],.ui-mobile [data-role=dialog],.ui-page{top:0;left:0;width:100%;min-height:100%;position:absolute;display:none;border:0}.ui-mobile .ui-page-active{display:block;overflow:visible}.ui-page{outline:0}@media screen and (orientation:portrait){.ui-mobile,.ui-mobile .ui-page{min-height:420px}}@media screen and (orientation:landscape){.ui-mobile,.ui-mobile .ui-page{min-height:300px}}.ui-loading .ui-loader{display:block}.ui-loader{display:none;z-index:9999999;position:fixed;top:50%;box-shadow:0 1px 1px -1px #fff;left:50%;border:0}.ui-loader-default{background:0;opacity:.18;width:46px;height:46px;margin-left:-23px;margin-top:-23px}.ui-loader-verbose{width:200px;opacity:.88;height:auto;margin-left:-110px;margin-top:-43px;padding:10px}.ui-loader-default h1{font-size:0;width:0;height:0;overflow:hidden}.ui-loader-verbose h1{font-size:16px;margin:0;text-align:center}.ui-loader .ui-icon{background-color:#000;display:block;margin:0;width:44px;height:44px;padding:1px;-webkit-border-radius:36px;-moz-border-radius:36px;border-radius:36px}.ui-loader-verbose .ui-icon{margin:0 auto 10px;opacity:.75}.ui-loader-textonly{padding:15px;margin-left:-115px}.ui-loader-textonly .ui-icon{display:none}.ui-loader-fakefix{position:absolute}.ui-mobile-rendering>*{visibility:hidden}.ui-bar,.ui-body{position:relative;padding:.4em 15px;overflow:hidden;display:block;clear:both}.ui-bar{font-size:16px;margin:0}.ui-bar h1,.ui-bar h2,.ui-bar h3,.ui-bar h4,.ui-bar h5,.ui-bar h6{margin:0;padding:0;font-size:16px;display:inline-block}.ui-header,.ui-footer{position:relative;border-left-width:0;border-right-width:0}.ui-header .ui-btn-left,.ui-header .ui-btn-right,.ui-footer .ui-btn-left,.ui-footer .ui-btn-right{position:absolute;top:3px}.ui-header .ui-btn-left,.ui-footer .ui-btn-left{left:5px}.ui-header .ui-btn-right,.ui-footer .ui-btn-right{right:5px}.ui-footer .ui-btn-icon-notext,.ui-header .ui-btn-icon-notext{top:6px}.ui-header .ui-title,.ui-footer .ui-title{min-height:1.1em;text-align:center;font-size:16px;display:block;margin:.6em 30% .8em;padding:0;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;outline:0!important}.ui-footer .ui-title{margin:.6em 15px .8em}.ui-content{border-width:0;overflow:visible;overflow-x:hidden;padding:15px}.ui-icon{width:18px;height:18px}.ui-nojs{position:absolute;left:-9999px}.ui-hide-label label,.ui-hidden-accessible{position:absolute!important;left:-9999px;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}.ui-mobile-viewport-transitioning,.ui-mobile-viewport-transitioning .ui-page{width:100%;height:100%;overflow:hidden}.in{-webkit-animation-timing-function:ease-out;-webkit-animation-duration:350ms;-moz-animation-timing-function:ease-out;-moz-animation-duration:350ms}.out{-webkit-animation-timing-function:ease-in;-webkit-animation-duration:225ms;-moz-animation-timing-function:ease-in;-moz-animation-duration:225}@-webkit-keyframes fadein{from{opacity:0}to{opacity:1}}@-moz-keyframes fadein{from{opacity:0}to{opacity:1}}@-webkit-keyframes fadeout{from{opacity:1}to{opacity:0}}@-moz-keyframes fadeout{from{opacity:1}to{opacity:0}}.fade.out{opacity:0;-webkit-animation-duration:125ms;-webkit-animation-name:fadeout;-moz-animation-duration:125ms;-moz-animation-name:fadeout}.fade.in{opacity:1;-webkit-animation-duration:225ms;-webkit-animation-name:fadein;-moz-animation-duration:225ms;-moz-animation-name:fadein}.pop{-webkit-transform-origin:50% 50%;-moz-transform-origin:50% 50%}.pop.in{-webkit-transform:scale(1);-moz-transform:scale(1);opacity:1;-webkit-animation-name:popin;-moz-animation-name:popin;-webkit-animation-duration:350ms;-moz-animation-duration:350ms}.pop.out{-webkit-animation-name:fadeout;-moz-animation-name:fadeout;opacity:0;-webkit-animation-duration:100ms;-moz-animation-duration:100ms}.pop.in.reverse{-webkit-animation-name:fadein;-moz-animation-name:fadein}.pop.out.reverse{-webkit-transform:scale(.8);-moz-transform:scale(.8);-webkit-animation-name:popout;-moz-animation-name:popout}@-webkit-keyframes popin{from{-webkit-transform:scale(.8);opacity:0}to{-webkit-transform:scale(1);opacity:1}}@-moz-keyframes popin{from{-moz-transform:scale(.8);opacity:0}to{-moz-transform:scale(1);opacity:1}}@-webkit-keyframes popout{from{-webkit-transform:scale(1);opacity:1}to{-webkit-transform:scale(.8);opacity:0}}@-moz-keyframes popout{from{-moz-transform:scale(1);opacity:1}to{-moz-transform:scale(.8);opacity:0}}@-webkit-keyframes slideinfromright{from{-webkit-transform:translateX(100%)}to{-webkit-transform:translateX(0)}}@-moz-keyframes slideinfromright{from{-moz-transform:translateX(100%)}to{-moz-transform:translateX(0)}}@-webkit-keyframes slideinfromleft{from{-webkit-transform:translateX(-100%)}to{-webkit-transform:translateX(0)}}@-moz-keyframes slideinfromleft{from{-moz-transform:translateX(-100%)}to{-moz-transform:translateX(0)}}@-webkit-keyframes slideouttoleft{from{-webkit-transform:translateX(0)}to{-webkit-transform:translateX(-100%)}}@-moz-keyframes slideouttoleft{from{-moz-transform:translateX(0)}to{-moz-transform:translateX(-100%)}}@-webkit-keyframes slideouttoright{from{-webkit-transform:translateX(0)}to{-webkit-transform:translateX(100%)}}@-moz-keyframes slideouttoright{from{-moz-transform:translateX(0)}to{-moz-transform:translateX(100%)}}.slide.out,.slide.in{-webkit-animation-timing-function:ease-out;-webkit-animation-duration:350ms;-moz-animation-timing-function:ease-out;-moz-animation-duration:350ms}.slide.out{-webkit-transform:translateX(-100%);-webkit-animation-name:slideouttoleft;-moz-transform:translateX(-100%);-moz-animation-name:slideouttoleft}.slide.in{-webkit-transform:translateX(0);-webkit-animation-name:slideinfromright;-moz-transform:translateX(0);-moz-animation-name:slideinfromright}.slide.out.reverse{-webkit-transform:translateX(100%);-webkit-animation-name:slideouttoright;-moz-transform:translateX(100%);-moz-animation-name:slideouttoright}.slide.in.reverse{-webkit-transform:translateX(0);-webkit-animation-name:slideinfromleft;-moz-transform:translateX(0);-moz-animation-name:slideinfromleft}.slidefade.out{-webkit-transform:translateX(-100%);-webkit-animation-name:slideouttoleft;-moz-transform:translateX(-100%);-moz-animation-name:slideouttoleft;-webkit-animation-duration:225ms;-moz-animation-duration:225ms}.slidefade.in{-webkit-transform:translateX(0);-webkit-animation-name:fadein;-moz-transform:translateX(0);-moz-animation-name:fadein;-webkit-animation-duration:200ms;-moz-animation-duration:200ms}.slidefade.out.reverse{-webkit-transform:translateX(100%);-webkit-animation-name:slideouttoright;-moz-transform:translateX(100%);-moz-animation-name:slideouttoright;-webkit-animation-duration:200ms;-moz-animation-duration:200ms}.slidefade.in.reverse{-webkit-transform:translateX(0);-webkit-animation-name:fadein;-moz-transform:translateX(0);-moz-animation-name:fadein;-webkit-animation-duration:200ms;-moz-animation-duration:200ms}.slidedown.out{-webkit-animation-name:fadeout;-moz-animation-name:fadeout;-webkit-animation-duration:100ms;-moz-animation-duration:100ms}.slidedown.in{-webkit-transform:translateY(0);-webkit-animation-name:slideinfromtop;-moz-transform:translateY(0);-moz-animation-name:slideinfromtop;-webkit-animation-duration:250ms;-moz-animation-duration:250ms}.slidedown.in.reverse{-webkit-animation-name:fadein;-moz-animation-name:fadein;-webkit-animation-duration:150ms;-moz-animation-duration:150ms}.slidedown.out.reverse{-webkit-transform:translateY(-100%);-moz-transform:translateY(-100%);-webkit-animation-name:slideouttotop;-moz-animation-name:slideouttotop;-webkit-animation-duration:200ms;-moz-animation-duration:200ms}@-webkit-keyframes slideinfromtop{from{-webkit-transform:translateY(-100%)}to{-webkit-transform:translateY(0)}}@-moz-keyframes slideinfromtop{from{-moz-transform:translateY(-100%)}to{-moz-transform:translateY(0)}}@-webkit-keyframes slideouttotop{from{-webkit-transform:translateY(0)}to{-webkit-transform:translateY(-100%)}}@-moz-keyframes slideouttotop{from{-moz-transform:translateY(0)}to{-moz-transform:translateY(-100%)}}.slideup.out{-webkit-animation-name:fadeout;-moz-animation-name:fadeout;-webkit-animation-duration:100ms;-moz-animation-duration:100ms}.slideup.in{-webkit-transform:translateY(0);-webkit-animation-name:slideinfrombottom;-moz-transform:translateY(0);-moz-animation-name:slideinfrombottom;-webkit-animation-duration:250ms;-moz-animation-duration:250ms}.slideup.in.reverse{-webkit-animation-name:fadein;-moz-animation-name:fadein;-webkit-animation-duration:150ms;-moz-animation-duration:150ms}.slideup.out.reverse{-webkit-transform:translateY(100%);-moz-transform:translateY(100%);-webkit-animation-name:slideouttobottom;-moz-animation-name:slideouttobottom;-webkit-animation-duration:200ms;-moz-animation-duration:200ms}@-webkit-keyframes slideinfrombottom{from{-webkit-transform:translateY(100%)}to{-webkit-transform:translateY(0)}}@-moz-keyframes slideinfrombottom{from{-moz-transform:translateY(100%)}to{-moz-transform:translateY(0)}}@-webkit-keyframes slideouttobottom{from{-webkit-transform:translateY(0)}to{-webkit-transform:translateY(100%)}}@-moz-keyframes slideouttobottom{from{-moz-transform:translateY(0)}to{-moz-transform:translateY(100%)}}.viewport-flip{-webkit-perspective:1000;-moz-perspective:1000;position:absolute}.flip{-webkit-backface-visibility:hidden;-webkit-transform:translateX(0);-moz-backface-visibility:hidden;-moz-transform:translateX(0)}.flip.out{-webkit-transform:rotateY(-90deg) scale(.9);-webkit-animation-name:flipouttoleft;-webkit-animation-duration:175ms;-moz-transform:rotateY(-90deg) scale(.9);-moz-animation-name:flipouttoleft;-moz-animation-duration:175ms}.flip.in{-webkit-animation-name:flipintoright;-webkit-animation-duration:225ms;-moz-animation-name:flipintoright;-moz-animation-duration:225ms}.flip.out.reverse{-webkit-transform:rotateY(90deg) scale(.9);-webkit-animation-name:flipouttoright;-moz-transform:rotateY(90deg) scale(.9);-moz-animation-name:flipouttoright}.flip.in.reverse{-webkit-animation-name:flipintoleft;-moz-animation-name:flipintoleft}@-webkit-keyframes flipouttoleft{from{-webkit-transform:rotateY(0)}to{-webkit-transform:rotateY(-90deg) scale(.9)}}@-moz-keyframes flipouttoleft{from{-moz-transform:rotateY(0)}to{-moz-transform:rotateY(-90deg) scale(.9)}}@-webkit-keyframes flipouttoright{from{-webkit-transform:rotateY(0)}to{-webkit-transform:rotateY(90deg) scale(.9)}}@-moz-keyframes flipouttoright{from{-moz-transform:rotateY(0)}to{-moz-transform:rotateY(90deg) scale(.9)}}@-webkit-keyframes flipintoleft{from{-webkit-transform:rotateY(-90deg) scale(.9)}to{-webkit-transform:rotateY(0)}}@-moz-keyframes flipintoleft{from{-moz-transform:rotateY(-90deg) scale(.9)}to{-moz-transform:rotateY(0)}}@-webkit-keyframes flipintoright{from{-webkit-transform:rotateY(90deg) scale(.9)}to{-webkit-transform:rotateY(0)}}@-moz-keyframes flipintoright{from{-moz-transform:rotateY(90deg) scale(.9)}to{-moz-transform:rotateY(0)}}.viewport-turn{-webkit-perspective:1000;-moz-perspective:1000;position:absolute}.turn{-webkit-backface-visibility:hidden;-webkit-transform:translateX(0);-webkit-transform-origin:0 0;-moz-backface-visibility:hidden;-moz-transform:translateX(0);-moz-transform-origin:0 0}.turn.out{-webkit-transform:rotateY(-90deg) scale(.9);-webkit-animation-name:flipouttoleft;-moz-transform:rotateY(-90deg) scale(.9);-moz-animation-name:flipouttoleft;-webkit-animation-duration:125ms;-moz-animation-duration:125ms}.turn.in{-webkit-animation-name:flipintoright;-moz-animation-name:flipintoright;-webkit-animation-duration:250ms;-moz-animation-duration:250ms}.turn.out.reverse{-webkit-transform:rotateY(90deg) scale(.9);-webkit-animation-name:flipouttoright;-moz-transform:rotateY(90deg) scale(.9);-moz-animation-name:flipouttoright}.turn.in.reverse{-webkit-animation-name:flipintoleft;-moz-animation-name:flipintoleft}@-webkit-keyframes flipouttoleft{from{-webkit-transform:rotateY(0)}to{-webkit-transform:rotateY(-90deg) scale(.9)}}@-moz-keyframes flipouttoleft{from{-moz-transform:rotateY(0)}to{-moz-transform:rotateY(-90deg) scale(.9)}}@-webkit-keyframes flipouttoright{from{-webkit-transform:rotateY(0)}to{-webkit-transform:rotateY(90deg) scale(.9)}}@-moz-keyframes flipouttoright{from{-moz-transform:rotateY(0)}to{-moz-transform:rotateY(90deg) scale(.9)}}@-webkit-keyframes flipintoleft{from{-webkit-transform:rotateY(-90deg) scale(.9)}to{-webkit-transform:rotateY(0)}}@-moz-keyframes flipintoleft{from{-moz-transform:rotateY(-90deg) scale(.9)}to{-moz-transform:rotateY(0)}}@-webkit-keyframes flipintoright{from{-webkit-transform:rotateY(90deg) scale(.9)}to{-webkit-transform:rotateY(0)}}@-moz-keyframes flipintoright{from{-moz-transform:rotateY(90deg) scale(.9)}to{-moz-transform:rotateY(0)}}.flow{-webkit-transform-origin:50% 30%;-moz-transform-origin:50% 30%;-webkit-box-shadow:0 0 20px rgba(0,0,0,.4);-moz-box-shadow:0 0 20px rgba(0,0,0,.4)}.ui-dialog.flow{-webkit-transform-origin:none;-moz-transform-origin:none;-webkit-box-shadow:none;-moz-box-shadow:none}.flow.out{-webkit-transform:translateX(-100%) scale(.7);-webkit-animation-name:flowouttoleft;-webkit-animation-timing-function:ease;-webkit-animation-duration:350ms;-moz-transform:translateX(-100%) scale(.7);-moz-animation-name:flowouttoleft;-moz-animation-timing-function:ease;-moz-animation-duration:350ms}.flow.in{-webkit-transform:translateX(0) scale(1);-webkit-animation-name:flowinfromright;-webkit-animation-timing-function:ease;-webkit-animation-duration:350ms;-moz-transform:translateX(0) scale(1);-moz-animation-name:flowinfromright;-moz-animation-timing-function:ease;-moz-animation-duration:350ms}.flow.out.reverse{-webkit-transform:translateX(100%);-webkit-animation-name:flowouttoright;-moz-transform:translateX(100%);-moz-animation-name:flowouttoright}.flow.in.reverse{-webkit-animation-name:flowinfromleft;-moz-animation-name:flowinfromleft}@-webkit-keyframes flowouttoleft{0%{-webkit-transform:translateX(0) scale(1)}60%,70%{-webkit-transform:translateX(0) scale(.7)}100%{-webkit-transform:translateX(-100%) scale(.7)}}@-moz-keyframes flowouttoleft{0%{-moz-transform:translateX(0) scale(1)}60%,70%{-moz-transform:translateX(0) scale(.7)}100%{-moz-transform:translateX(-100%) scale(.7)}}@-webkit-keyframes flowouttoright{0%{-webkit-transform:translateX(0) scale(1)}60%,70%{-webkit-transform:translateX(0) scale(.7)}100%{-webkit-transform:translateX(100%) scale(.7)}}@-moz-keyframes flowouttoright{0%{-moz-transform:translateX(0) scale(1)}60%,70%{-moz-transform:translateX(0) scale(.7)}100%{-moz-transform:translateX(100%) scale(.7)}}@-webkit-keyframes flowinfromleft{0%{-webkit-transform:translateX(-100%) scale(.7)}30%,40%{-webkit-transform:translateX(0) scale(.7)}100%{-webkit-transform:translateX(0) scale(1)}}@-moz-keyframes flowinfromleft{0%{-moz-transform:translateX(-100%) scale(.7)}30%,40%{-moz-transform:translateX(0) scale(.7)}100%{-moz-transform:translateX(0) scale(1)}}@-webkit-keyframes flowinfromright{0%{-webkit-transform:translateX(100%) scale(.7)}30%,40%{-webkit-transform:translateX(0) scale(.7)}100%{-webkit-transform:translateX(0) scale(1)}}@-moz-keyframes flowinfromright{0%{-moz-transform:translateX(100%) scale(.7)}30%,40%{-moz-transform:translateX(0) scale(.7)}100%{-moz-transform:translateX(0) scale(1)}}.ui-grid-a,.ui-grid-b,.ui-grid-c,.ui-grid-d{overflow:hidden}.ui-block-a,.ui-block-b,.ui-block-c,.ui-block-d,.ui-block-e{margin:0;padding:0;border:0;float:left;min-height:1px}.ui-grid-solo .ui-block-a{width:100%;float:none}.ui-grid-a .ui-block-a,.ui-grid-a .ui-block-b{width:50%}.ui-grid-a .ui-block-a{clear:left}.ui-grid-b .ui-block-a,.ui-grid-b .ui-block-b,.ui-grid-b .ui-block-c{width:33.333%}.ui-grid-b .ui-block-a{clear:left}.ui-grid-c .ui-block-a,.ui-grid-c .ui-block-b,.ui-grid-c .ui-block-c,.ui-grid-c .ui-block-d{width:25%}.ui-grid-c .ui-block-a{clear:left}.ui-grid-d .ui-block-a,.ui-grid-d .ui-block-b,.ui-grid-d .ui-block-c,.ui-grid-d .ui-block-d,.ui-grid-d .ui-block-e{width:20%}.ui-grid-d .ui-block-a{clear:left}.ui-header-fixed,.ui-footer-fixed{left:0;right:0;width:100%;position:fixed;z-index:1000}.ui-header-fixed{top:0}.ui-footer-fixed{bottom:0}.ui-header-fullscreen,.ui-footer-fullscreen{opacity:.9}.ui-page-header-fixed{padding-top:2.5em}.ui-page-footer-fixed{padding-bottom:3em}.ui-page-header-fullscreen .ui-content,.ui-page-footer-fullscreen .ui-content{padding:0}.ui-fixed-hidden{position:absolute}.ui-page-header-fullscreen .ui-fixed-hidden,.ui-page-footer-fullscreen .ui-fixed-hidden{left:-99999em}.ui-header-fixed .ui-btn,.ui-footer-fixed .ui-btn{z-index:10}.ui-navbar{overflow:hidden}.ui-navbar ul,.ui-navbar-expanded ul{list-style:none;padding:0;margin:0;position:relative;display:block;border:0}.ui-navbar-collapsed ul{float:left;width:75%;margin-right:-2px}.ui-navbar-collapsed .ui-navbar-toggle{float:left;width:25%}.ui-navbar li.ui-navbar-truncate{position:absolute;left:-9999px;top:-9999px}.ui-navbar li .ui-btn,.ui-navbar .ui-navbar-toggle .ui-btn{display:block;font-size:12px;text-align:center;margin:0;border-right-width:0;max-width:100%}.ui-navbar li .ui-btn{margin-right:-1px}.ui-navbar li .ui-btn:last-child{margin-right:0}.ui-header .ui-navbar li .ui-btn,.ui-header .ui-navbar .ui-navbar-toggle .ui-btn,.ui-footer .ui-navbar li .ui-btn,.ui-footer .ui-navbar .ui-navbar-toggle .ui-btn{border-top-width:0;border-bottom-width:0}.ui-navbar .ui-btn-inner{padding-left:2px;padding-right:2px}.ui-navbar-noicons li .ui-btn .ui-btn-inner,.ui-navbar-noicons .ui-navbar-toggle .ui-btn-inner{padding-top:.8em;padding-bottom:.9em}.ui-navbar-expanded .ui-btn{margin:0;font-size:14px}.ui-navbar-expanded .ui-btn-inner{padding-left:5px;padding-right:5px}.ui-navbar-expanded .ui-btn-icon-top .ui-btn-inner{padding:45px 5px 15px;text-align:center}.ui-navbar-expanded .ui-btn-icon-top .ui-icon{top:15px}.ui-navbar-expanded .ui-btn-icon-bottom .ui-btn-inner{padding:15px 5px 45px;text-align:center}.ui-navbar-expanded .ui-btn-icon-bottom .ui-icon{bottom:15px}.ui-navbar-expanded li .ui-btn .ui-btn-inner{min-height:2.5em}.ui-navbar-expanded .ui-navbar-noicons .ui-btn .ui-btn-inner{padding-top:1.8em;padding-bottom:1.9em}.ui-btn{display:block;text-align:center;cursor:pointer;position:relative;margin:.5em 5px;padding:0}.ui-mini{margin:.25em 5px}.ui-btn-inner{padding:.6em 20px;min-width:.75em;display:block;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;position:relative;zoom:1}.ui-btn input,.ui-btn button{z-index:2}.ui-btn-left,.ui-btn-right,.ui-btn-inline{display:inline-block}.ui-btn-block{display:block}.ui-header .ui-btn,.ui-footer .ui-btn{display:inline-block;margin:0}.ui-header .ui-btn-inner,.ui-footer .ui-btn-inner,.ui-mini .ui-btn-inner{font-size:12.5px;padding:.55em 11px .5em}.ui-header .ui-fullsize .ui-btn-inner,.ui-footer .ui-fullsize .ui-btn-inner{font-size:16px;padding:.6em 25px}.ui-btn-icon-notext{width:24px;height:24px}.ui-btn-icon-notext .ui-btn-inner{padding:0;height:100%}.ui-btn-icon-notext .ui-btn-inner .ui-icon{margin:2px 1px 2px 3px}.ui-btn-text{position:relative;z-index:1;width:100%}.ui-btn-icon-notext .ui-btn-text{position:absolute;left:-9999px}.ui-btn-icon-left .ui-btn-inner{padding-left:40px}.ui-btn-icon-right .ui-btn-inner{padding-right:40px}.ui-btn-icon-top .ui-btn-inner{padding-top:40px}.ui-btn-icon-bottom .ui-btn-inner{padding-bottom:40px}.ui-header .ui-btn-icon-left .ui-btn-inner,.ui-footer .ui-btn-icon-left .ui-btn-inner,.ui-mini .ui-btn-icon-left .ui-btn-inner{padding-left:30px}.ui-header .ui-btn-icon-right .ui-btn-inner,.ui-footer .ui-btn-icon-right .ui-btn-inner,.ui-mini .ui-btn-icon-right .ui-btn-inner{padding-right:30px}.ui-header .ui-btn-icon-top .ui-btn-inner,.ui-footer .ui-btn-icon-top .ui-btn-inner,.ui-mini .ui-btn-icon-top .ui-btn-inner{padding:30px 3px .5em 3px}.ui-header .ui-btn-icon-bottom .ui-btn-inner,.ui-footer .ui-btn-icon-bottom .ui-btn-inner,.ui-mini .ui-btn-icon-bottom .ui-btn-inner{padding:.55em 3px 30px 3px}.ui-btn-icon-notext .ui-icon{display:block;z-index:0}.ui-btn-icon-left .ui-btn-inner .ui-icon,.ui-btn-icon-right .ui-btn-inner .ui-icon{position:absolute;top:50%;margin-top:-9px}.ui-btn-icon-top .ui-btn-inner .ui-icon,.ui-btn-icon-bottom .ui-btn-inner .ui-icon{position:absolute;left:50%;margin-left:-9px}.ui-btn-icon-left .ui-icon{left:10px}.ui-btn-icon-right .ui-icon{right:10px}.ui-btn-icon-top .ui-icon{top:10px}.ui-btn-icon-bottom .ui-icon{top:auto;bottom:10px}.ui-header .ui-btn-icon-left .ui-icon,.ui-footer .ui-btn-icon-left .ui-icon,.ui-mini.ui-btn-icon-left .ui-icon,.ui-mini .ui-btn-icon-left .ui-icon{left:5px}.ui-header .ui-btn-icon-right .ui-icon,.ui-footer .ui-btn-icon-right .ui-icon,.ui-mini.ui-btn-icon-right .ui-icon,.ui-mini .ui-btn-icon-right .ui-icon{right:5px}.ui-header .ui-btn-icon-top .ui-icon,.ui-footer .ui-btn-icon-top .ui-icon,.ui-mini.ui-btn-icon-top .ui-icon,.ui-mini .ui-btn-icon-top .ui-icon{top:5px}.ui-header .ui-btn-icon-bottom .ui-icon,.ui-footer .ui-btn-icon-bottom .ui-icon,.ui-mini.ui-btn-icon-bottom .ui-icon,.ui-mini .ui-btn-icon-bottom .ui-icon{bottom:5px}.ui-btn-hidden{position:absolute;top:0;left:0;width:100%;height:100%;-webkit-appearance:button;opacity:.1;cursor:pointer;background:#fff;background:rgba(255,255,255,0);filter:Alpha(Opacity=.0001);font-size:1px;border:0;text-indent:-9999px}.ui-collapsible{margin:.5em 0}.ui-collapsible-heading{font-size:16px;display:block;margin:0 -8px;padding:0;border-width:0 0 1px 0;position:relative}.ui-collapsible-heading a{text-align:left;margin:0}.ui-collapsible-heading .ui-btn-inner,.ui-collapsible-heading .ui-btn-icon-left .ui-btn-inner{padding-left:40px}.ui-collapsible-heading .ui-btn-icon-right .ui-btn-inner{padding-left:12px;padding-right:40px}.ui-collapsible-heading .ui-btn-icon-top .ui-btn-inner,.ui-collapsible-heading .ui-btn-icon-bottom .ui-btn-inner{padding-right:40px;text-align:center}.ui-collapsible-heading a span.ui-btn{position:absolute;left:6px;top:50%;margin:-12px 0 0 0;width:20px;height:20px;padding:1px 0 1px 2px;text-indent:-9999px}.ui-collapsible-heading a span.ui-btn .ui-btn-inner{padding:10px 0}.ui-collapsible-heading a span.ui-btn .ui-icon{left:0;margin-top:-10px}.ui-collapsible-heading-status{position:absolute;top:-9999px;left:0}.ui-collapsible-content{display:block;margin:0 -8px;padding:10px 16px;border-top:0;background-image:none;font-weight:normal}.ui-collapsible-content-collapsed{display:none}.ui-collapsible-set{margin:.5em 0}.ui-collapsible-set .ui-collapsible{margin:-1px 0 0}.ui-controlgroup,fieldset.ui-controlgroup{padding:0;margin:0 0 .5em;zoom:1}.ui-bar .ui-controlgroup{margin:0 .3em}.ui-controlgroup-label{font-size:16px;line-height:1.4;font-weight:normal;margin:0 0 .4em}.ui-controlgroup-controls{display:block;width:100%}.ui-controlgroup li{list-style:none}.ui-controlgroup-vertical .ui-btn,.ui-controlgroup-vertical .ui-checkbox,.ui-controlgroup-vertical .ui-radio{margin:0;border-bottom-width:0}.ui-controlgroup-controls label.ui-select{position:absolute;left:-9999px}.ui-controlgroup-vertical .ui-controlgroup-last{border-bottom-width:1px}.ui-controlgroup-horizontal{padding:0}.ui-controlgroup-horizontal .ui-btn-inner{text-align:center}.ui-controlgroup-horizontal .ui-btn,.ui-controlgroup-horizontal .ui-select{display:inline-block;margin:0 -6px 0 0}.ui-controlgroup-horizontal .ui-checkbox,.ui-controlgroup-horizontal .ui-radio{float:left;clear:none;margin:0 -1px 0 0}.ui-controlgroup-horizontal .ui-checkbox .ui-btn,.ui-controlgroup-horizontal .ui-radio .ui-btn,.ui-controlgroup-horizontal .ui-checkbox:last-child,.ui-controlgroup-horizontal .ui-radio:last-child{margin-right:0}.ui-controlgroup-horizontal .ui-controlgroup-last{margin-right:0}.ui-controlgroup .ui-checkbox label,.ui-controlgroup .ui-radio label{font-size:16px}@media all and (min-width:450px){.ui-field-contain .ui-controlgroup-label{vertical-align:top;display:inline-block;width:20%;margin:0 2% 0 0}.ui-field-contain .ui-controlgroup-controls{width:60%;display:inline-block}.ui-field-contain .ui-controlgroup .ui-select{width:100%}.ui-field-contain .ui-controlgroup-horizontal .ui-select{width:auto}}.ui-dialog{background:none!important}.ui-dialog-contain{width:92.5%;max-width:500px;margin:10% auto 15px auto;padding:0}.ui-dialog .ui-header{margin-top:15%;border:0;overflow:hidden}.ui-dialog .ui-header,.ui-dialog .ui-content,.ui-dialog .ui-footer{display:block;position:relative;width:auto}.ui-dialog .ui-header,.ui-dialog .ui-footer{z-index:10;padding:0}.ui-dialog .ui-footer{padding:0 15px}.ui-dialog .ui-content{padding:15px}.ui-dialog{margin-top:-15px}.ui-checkbox,.ui-radio{position:relative;clear:both;margin:.2em 0 .5em;z-index:1}.ui-checkbox .ui-btn,.ui-radio .ui-btn{margin:0;text-align:left;z-index:2}.ui-checkbox .ui-btn-inner,.ui-radio .ui-btn-inner{white-space:normal}.ui-checkbox .ui-btn-icon-left .ui-btn-inner,.ui-radio .ui-btn-icon-left .ui-btn-inner{padding-left:45px}.ui-checkbox .ui-mini.ui-btn-icon-left .ui-btn-inner,.ui-radio .ui-mini.ui-btn-icon-left .ui-btn-inner{padding-left:36px}.ui-checkbox .ui-btn-icon-right .ui-btn-inner,.ui-radio .ui-btn-icon-right .ui-btn-inner{padding-right:45px}.ui-checkbox .ui-mini.ui-btn-icon-right .ui-btn-inner,.ui-radio .ui-mini.ui-btn-icon-right .ui-btn-inner{padding-right:36px}.ui-checkbox .ui-btn-icon-top .ui-btn-inner,.ui-radio .ui-btn-icon-top .ui-btn-inner{padding-right:0;padding-left:0;text-align:center}.ui-checkbox .ui-btn-icon-bottom .ui-btn-inner,.ui-radio .ui-btn-icon-bottom .ui-btn-inner{padding-right:0;padding-left:0;text-align:center}.ui-checkbox .ui-icon,.ui-radio .ui-icon{top:1.1em}.ui-checkbox .ui-btn-icon-left .ui-icon,.ui-radio .ui-btn-icon-left .ui-icon{left:15px}.ui-checkbox .ui-mini.ui-btn-icon-left .ui-icon,.ui-radio .ui-mini.ui-btn-icon-left .ui-icon{left:9px}.ui-checkbox .ui-btn-icon-right .ui-icon,.ui-radio .ui-btn-icon-right .ui-icon{right:15px}.ui-checkbox .ui-mini.ui-btn-icon-right .ui-icon,.ui-radio .ui-mini.ui-btn-icon-right .ui-icon{right:9px}.ui-checkbox .ui-btn-icon-top .ui-icon,.ui-radio .ui-btn-icon-top .ui-icon{top:10px}.ui-checkbox .ui-btn-icon-bottom .ui-icon,.ui-radio .ui-btn-icon-bottom .ui-icon{top:auto;bottom:10px}.ui-checkbox .ui-btn-icon-right .ui-icon,.ui-radio .ui-btn-icon-right .ui-icon{right:15px}.ui-checkbox .ui-mini.ui-btn-icon-right .ui-icon,.ui-radio .ui-mini.ui-btn-icon-right .ui-icon{right:9px}.ui-checkbox input,.ui-radio input{position:absolute;left:20px;top:50%;width:10px;height:10px;margin:-5px 0 0 0;outline:0!important;z-index:1}.ui-field-contain,fieldset.ui-field-contain{padding:.8em 0;margin:0;border-width:0 0 1px 0;overflow:visible}.ui-field-contain:first-child{border-top-width:0}.ui-header .ui-field-contain-left,.ui-header .ui-field-contain-right{position:absolute;top:0;width:25%}.ui-header .ui-field-contain-left{left:1em}.ui-header .ui-field-contain-right{right:1em}@media all and (min-width:450px){.ui-field-contain,.ui-mobile fieldset.ui-field-contain{border-width:0;padding:0;margin:1em 0}}.ui-select{display:block;position:relative}.ui-select select{position:absolute;left:-9999px;top:-9999px}.ui-select .ui-btn{overflow:hidden;opacity:1;margin:0}.ui-select .ui-btn select{cursor:pointer;-webkit-appearance:button;left:0;top:0;width:100%;min-height:1.5em;min-height:100%;height:3em;max-height:100%;opacity:0;-ms-filter:"alpha(opacity=0)";filter:alpha(opacity=0);z-index:2}.ui-select .ui-disabled{opacity:.3}@-moz-document url-prefix(){.ui-select .ui-btn select{opacity:.0001}}.ui-select .ui-btn select.ui-select-nativeonly{opacity:1;text-indent:0}.ui-select .ui-btn-icon-right .ui-btn-inner{padding-right:45px}.ui-select .ui-btn-icon-right .ui-icon{right:15px}.ui-select .ui-mini.ui-btn-icon-right .ui-icon{right:7px}label.ui-select{font-size:16px;line-height:1.4;font-weight:normal;margin:0 0 .3em;display:block}.ui-select .ui-btn-text,.ui-selectmenu .ui-btn-text{display:block;min-height:1em;overflow:hidden!important}.ui-select .ui-btn-text{text-overflow:ellipsis}.ui-selectmenu{position:absolute;padding:0;z-index:1100!important;width:80%;max-width:350px;padding:6px}.ui-selectmenu .ui-listview{margin:0}.ui-selectmenu .ui-btn.ui-li-divider{cursor:default}.ui-selectmenu-hidden{top:-9999px;left:-9999px}.ui-selectmenu-screen{position:absolute;top:0;left:0;width:100%;height:100%;z-index:99}.ui-screen-hidden,.ui-selectmenu-list .ui-li .ui-icon{display:none}.ui-selectmenu-list .ui-li .ui-icon{display:block}.ui-li.ui-selectmenu-placeholder{display:none}.ui-selectmenu .ui-header .ui-title{margin:.6em 46px .8em}@media all and (min-width:450px){.ui-field-contain label.ui-select{vertical-align:top;display:inline-block;width:20%;margin:0 2% 0 0}.ui-field-contain .ui-select{width:60%;display:inline-block}}.ui-selectmenu .ui-header h1:after{content:'.';visibility:hidden}label.ui-input-text{font-size:16px;line-height:1.4;display:block;font-weight:normal;margin:0 0 .3em}input.ui-input-text,textarea.ui-input-text{background-image:none;padding:.4em;line-height:1.4;font-size:16px;display:block;width:97%;outline:0}.ui-header input.ui-input-text,.ui-footer input.ui-input-text{margin-left:1.25%;padding:.4em 1%;width:95.5%}input.ui-input-text{-webkit-appearance:none}textarea.ui-input-text{height:50px;-webkit-transition:height 200ms linear;-moz-transition:height 200ms linear;-o-transition:height 200ms linear;transition:height 200ms linear}.ui-input-search{padding:0 30px;background-image:none;position:relative}.ui-icon-searchfield:after{position:absolute;left:7px;top:50%;margin-top:-9px;content:"";width:18px;height:18px;opacity:.5}.ui-input-search input.ui-input-text{border:0;width:98%;padding:.4em 0;margin:0;display:block;background:transparent none;outline:0!important}.ui-input-search .ui-input-clear{position:absolute;right:0;top:50%;margin-top:-13px}.ui-mini .ui-input-clear{right:-3px}.ui-input-search .ui-input-clear-hidden{display:none}input.ui-mini,.ui-mini input,textarea.ui-mini{font-size:14px}textarea.ui-mini{height:45px}@media all and (min-width:450px){.ui-field-contain label.ui-input-text{vertical-align:top;display:inline-block;width:20%;margin:0 2% 0 0}.ui-field-contain input.ui-input-text,.ui-field-contain textarea.ui-input-text,.ui-field-contain .ui-input-search{width:60%;display:inline-block}.ui-field-contain .ui-input-search{width:50%}.ui-hide-label input.ui-input-text,.ui-hide-label textarea.ui-input-text,.ui-hide-label .ui-input-search{padding:.4em;width:97%}.ui-input-search input.ui-input-text{width:98%}}.ui-listview{margin:0;counter-reset:listnumbering}.ui-content .ui-listview{margin:-15px}.ui-content .ui-listview-inset{margin:1em 0}.ui-listview,.ui-li{list-style:none;padding:0}.ui-li,.ui-li.ui-field-contain{display:block;margin:0;position:relative;overflow:visible;text-align:left;border-width:0;border-top-width:1px}.ui-li .ui-btn-text a.ui-link-inherit{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.ui-li-divider,.ui-li-static{padding:.5em 15px;font-size:14px;font-weight:bold}.ui-li-divider{counter-reset:listnumbering}ol.ui-listview .ui-link-inherit:before,ol.ui-listview .ui-li-static:before,.ui-li-dec{font-size:.8em;display:inline-block;padding-right:.3em;font-weight:normal;counter-increment:listnumbering;content:counter(listnumbering) ". "}ol.ui-listview .ui-li-jsnumbering:before{content:""!important}.ui-listview-inset .ui-li{border-right-width:1px;border-left-width:1px}.ui-li:last-child,.ui-li.ui-field-contain:last-child{border-bottom-width:1px}.ui-li>.ui-btn-inner{display:block;position:relative;padding:0}.ui-li .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li{padding:.7em 15px .7em 15px;display:block}.ui-li-has-thumb .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-thumb{min-height:60px;padding-left:100px}.ui-li-has-icon .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-icon{min-height:20px;padding-left:40px}.ui-li-has-count .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-count{padding-right:45px}.ui-li-has-arrow .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-arrow{padding-right:30px}.ui-li-has-arrow.ui-li-has-count .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-arrow.ui-li-has-count{padding-right:75px}.ui-li-has-count .ui-btn-text{padding-right:15px}.ui-li-heading{font-size:16px;font-weight:bold;display:block;margin:.6em 0;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.ui-li-desc{font-size:12px;font-weight:normal;display:block;margin:-.5em 0 .6em;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.ui-li-thumb,.ui-listview .ui-li-icon{position:absolute;left:1px;top:0;max-height:80px;max-width:80px}.ui-listview .ui-li-icon{max-height:40px;max-width:40px;left:10px;top:.9em}.ui-li-thumb,.ui-listview .ui-li-icon,.ui-li-content{float:left;margin-right:10px}.ui-li-aside{float:right;width:50%;text-align:right;margin:.3em 0}@media all and (min-width:480px){.ui-li-aside{width:45%}}.ui-li-divider{cursor:default}.ui-li-has-alt .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-alt{padding-right:95px}.ui-li-has-count .ui-li-count{position:absolute;font-size:11px;font-weight:bold;padding:.2em .5em;top:50%;margin-top:-.9em;right:48px}.ui-li-divider .ui-li-count,.ui-li-static .ui-li-count{right:10px}.ui-li-has-alt .ui-li-count{right:55px}.ui-li-link-alt{position:absolute;width:40px;height:100%;border-width:0;border-left-width:1px;top:0;right:0;margin:0;padding:0;z-index:2}.ui-li-link-alt .ui-btn{overflow:hidden;position:absolute;right:8px;top:50%;margin:-11px 0 0 0;border-bottom-width:1px;z-index:-1}.ui-li-link-alt .ui-btn-inner{padding:0;height:100%;position:absolute;width:100%;top:0;left:0}.ui-li-link-alt .ui-btn .ui-icon{right:50%;margin-right:-9px}.ui-listview * .ui-btn-inner>.ui-btn>.ui-btn-inner{border-top:0}.ui-listview-filter{border-width:0;overflow:hidden;margin:-15px -15px 15px -15px}.ui-listview-filter .ui-input-search{margin:5px;width:auto;display:block}.ui-listview-filter-inset{margin:-15px -5px -15px -5px;background:transparent}.ui-li.ui-screen-hidden{display:none}@media only screen and (min-device-width:768px) and (max-device-width:1024px){.ui-li .ui-btn-text{overflow:visible}}label.ui-slider{font-size:16px;line-height:1.4;font-weight:normal;margin:0 0 .3em;display:block}input.ui-slider-input,.ui-field-contain input.ui-slider-input{display:inline-block;width:50px}select.ui-slider-switch{display:none}div.ui-slider{position:relative;display:inline-block;overflow:visible;height:15px;padding:0;margin:0 2% 0 20px;top:4px;width:65%}div.ui-slider-mini{height:12px;margin-left:10px}div.ui-slider-bg{border:0;height:100%;padding-right:8px}.ui-controlgroup a.ui-slider-handle,a.ui-slider-handle{position:absolute;z-index:1;top:50%;width:28px;height:28px;margin-top:-15px;margin-left:-15px;outline:0}a.ui-slider-handle .ui-btn-inner{padding:0;height:100%}div.ui-slider-mini a.ui-slider-handle{height:14px;width:14px;margin:-8px 0 0 -7px}div.ui-slider-mini a.ui-slider-handle .ui-btn-inner{height:30px;width:30px;padding:0;margin:-9px 0 0 -9px}@media all and (min-width:450px){.ui-field-contain label.ui-slider{vertical-align:top;display:inline-block;width:20%;margin:0 2% 0 0}.ui-field-contain div.ui-slider{width:43%}.ui-field-contain div.ui-slider-switch{width:5.5em}}div.ui-slider-switch{height:32px;margin-left:0;width:5.8em}a.ui-slider-handle-snapping{-webkit-transition:left 70ms linear;-moz-transition:left 70ms linear}div.ui-slider-switch .ui-slider-handle{margin-top:1px}.ui-slider-inneroffset{margin:0 16px;position:relative;z-index:1}div.ui-slider-switch.ui-slider-mini{width:5em;height:29px}div.ui-slider-switch.ui-slider-mini .ui-slider-inneroffset{margin:0 15px 0 14px}div.ui-slider-switch.ui-slider-mini .ui-slider-handle{width:25px;height:25px;margin:1px 0 0 -13px}div.ui-slider-switch.ui-slider-mini a.ui-slider-handle .ui-btn-inner{height:30px;width:30px;padding:0;margin:0}span.ui-slider-label{position:absolute;text-align:center;width:100%;overflow:hidden;font-size:16px;top:0;line-height:2;min-height:100%;border-width:0;white-space:nowrap}.ui-slider-mini span.ui-slider-label{font-size:14px}span.ui-slider-label-a{z-index:1;left:0;text-indent:-1.5em}span.ui-slider-label-b{z-index:0;right:0;text-indent:1.5em}.ui-slider-inline{width:120px;display:inline-block}
\ No newline at end of file
--- a/OrthancExplorer/libs/jquery.mobile-1.1.0.min.js	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,177 +0,0 @@
-/*! jQuery Mobile v1.1.0 db342b1f315c282692791aa870455901fdb46a55 jquerymobile.com | jquery.org/license */
-(function(D,s,k){typeof define==="function"&&define.amd?define(["jquery"],function(a){k(a,D,s);return a.mobile}):k(D.jQuery,D,s)})(this,document,function(D,s,k){(function(a,c,b,e){function f(a){for(;a&&typeof a.originalEvent!=="undefined";)a=a.originalEvent;return a}function d(b){for(var d={},f,g;b;){f=a.data(b,t);for(g in f)if(f[g])d[g]=d.hasVirtualBinding=true;b=b.parentNode}return d}function g(){y&&(clearTimeout(y),y=0);y=setTimeout(function(){F=y=0;C.length=0;z=false;G=true},a.vmouse.resetTimerDuration)}
-function h(b,d,g){var c,h;if(!(h=g&&g[b])){if(g=!g)a:{for(g=d.target;g;){if((h=a.data(g,t))&&(!b||h[b]))break a;g=g.parentNode}g=null}h=g}if(h){c=d;var g=c.type,z,j;c=a.Event(c);c.type=b;h=c.originalEvent;z=a.event.props;g.search(/^(mouse|click)/)>-1&&(z=w);if(h)for(j=z.length;j;)b=z[--j],c[b]=h[b];if(g.search(/mouse(down|up)|click/)>-1&&!c.which)c.which=1;if(g.search(/^touch/)!==-1&&(b=f(h),g=b.touches,b=b.changedTouches,g=g&&g.length?g[0]:b&&b.length?b[0]:e))for(h=0,len=u.length;h<len;h++)b=u[h],
-c[b]=g[b];a(d.target).trigger(c)}return c}function j(b){var d=a.data(b.target,x);if(!z&&(!F||F!==d))if(d=h("v"+b.type,b))d.isDefaultPrevented()&&b.preventDefault(),d.isPropagationStopped()&&b.stopPropagation(),d.isImmediatePropagationStopped()&&b.stopImmediatePropagation()}function o(b){var g=f(b).touches,c;if(g&&g.length===1&&(c=b.target,g=d(c),g.hasVirtualBinding))F=L++,a.data(c,x,F),y&&(clearTimeout(y),y=0),A=G=false,c=f(b).touches[0],s=c.pageX,E=c.pageY,h("vmouseover",b,g),h("vmousedown",b,g)}
-function m(a){G||(A||h("vmousecancel",a,d(a.target)),A=true,g())}function p(b){if(!G){var c=f(b).touches[0],e=A,z=a.vmouse.moveDistanceThreshold;A=A||Math.abs(c.pageX-s)>z||Math.abs(c.pageY-E)>z;flags=d(b.target);A&&!e&&h("vmousecancel",b,flags);h("vmousemove",b,flags);g()}}function l(a){if(!G){G=true;var b=d(a.target),c;h("vmouseup",a,b);if(!A&&(c=h("vclick",a,b))&&c.isDefaultPrevented())c=f(a).changedTouches[0],C.push({touchID:F,x:c.clientX,y:c.clientY}),z=true;h("vmouseout",a,b);A=false;g()}}function r(b){var b=
-a.data(b,t),d;if(b)for(d in b)if(b[d])return true;return false}function n(){}function k(b){var d=b.substr(1);return{setup:function(){r(this)||a.data(this,t,{});a.data(this,t)[b]=true;v[b]=(v[b]||0)+1;v[b]===1&&H.bind(d,j);a(this).bind(d,n);if(K)v.touchstart=(v.touchstart||0)+1,v.touchstart===1&&H.bind("touchstart",o).bind("touchend",l).bind("touchmove",p).bind("scroll",m)},teardown:function(){--v[b];v[b]||H.unbind(d,j);K&&(--v.touchstart,v.touchstart||H.unbind("touchstart",o).unbind("touchmove",p).unbind("touchend",
-l).unbind("scroll",m));var f=a(this),g=a.data(this,t);g&&(g[b]=false);f.unbind(d,n);r(this)||f.removeData(t)}}}var t="virtualMouseBindings",x="virtualTouchID",c="vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split(" "),u="clientX clientY pageX pageY screenX screenY".split(" "),w=a.event.props.concat(a.event.mouseHooks?a.event.mouseHooks.props:[]),v={},y=0,s=0,E=0,A=false,C=[],z=false,G=false,K="addEventListener"in b,H=a(b),L=1,F=0;a.vmouse={moveDistanceThreshold:10,clickDistanceThreshold:10,
-resetTimerDuration:1500};for(var I=0;I<c.length;I++)a.event.special[c[I]]=k(c[I]);K&&b.addEventListener("click",function(b){var d=C.length,f=b.target,g,c,e,h,z;if(d){g=b.clientX;c=b.clientY;threshold=a.vmouse.clickDistanceThreshold;for(e=f;e;){for(h=0;h<d;h++)if(z=C[h],e===f&&Math.abs(z.x-g)<threshold&&Math.abs(z.y-c)<threshold||a.data(e,x)===z.touchID){b.preventDefault();b.stopPropagation();return}e=e.parentNode}}},true)})(jQuery,s,k);(function(a,c,b){function e(a){a=a||location.href;return"#"+a.replace(/^[^#]*#?(.*)$/,
-"$1")}var f="hashchange",d=k,g,h=a.event.special,j=d.documentMode,o="on"+f in c&&(j===b||j>7);a.fn[f]=function(a){return a?this.bind(f,a):this.trigger(f)};a.fn[f].delay=50;h[f]=a.extend(h[f],{setup:function(){if(o)return false;a(g.start)},teardown:function(){if(o)return false;a(g.stop)}});g=function(){function g(){var b=e(),d=t(r);if(b!==r)k(r=b,d),a(c).trigger(f);else if(d!==r)location.href=location.href.replace(/#.*/,"")+d;j=setTimeout(g,a.fn[f].delay)}var h={},j,r=e(),n=function(a){return a},k=
-n,t=n;h.start=function(){j||g()};h.stop=function(){j&&clearTimeout(j);j=b};a.browser.msie&&!o&&function(){var b,c;h.start=function(){if(!b)c=(c=a.fn[f].src)&&c+e(),b=a('<iframe tabindex="-1" title="empty"/>').hide().one("load",function(){c||k(e());g()}).attr("src",c||"javascript:0").insertAfter("body")[0].contentWindow,d.onpropertychange=function(){try{if(event.propertyName==="title")b.document.title=d.title}catch(a){}}};h.stop=n;t=function(){return e(b.location.href)};k=function(g,c){var e=b.document,
-h=a.fn[f].domain;if(g!==c)e.title=d.title,e.open(),h&&e.write('<script>document.domain="'+h+'"<\/script>'),e.close(),b.location.hash=g}}();return h}()})(jQuery,this);(function(a,c){if(a.cleanData){var b=a.cleanData;a.cleanData=function(f){for(var d=0,g;(g=f[d])!=null;d++)a(g).triggerHandler("remove");b(f)}}else{var e=a.fn.remove;a.fn.remove=function(b,d){return this.each(function(){d||(!b||a.filter(b,[this]).length)&&a("*",this).add([this]).each(function(){a(this).triggerHandler("remove")});return e.call(a(this),
-b,d)})}}a.widget=function(b,d,g){var c=b.split(".")[0],e,b=b.split(".")[1];e=c+"-"+b;if(!g)g=d,d=a.Widget;a.expr[":"][e]=function(d){return!!a.data(d,b)};a[c]=a[c]||{};a[c][b]=function(a,b){arguments.length&&this._createWidget(a,b)};d=new d;d.options=a.extend(true,{},d.options);a[c][b].prototype=a.extend(true,d,{namespace:c,widgetName:b,widgetEventPrefix:a[c][b].prototype.widgetEventPrefix||b,widgetBaseClass:e},g);a.widget.bridge(b,a[c][b])};a.widget.bridge=function(b,d){a.fn[b]=function(g){var e=
-typeof g==="string",j=Array.prototype.slice.call(arguments,1),o=this,g=!e&&j.length?a.extend.apply(null,[true,g].concat(j)):g;if(e&&g.charAt(0)==="_")return o;e?this.each(function(){var d=a.data(this,b);if(!d)throw"cannot call methods on "+b+" prior to initialization; attempted to call method '"+g+"'";if(!a.isFunction(d[g]))throw"no such method '"+g+"' for "+b+" widget instance";var e=d[g].apply(d,j);if(e!==d&&e!==c)return o=e,false}):this.each(function(){var c=a.data(this,b);c?c.option(g||{})._init():
-a.data(this,b,new d(g,this))});return o}};a.Widget=function(a,b){arguments.length&&this._createWidget(a,b)};a.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(b,d){a.data(d,this.widgetName,this);this.element=a(d);this.options=a.extend(true,{},this.options,this._getCreateOptions(),b);var g=this;this.element.bind("remove."+this.widgetName,function(){g.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){var b=
-{};a.metadata&&(b=a.metadata.get(element)[this.widgetName]);return b},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(b,d){var g=b;if(arguments.length===0)return a.extend({},this.options);if(typeof b==="string"){if(d===c)return this.options[b];
-g={};g[b]=d}this._setOptions(g);return this},_setOptions:function(b){var d=this;a.each(b,function(a,b){d._setOption(a,b)});return this},_setOption:function(a,b){this.options[a]=b;a==="disabled"&&this.widget()[b?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",b);return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(b,d,g){var c=this.options[b],d=a.Event(d);
-d.type=(b===this.widgetEventPrefix?b:this.widgetEventPrefix+b).toLowerCase();g=g||{};if(d.originalEvent)for(var b=a.event.props.length,e;b;)e=a.event.props[--b],d[e]=d.originalEvent[e];this.element.trigger(d,g);return!(a.isFunction(c)&&c.call(this.element[0],d,g)===false||d.isDefaultPrevented())}}})(jQuery);(function(a,c){a.widget("mobile.widget",{_createWidget:function(){a.Widget.prototype._createWidget.apply(this,arguments);this._trigger("init")},_getCreateOptions:function(){var b=this.element,
-e={};a.each(this.options,function(a){var d=b.jqmData(a.replace(/[A-Z]/g,function(a){return"-"+a.toLowerCase()}));d!==c&&(e[a]=d)});return e},enhanceWithin:function(b,c){this.enhance(a(this.options.initSelector,a(b)),c)},enhance:function(b,c){var f,d=a(b),d=a.mobile.enhanceable(d);c&&d.length&&(f=(f=a.mobile.closestPageData(d))&&f.keepNativeSelector()||"",d=d.not(f));d[this.widgetName]()},raise:function(a){throw"Widget ["+this.widgetName+"]: "+a;}})})(jQuery);(function(a,c){var b={};a.mobile=a.extend({},
-{version:"1.1.0",ns:"",subPageUrlKey:"ui-page",activePageClass:"ui-page-active",activeBtnClass:"ui-btn-active",focusClass:"ui-focus",ajaxEnabled:true,hashListeningEnabled:true,linkBindingEnabled:true,defaultPageTransition:"fade",maxTransitionWidth:false,minScrollBack:250,touchOverflowEnabled:false,defaultDialogTransition:"pop",loadingMessage:"loading",pageLoadErrorMessage:"Error Loading Page",loadingMessageTextVisible:false,loadingMessageTheme:"a",pageLoadErrorMessageTheme:"e",autoInitializePage:true,
-pushStateEnabled:true,ignoreContentEnabled:false,orientationChangeEnabled:true,buttonMarkup:{hoverDelay:200},keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91},silentScroll:function(b){if(a.type(b)!==
-"number")b=a.mobile.defaultHomeScroll;a.event.special.scrollstart.enabled=false;setTimeout(function(){c.scrollTo(0,b);a(k).trigger("silentscroll",{x:0,y:b})},20);setTimeout(function(){a.event.special.scrollstart.enabled=true},150)},nsNormalizeDict:b,nsNormalize:function(d){return!d?void 0:b[d]||(b[d]=a.camelCase(a.mobile.ns+d))},getInheritedTheme:function(a,b){for(var c=a[0],e="",f=/ui-(bar|body|overlay)-([a-z])\b/,m,p;c;){m=c.className||"";if((p=f.exec(m))&&(e=p[2]))break;c=c.parentNode}return e||
-b||"a"},closestPageData:function(a){return a.closest(':jqmData(role="page"), :jqmData(role="dialog")').data("page")},enhanceable:function(a){return this.haveParents(a,"enhance")},hijackable:function(a){return this.haveParents(a,"ajax")},haveParents:function(b,c){if(!a.mobile.ignoreContentEnabled)return b;for(var e=b.length,f=a(),o,m,p,l=0;l<e;l++){m=b.eq(l);p=false;for(o=b[l];o;){if((o.getAttribute?o.getAttribute("data-"+a.mobile.ns+c):"")==="false"){p=true;break}o=o.parentNode}p||(f=f.add(m))}return f}},
-a.mobile);a.fn.jqmData=function(b,c){var f;typeof b!="undefined"&&(b&&(b=a.mobile.nsNormalize(b)),f=this.data.apply(this,arguments.length<2?[b]:[b,c]));return f};a.jqmData=function(b,c,f){var e;typeof c!="undefined"&&(e=a.data(b,c?a.mobile.nsNormalize(c):c,f));return e};a.fn.jqmRemoveData=function(b){return this.removeData(a.mobile.nsNormalize(b))};a.jqmRemoveData=function(b,c){return a.removeData(b,a.mobile.nsNormalize(c))};a.fn.removeWithDependents=function(){a.removeWithDependents(this)};a.removeWithDependents=
-function(b){b=a(b);(b.jqmData("dependents")||a()).remove();b.remove()};a.fn.addDependents=function(b){a.addDependents(a(this),b)};a.addDependents=function(b,c){var f=a(b).jqmData("dependents")||a();a(b).jqmData("dependents",a.merge(f,c))};a.fn.getEncodedText=function(){return a("<div/>").text(a(this).text()).html()};a.fn.jqmEnhanceable=function(){return a.mobile.enhanceable(this)};a.fn.jqmHijackable=function(){return a.mobile.hijackable(this)};var e=a.find,f=/:jqmData\(([^)]*)\)/g;a.find=function(b,
-c,h,j){b=b.replace(f,"[data-"+(a.mobile.ns||"")+"$1]");return e.call(this,b,c,h,j)};a.extend(a.find,e);a.find.matches=function(b,c){return a.find(b,null,null,c)};a.find.matchesSelector=function(b,c){return a.find(c,null,null,[b]).length>0}})(jQuery,this);(function(a){a(s);var c=a("html");a.mobile.media=function(){var b={},e=a("<div id='jquery-mediatest'>"),f=a("<body>").append(e);return function(a){if(!(a in b)){var g=k.createElement("style"),h="@media "+a+" { #jquery-mediatest { position:absolute; } }";
-g.type="text/css";g.styleSheet?g.styleSheet.cssText=h:g.appendChild(k.createTextNode(h));c.prepend(f).prepend(g);b[a]=e.css("position")==="absolute";f.add(g).remove()}return b[a]}}()})(jQuery);(function(a,c){function b(a){var b=a.charAt(0).toUpperCase()+a.substr(1),a=(a+" "+g.join(b+" ")+b).split(" "),f;for(f in a)if(d[a[f]]!==c)return true}function e(a,b,c){var d=k.createElement("div"),c=c?[c]:g,f;for(i=0;i<c.length;i++){var e=c[i],h="-"+e.charAt(0).toLowerCase()+e.substr(1)+"-"+a+": "+b+";",e=e.charAt(0).toUpperCase()+
-e.substr(1)+(a.charAt(0).toUpperCase()+a.substr(1));d.setAttribute("style",h);d.style[e]&&(f=true)}return!!f}var f=a("<body>").prependTo("html"),d=f[0].style,g=["Webkit","Moz","O"],h="palmGetResource"in s,j=s.operamini&&{}.toString.call(s.operamini)==="[object OperaMini]",o=s.blackberry;a.extend(a.mobile,{browser:{}});a.mobile.browser.ie=function(){for(var a=3,b=k.createElement("div"),c=b.all||[];b.innerHTML="<\!--[if gt IE "+ ++a+"]><br><![endif]--\>",c[0];);return a>4?a:!a}();a.extend(a.support,
-{orientation:"orientation"in s&&"onorientationchange"in s,touch:"ontouchend"in k,cssTransitions:"WebKitTransitionEvent"in s||e("transition","height 100ms linear"),pushState:"pushState"in history&&"replaceState"in history,mediaquery:a.mobile.media("only all"),cssPseudoElement:!!b("content"),touchOverflow:!!b("overflowScrolling"),cssTransform3d:e("perspective","10px","moz")||a.mobile.media("(-"+g.join("-transform-3d),(-")+"-transform-3d),(transform-3d)"),boxShadow:!!b("boxShadow")&&!o,scrollTop:("pageXOffset"in
-s||"scrollTop"in k.documentElement||"scrollTop"in f[0])&&!h&&!j,dynamicBaseTag:function(){var b=location.protocol+"//"+location.host+location.pathname+"ui-dir/",c=a("head base"),d=null,e="",g;c.length?e=c.attr("href"):c=d=a("<base>",{href:b}).appendTo("head");g=a("<a href='testurl' />").prependTo(f)[0].href;c[0].href=e||location.pathname;d&&d.remove();return g.indexOf(b)===0}()});f.remove();h=function(){var a=s.navigator.userAgent;return a.indexOf("Nokia")>-1&&(a.indexOf("Symbian/3")>-1||a.indexOf("Series60/5")>
--1)&&a.indexOf("AppleWebKit")>-1&&a.match(/(BrowserNG|NokiaBrowser)\/7\.[0-3]/)}();a.mobile.gradeA=function(){return a.support.mediaquery||a.mobile.browser.ie&&a.mobile.browser.ie>=7};a.mobile.ajaxBlacklist=s.blackberry&&!s.WebKitPoint||j||h;h&&a(function(){a("head link[rel='stylesheet']").attr("rel","alternate stylesheet").attr("rel","stylesheet")});a.support.boxShadow||a("html").addClass("ui-mobile-nosupport-boxshadow")})(jQuery);(function(a,c,b){function e(b,c,d){var f=d.type;d.type=c;a.event.handle.call(b,
-d);d.type=f}a.each("touchstart touchmove touchend orientationchange throttledresize tap taphold swipe swipeleft swiperight scrollstart scrollstop".split(" "),function(b,c){a.fn[c]=function(a){return a?this.bind(c,a):this.trigger(c)};a.attrFn[c]=true});var f=a.support.touch,d=f?"touchstart":"mousedown",g=f?"touchend":"mouseup",h=f?"touchmove":"mousemove";a.event.special.scrollstart={enabled:true,setup:function(){function b(a,f){d=f;e(c,d?"scrollstart":"scrollstop",a)}var c=this,d,f;a(c).bind("touchmove scroll",
-function(c){a.event.special.scrollstart.enabled&&(d||b(c,true),clearTimeout(f),f=setTimeout(function(){b(c,false)},50))})}};a.event.special.tap={setup:function(){var b=this,c=a(b);c.bind("vmousedown",function(d){function f(){clearTimeout(q)}function g(){f();c.unbind("vclick",h).unbind("vmouseup",f);a(k).unbind("vmousecancel",g)}function h(a){g();n==a.target&&e(b,"tap",a)}if(d.which&&d.which!==1)return false;var n=d.target,q;c.bind("vmouseup",f).bind("vclick",h);a(k).bind("vmousecancel",g);q=setTimeout(function(){e(b,
-"taphold",a.Event("taphold",{target:n}))},750)})}};a.event.special.swipe={scrollSupressionThreshold:10,durationThreshold:1E3,horizontalDistanceThreshold:30,verticalDistanceThreshold:75,setup:function(){var c=a(this);c.bind(d,function(d){function f(b){if(l){var c=b.originalEvent.touches?b.originalEvent.touches[0]:b;k={time:(new Date).getTime(),coords:[c.pageX,c.pageY]};Math.abs(l.coords[0]-k.coords[0])>a.event.special.swipe.scrollSupressionThreshold&&b.preventDefault()}}var e=d.originalEvent.touches?
-d.originalEvent.touches[0]:d,l={time:(new Date).getTime(),coords:[e.pageX,e.pageY],origin:a(d.target)},k;c.bind(h,f).one(g,function(){c.unbind(h,f);l&&k&&k.time-l.time<a.event.special.swipe.durationThreshold&&Math.abs(l.coords[0]-k.coords[0])>a.event.special.swipe.horizontalDistanceThreshold&&Math.abs(l.coords[1]-k.coords[1])<a.event.special.swipe.verticalDistanceThreshold&&l.origin.trigger("swipe").trigger(l.coords[0]>k.coords[0]?"swipeleft":"swiperight");l=k=b})})}};(function(a,b){function c(){var a=
-f();a!==e&&(e=a,d.trigger("orientationchange"))}var d=a(b),f,e,g,h,t={0:true,180:true};if(a.support.orientation&&(g=b.innerWidth||a(b).width(),h=b.innerHeight||a(b).height(),g=g>h&&g-h>50,h=t[b.orientation],g&&h||!g&&!h))t={"-90":true,90:true};a.event.special.orientationchange={setup:function(){if(a.support.orientation&&a.mobile.orientationChangeEnabled)return false;e=f();d.bind("throttledresize",c)},teardown:function(){if(a.support.orientation&&a.mobile.orientationChangeEnabled)return false;d.unbind("throttledresize",
-c)},add:function(a){var b=a.handler;a.handler=function(a){a.orientation=f();return b.apply(this,arguments)}}};a.event.special.orientationchange.orientation=f=function(){var c=true,c=k.documentElement;return(c=a.support.orientation?t[b.orientation]:c&&c.clientWidth/c.clientHeight<1.1)?"portrait":"landscape"}})(jQuery,c);(function(){a.event.special.throttledresize={setup:function(){a(this).bind("resize",b)},teardown:function(){a(this).unbind("resize",b)}};var b=function(){f=(new Date).getTime();g=f-
-c;g>=250?(c=f,a(this).trigger("throttledresize")):(d&&clearTimeout(d),d=setTimeout(b,250-g))},c=0,d,f,g})();a.each({scrollstop:"scrollstart",taphold:"tap",swipeleft:"swipe",swiperight:"swipe"},function(b,c){a.event.special[b]={setup:function(){a(this).bind(c,a.noop)}}})})(jQuery,this);(function(a){a.widget("mobile.page",a.mobile.widget,{options:{theme:"c",domCache:false,keepNativeDefault:":jqmData(role='none'), :jqmData(role='nojs')"},_create:function(){var a=this;if(a._trigger("beforecreate")===
-false)return false;a.element.attr("tabindex","0").addClass("ui-page ui-body-"+a.options.theme).bind("pagebeforehide",function(){a.removeContainerBackground()}).bind("pagebeforeshow",function(){a.setContainerBackground()})},removeContainerBackground:function(){a.mobile.pageContainer.removeClass("ui-overlay-"+a.mobile.getInheritedTheme(this.element.parent()))},setContainerBackground:function(c){this.options.theme&&a.mobile.pageContainer.addClass("ui-overlay-"+(c||this.options.theme))},keepNativeSelector:function(){var c=
-this.options;return c.keepNative&&a.trim(c.keepNative)&&c.keepNative!==c.keepNativeDefault?[c.keepNative,c.keepNativeDefault].join(", "):c.keepNativeDefault}})})(jQuery);(function(a,c,b){var e=function(d){d===b&&(d=true);return function(b,f,e,o){var k=new a.Deferred,p=f?" reverse":"",l=a.mobile.urlHistory.getActive().lastScroll||a.mobile.defaultHomeScroll,r=a.mobile.getScreenHeight(),n=a.mobile.maxTransitionWidth!==false&&a(c).width()>a.mobile.maxTransitionWidth,q=!a.support.cssTransitions||n||!b||
-b==="none",t=function(){a.mobile.pageContainer.toggleClass("ui-mobile-viewport-transitioning viewport-"+b)},x=function(){a.event.special.scrollstart.enabled=false;c.scrollTo(0,l);setTimeout(function(){a.event.special.scrollstart.enabled=true},150)},u=function(){o.removeClass(a.mobile.activePageClass+" out in reverse "+b).height("")},n=function(){o&&d&&u();e.addClass(a.mobile.activePageClass);a.mobile.focusPage(e);e.height(r+l);x();q||e.animationComplete(w);e.addClass(b+" in"+p);q&&w()},w=function(){d||
-o&&u();e.removeClass("out in reverse "+b).height("");t();a(c).scrollTop()!==l&&x();k.resolve(b,f,e,o,true)};t();o&&!q?(d?o.animationComplete(n):n(),o.height(r+a(c).scrollTop()).addClass(b+" out"+p)):n();return k.promise()}},f=e(),e=e(false);a.mobile.defaultTransitionHandler=f;a.mobile.transitionHandlers={"default":a.mobile.defaultTransitionHandler,sequential:f,simultaneous:e};a.mobile.transitionFallbacks={}})(jQuery,this);(function(a,c){function b(b){r&&(!r.closest(".ui-page-active").length||b)&&
-r.removeClass(a.mobile.activeBtnClass);r=null}function e(){t=false;q.length>0&&a.mobile.changePage.apply(null,q.pop())}function f(b,c,d,f){c&&c.data("page")._trigger("beforehide",null,{nextPage:b});b.data("page")._trigger("beforeshow",null,{prevPage:c||a("")});a.mobile.hidePageLoadingMsg();d&&!a.support.cssTransform3d&&a.mobile.transitionFallbacks[d]&&(d=a.mobile.transitionFallbacks[d]);d=(a.mobile.transitionHandlers[d||"default"]||a.mobile.defaultTransitionHandler)(d,f,b,c);d.done(function(){c&&
-c.data("page")._trigger("hide",null,{nextPage:b});b.data("page")._trigger("show",null,{prevPage:c||a("")})});return d}function d(){return s.innerHeight||a(s).height()}function g(){var b=a("."+a.mobile.activePageClass),c=parseFloat(b.css("padding-top")),f=parseFloat(b.css("padding-bottom"));b.css("min-height",d()-c-f)}function h(b,c){c&&b.attr("data-"+a.mobile.ns+"role",c);b.page()}function j(a){for(;a;){if(typeof a.nodeName==="string"&&a.nodeName.toLowerCase()=="a")break;a=a.parentNode}return a}function o(b){var b=
-a(b).closest(".ui-page").jqmData("url"),c=v.hrefNoHash;if(!b||!l.isPath(b))b=c;return l.makeUrlAbsolute(b,c)}var m=a(s);a("html");var p=a("head"),l={urlParseRE:/^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,parseUrl:function(b){if(a.type(b)==="object")return b;b=l.urlParseRE.exec(b||"")||[];return{href:b[0]||"",hrefNoHash:b[1]||"",hrefNoSearch:b[2]||"",domain:b[3]||"",
-protocol:b[4]||"",doubleSlash:b[5]||"",authority:b[6]||"",username:b[8]||"",password:b[9]||"",host:b[10]||"",hostname:b[11]||"",port:b[12]||"",pathname:b[13]||"",directory:b[14]||"",filename:b[15]||"",search:b[16]||"",hash:b[17]||""}},makePathAbsolute:function(a,b){if(a&&a.charAt(0)==="/")return a;for(var a=a||"",c=(b=b?b.replace(/^\/|(\/[^\/]*|[^\/]+)$/g,""):"")?b.split("/"):[],d=a.split("/"),f=0;f<d.length;f++){var e=d[f];switch(e){case ".":break;case "..":c.length&&c.pop();break;default:c.push(e)}}return"/"+
-c.join("/")},isSameDomain:function(a,b){return l.parseUrl(a).domain===l.parseUrl(b).domain},isRelativeUrl:function(a){return l.parseUrl(a).protocol===""},isAbsoluteUrl:function(a){return l.parseUrl(a).protocol!==""},makeUrlAbsolute:function(a,b){if(!l.isRelativeUrl(a))return a;var c=l.parseUrl(a),d=l.parseUrl(b),f=c.protocol||d.protocol,e=c.protocol?c.doubleSlash:c.doubleSlash||d.doubleSlash,g=c.authority||d.authority,h=c.pathname!=="",j=l.makePathAbsolute(c.pathname||d.filename,d.pathname);return f+
-e+g+j+(c.search||!h&&d.search||"")+c.hash},addSearchParams:function(b,c){var d=l.parseUrl(b),f=typeof c==="object"?a.param(c):c,e=d.search||"?";return d.hrefNoSearch+e+(e.charAt(e.length-1)!=="?"?"&":"")+f+(d.hash||"")},convertUrlToDataUrl:function(a){var b=l.parseUrl(a);if(l.isEmbeddedPage(b))return b.hash.split(x)[0].replace(/^#/,"");else if(l.isSameDomain(b,v))return b.hrefNoHash.replace(v.domain,"");return a},get:function(a){if(a===c)a=location.hash;return l.stripHash(a).replace(/[^\/]*\.[^\/*]+$/,
-"")},getFilePath:function(b){var c="&"+a.mobile.subPageUrlKey;return b&&b.split(c)[0].split(x)[0]},set:function(a){location.hash=a},isPath:function(a){return/\//.test(a)},clean:function(a){return a.replace(v.domain,"")},stripHash:function(a){return a.replace(/^#/,"")},cleanHash:function(a){return l.stripHash(a.replace(/\?.*$/,"").replace(x,""))},isExternal:function(a){a=l.parseUrl(a);return a.protocol&&a.domain!==w.domain?true:false},hasProtocol:function(a){return/^(:?\w+:)/.test(a)},isFirstPageUrl:function(b){var b=
-l.parseUrl(l.makeUrlAbsolute(b,v)),d=a.mobile.firstPage,d=d&&d[0]?d[0].id:c;return(b.hrefNoHash===w.hrefNoHash||y&&b.hrefNoHash===v.hrefNoHash)&&(!b.hash||b.hash==="#"||d&&b.hash.replace(/^#/,"")===d)},isEmbeddedPage:function(a){a=l.parseUrl(a);return a.protocol!==""?a.hash&&(a.hrefNoHash===w.hrefNoHash||y&&a.hrefNoHash===v.hrefNoHash):/^#/.test(a.href)}},r=null,n={stack:[],activeIndex:0,getActive:function(){return n.stack[n.activeIndex]},getPrev:function(){return n.stack[n.activeIndex-1]},getNext:function(){return n.stack[n.activeIndex+
-1]},addNew:function(a,b,c,d,f){n.getNext()&&n.clearForward();n.stack.push({url:a,transition:b,title:c,pageUrl:d,role:f});n.activeIndex=n.stack.length-1},clearForward:function(){n.stack=n.stack.slice(0,n.activeIndex+1)},directHashChange:function(b){var d,f,e;this.getActive();a.each(n.stack,function(a,c){b.currentUrl===c.url&&(d=a<n.activeIndex,f=!d,e=a)});this.activeIndex=e!==c?e:this.activeIndex;d?(b.either||b.isBack)(true):f&&(b.either||b.isForward)(false)},ignoreNextHashChange:false},q=[],t=false,
-x="&ui-state=dialog",u=p.children("base"),w=l.parseUrl(location.href),v=u.length?l.parseUrl(l.makeUrlAbsolute(u.attr("href"),w.href)):w,y=w.hrefNoHash!==v.hrefNoHash,B=a.support.dynamicBaseTag?{element:u.length?u:a("<base>",{href:v.hrefNoHash}).prependTo(p),set:function(a){B.element.attr("href",l.makeUrlAbsolute(a,v))},reset:function(){B.element.attr("href",v.hrefNoHash)}}:c;a.mobile.focusPage=function(a){var b=a.find("[autofocus]"),c=a.find(".ui-title:eq(0)");b.length?b.focus():c.length?c.focus():
-a.focus()};var E=true,A,C;A=function(){if(E){var b=a.mobile.urlHistory.getActive();if(b){var c=m.scrollTop();b.lastScroll=c<a.mobile.minScrollBack?a.mobile.defaultHomeScroll:c}}};C=function(){setTimeout(A,100)};m.bind(a.support.pushState?"popstate":"hashchange",function(){E=false});m.one(a.support.pushState?"popstate":"hashchange",function(){E=true});m.one("pagecontainercreate",function(){a.mobile.pageContainer.bind("pagechange",function(){E=true;m.unbind("scrollstop",C);m.bind("scrollstop",C)})});
-m.bind("scrollstop",C);a.mobile.getScreenHeight=d;a.fn.animationComplete=function(b){return a.support.cssTransitions?a(this).one("webkitAnimationEnd animationend",b):(setTimeout(b,0),a(this))};a.mobile.path=l;a.mobile.base=B;a.mobile.urlHistory=n;a.mobile.dialogHashKey=x;a.mobile.allowCrossDomainPages=false;a.mobile.getDocumentUrl=function(b){return b?a.extend({},w):w.href};a.mobile.getDocumentBase=function(b){return b?a.extend({},v):v.href};a.mobile._bindPageRemove=function(){var b=a(this);!b.data("page").options.domCache&&
-b.is(":jqmData(external-page='true')")&&b.bind("pagehide.remove",function(){var b=a(this),c=new a.Event("pageremove");b.trigger(c);c.isDefaultPrevented()||b.removeWithDependents()})};a.mobile.loadPage=function(b,d){var f=a.Deferred(),e=a.extend({},a.mobile.loadPage.defaults,d),g=null,j=null,k=l.makeUrlAbsolute(b,a.mobile.activePage&&o(a.mobile.activePage)||v.hrefNoHash);if(e.data&&e.type==="get")k=l.addSearchParams(k,e.data),e.data=c;if(e.data&&e.type==="post")e.reloadPage=true;var u=l.getFilePath(k),
-n=l.convertUrlToDataUrl(k);e.pageContainer=e.pageContainer||a.mobile.pageContainer;g=e.pageContainer.children(":jqmData(url='"+n+"')");g.length===0&&n&&!l.isPath(n)&&(g=e.pageContainer.children("#"+n).attr("data-"+a.mobile.ns+"url",n));if(g.length===0)if(a.mobile.firstPage&&l.isFirstPageUrl(u))a.mobile.firstPage.parent().length&&(g=a(a.mobile.firstPage));else if(l.isEmbeddedPage(u))return f.reject(k,d),f.promise();B&&B.reset();if(g.length){if(!e.reloadPage)return h(g,e.role),f.resolve(k,d,g),f.promise();
-j=g}var m=e.pageContainer,x=new a.Event("pagebeforeload"),p={url:b,absUrl:k,dataUrl:n,deferred:f,options:e};m.trigger(x,p);if(x.isDefaultPrevented())return f.promise();if(e.showLoadMsg)var r=setTimeout(function(){a.mobile.showPageLoadingMsg()},e.loadMsgDelay);!a.mobile.allowCrossDomainPages&&!l.isSameDomain(w,k)?f.reject(k,d):a.ajax({url:u,type:e.type,data:e.data,dataType:"html",success:function(c,o,m){var x=a("<div></div>"),v=c.match(/<title[^>]*>([^<]*)/)&&RegExp.$1,w=RegExp("\\bdata-"+a.mobile.ns+
-"url=[\"']?([^\"'>]*)[\"']?");RegExp("(<[^>]+\\bdata-"+a.mobile.ns+"role=[\"']?page[\"']?[^>]*>)").test(c)&&RegExp.$1&&w.test(RegExp.$1)&&RegExp.$1&&(b=u=l.getFilePath(RegExp.$1));B&&B.set(u);x.get(0).innerHTML=c;g=x.find(":jqmData(role='page'), :jqmData(role='dialog')").first();g.length||(g=a("<div data-"+a.mobile.ns+"role='page'>"+c.split(/<\/?body[^>]*>/gmi)[1]+"</div>"));v&&!g.jqmData("title")&&(~v.indexOf("&")&&(v=a("<div>"+v+"</div>").text()),g.jqmData("title",v));if(!a.support.dynamicBaseTag){var q=
-l.get(u);g.find("[src], link[href], a[rel='external'], :jqmData(ajax='false'), a[target]").each(function(){var b=a(this).is("[href]")?"href":a(this).is("[src]")?"src":"action",c=a(this).attr(b),c=c.replace(location.protocol+"//"+location.host+location.pathname,"");/^(\w+:|#|\/)/.test(c)||a(this).attr(b,q+c)})}g.attr("data-"+a.mobile.ns+"url",l.convertUrlToDataUrl(u)).attr("data-"+a.mobile.ns+"external-page",true).appendTo(e.pageContainer);g.one("pagecreate",a.mobile._bindPageRemove);h(g,e.role);k.indexOf("&"+
-a.mobile.subPageUrlKey)>-1&&(g=e.pageContainer.children(":jqmData(url='"+n+"')"));e.showLoadMsg&&(clearTimeout(r),a.mobile.hidePageLoadingMsg());p.xhr=m;p.textStatus=o;p.page=g;e.pageContainer.trigger("pageload",p);f.resolve(k,d,g,j)},error:function(b,c,g){B&&B.set(l.get());p.xhr=b;p.textStatus=c;p.errorThrown=g;b=new a.Event("pageloadfailed");e.pageContainer.trigger(b,p);b.isDefaultPrevented()||(e.showLoadMsg&&(clearTimeout(r),a.mobile.hidePageLoadingMsg(),a.mobile.showPageLoadingMsg(a.mobile.pageLoadErrorMessageTheme,
-a.mobile.pageLoadErrorMessage,true),setTimeout(a.mobile.hidePageLoadingMsg,1500)),f.reject(k,d))}});return f.promise()};a.mobile.loadPage.defaults={type:"get",data:c,reloadPage:false,role:c,showLoadMsg:false,pageContainer:c,loadMsgDelay:50};a.mobile.changePage=function(d,g){if(t)q.unshift(arguments);else{var j=a.extend({},a.mobile.changePage.defaults,g);j.pageContainer=j.pageContainer||a.mobile.pageContainer;j.fromPage=j.fromPage||a.mobile.activePage;var u=j.pageContainer,o=new a.Event("pagebeforechange"),
-m={toPage:d,options:j};u.trigger(o,m);if(!o.isDefaultPrevented())if(d=m.toPage,t=true,typeof d=="string")a.mobile.loadPage(d,j).done(function(b,c,d,e){t=false;c.duplicateCachedPage=e;a.mobile.changePage(d,c)}).fail(function(){t=false;b(true);e();j.pageContainer.trigger("pagechangefailed",m)});else{if(d[0]===a.mobile.firstPage[0]&&!j.dataUrl)j.dataUrl=w.hrefNoHash;var o=j.fromPage,p=j.dataUrl&&l.convertUrlToDataUrl(j.dataUrl)||d.jqmData("url"),v=p;l.getFilePath(p);var r=n.getActive(),s=n.activeIndex===
-0,y=0,B=k.title,A=j.role==="dialog"||d.jqmData("role")==="dialog";if(o&&o[0]===d[0]&&!j.allowSamePageTransition)t=false,u.trigger("pagechange",m);else{h(d,j.role);j.fromHashChange&&n.directHashChange({currentUrl:p,isBack:function(){y=-1},isForward:function(){y=1}});try{k.activeElement&&k.activeElement.nodeName.toLowerCase()!="body"?a(k.activeElement).blur():a("input:focus, textarea:focus, select:focus").blur()}catch(E){}A&&r&&(p=(r.url||"")+x);if(j.changeHash!==false&&p)n.ignoreNextHashChange=true,
-l.set(p);var C=!r?B:d.jqmData("title")||d.children(":jqmData(role='header')").find(".ui-title").getEncodedText();C&&B==k.title&&(B=C);d.jqmData("title")||d.jqmData("title",B);j.transition=j.transition||(y&&!s?r.transition:c)||(A?a.mobile.defaultDialogTransition:a.mobile.defaultPageTransition);y||n.addNew(p,j.transition,B,v,j.role);k.title=n.getActive().title;a.mobile.activePage=d;j.reverse=j.reverse||y<0;f(d,o,j.transition,j.reverse).done(function(c,f,g,h,l){b();j.duplicateCachedPage&&j.duplicateCachedPage.remove();
-l||a.mobile.focusPage(d);e();u.trigger("pagechange",m)})}}}};a.mobile.changePage.defaults={transition:c,reverse:false,changeHash:true,fromHashChange:false,role:c,duplicateCachedPage:c,pageContainer:c,showLoadMsg:true,dataUrl:c,fromPage:c,allowSamePageTransition:false};a.mobile._registerInternalEvents=function(){a(k).delegate("form","submit",function(b){var c=a(this);if(a.mobile.ajaxEnabled&&!c.is(":jqmData(ajax='false')")&&c.jqmHijackable().length){var d=c.attr("method"),e=c.attr("target"),f=c.attr("action");
-if(!f&&(f=o(c),f===v.hrefNoHash))f=w.hrefNoSearch;f=l.makeUrlAbsolute(f,o(c));!l.isExternal(f)&&!e&&(a.mobile.changePage(f,{type:d&&d.length&&d.toLowerCase()||"get",data:c.serialize(),transition:c.jqmData("transition"),direction:c.jqmData("direction"),reloadPage:true}),b.preventDefault())}});a(k).bind("vclick",function(c){if(!(c.which>1)&&a.mobile.linkBindingEnabled&&(c=j(c.target),a(c).jqmHijackable().length&&c&&l.parseUrl(c.getAttribute("href")||"#").hash!=="#"))b(true),r=a(c).closest(".ui-btn").not(".ui-disabled"),
-r.addClass(a.mobile.activeBtnClass),a("."+a.mobile.activePageClass+" .ui-btn").not(c).blur(),a(c).jqmData("href",a(c).attr("href")).attr("href","#")});a(k).bind("click",function(d){if(a.mobile.linkBindingEnabled){var f=j(d.target),e=a(f),g;if(f&&!(d.which>1)&&e.jqmHijackable().length){g=function(){s.setTimeout(function(){b(true)},200)};e.jqmData("href")&&e.attr("href",e.jqmData("href"));if(e.is(":jqmData(rel='back')"))return s.history.back(),false;var h=o(e),f=l.makeUrlAbsolute(e.attr("href")||"#",
-h);if(!a.mobile.ajaxEnabled&&!l.isEmbeddedPage(f))g();else{if(f.search("#")!=-1)if(f=f.replace(/[^#]*#/,""))f=l.isPath(f)?l.makeUrlAbsolute(f,h):l.makeUrlAbsolute("#"+f,w.hrefNoHash);else{d.preventDefault();return}var h=e.is("[rel='external']")||e.is(":jqmData(ajax='false')")||e.is("[target]"),k=a.mobile.allowCrossDomainPages&&w.protocol==="file:"&&f.search(/^https?:/)!=-1;h||l.isExternal(f)&&!k?g():(g=e.jqmData("transition"),h=(h=e.jqmData("direction"))&&h==="reverse"||e.jqmData("back"),e=e.attr("data-"+
-a.mobile.ns+"rel")||c,a.mobile.changePage(f,{transition:g,reverse:h,role:e}),d.preventDefault())}}}});a(k).delegate(".ui-page","pageshow.prefetch",function(){var b=[];a(this).find("a:jqmData(prefetch)").each(function(){var c=a(this),d=c.attr("href");d&&a.inArray(d,b)===-1&&(b.push(d),a.mobile.loadPage(d,{role:c.attr("data-"+a.mobile.ns+"rel")}))})});a.mobile._handleHashChange=function(b){var d=l.stripHash(b),f={transition:a.mobile.urlHistory.stack.length===0?"none":c,changeHash:false,fromHashChange:true};
-if(!a.mobile.hashListeningEnabled||n.ignoreNextHashChange)n.ignoreNextHashChange=false;else{if(n.stack.length>1&&d.indexOf(x)>-1)if(a.mobile.activePage.is(".ui-dialog"))n.directHashChange({currentUrl:d,either:function(b){var c=a.mobile.urlHistory.getActive();d=c.pageUrl;a.extend(f,{role:c.role,transition:c.transition,reverse:b})}});else{n.directHashChange({currentUrl:d,isBack:function(){s.history.back()},isForward:function(){s.history.forward()}});return}d?(d=typeof d==="string"&&!l.isPath(d)?l.makeUrlAbsolute("#"+
-d,v):d,a.mobile.changePage(d,f)):a.mobile.changePage(a.mobile.firstPage,f)}};m.bind("hashchange",function(){a.mobile._handleHashChange(location.hash)});a(k).bind("pageshow",g);a(s).bind("throttledresize",g)}})(jQuery);(function(a,c){var b={},e=a(c),f=a.mobile.path.parseUrl(location.href);a.extend(b,{initialFilePath:f.pathname+f.search,initialHref:f.hrefNoHash,state:function(){return{hash:location.hash||"#"+b.initialFilePath,title:k.title,initialHref:b.initialHref}},resetUIKeys:function(b){var c="&"+
-a.mobile.subPageUrlKey,f=b.indexOf(a.mobile.dialogHashKey);f>-1?b=b.slice(0,f)+"#"+b.slice(f):b.indexOf(c)>-1&&(b=b.split(c).join("#"+c));return b},hashValueAfterReset:function(c){c=b.resetUIKeys(c);return a.mobile.path.parseUrl(c).hash},nextHashChangePrevented:function(c){a.mobile.urlHistory.ignoreNextHashChange=c;b.onHashChangeDisabled=c},onHashChange:function(){if(!b.onHashChangeDisabled){var c,f;c=location.hash;var e=a.mobile.path.isPath(c),j=e?location.href:a.mobile.getDocumentUrl();c=e?c.replace("#",
-""):c;f=b.state();c=a.mobile.path.makeUrlAbsolute(c,j);e&&(c=b.resetUIKeys(c));history.replaceState(f,k.title,c)}},onPopState:function(c){var c=c.originalEvent.state,f,h;if(c){f=b.hashValueAfterReset(a.mobile.urlHistory.getActive().url);h=b.hashValueAfterReset(c.hash.replace("#",""));if(f=f!==h)e.one("hashchange.pushstate",function(){b.nextHashChangePrevented(false)});b.nextHashChangePrevented(false);a.mobile._handleHashChange(c.hash);f&&b.nextHashChangePrevented(true)}},init:function(){e.bind("hashchange",
-b.onHashChange);e.bind("popstate",b.onPopState);location.hash===""&&history.replaceState(b.state(),k.title,location.href)}});a(function(){a.mobile.pushStateEnabled&&a.support.pushState&&b.init()})})(jQuery,this);jQuery.mobile.transitionFallbacks.pop="fade";(function(a){a.mobile.transitionHandlers.slide=a.mobile.transitionHandlers.simultaneous;a.mobile.transitionFallbacks.slide="fade"})(jQuery,this);jQuery.mobile.transitionFallbacks.slidedown="fade";jQuery.mobile.transitionFallbacks.slideup="fade";
-jQuery.mobile.transitionFallbacks.flip="fade";jQuery.mobile.transitionFallbacks.flow="fade";jQuery.mobile.transitionFallbacks.turn="fade";(function(a){a.mobile.page.prototype.options.degradeInputs={color:false,date:false,datetime:false,"datetime-local":false,email:false,month:false,number:false,range:"number",search:"text",tel:false,time:false,url:false,week:false};a(k).bind("pagecreate create",function(c){var b=a.mobile.closestPageData(a(c.target)),e;if(b)e=b.options,a(c.target).find("input").not(b.keepNativeSelector()).each(function(){var b=
-a(this),c=this.getAttribute("type"),g=e.degradeInputs[c]||"text";if(e.degradeInputs[c]){var h=a("<div>").html(b.clone()).html(),j=h.indexOf(" type=")>-1;b.replaceWith(h.replace(j?/\s+type=["']?\w+['"]?/:/\/?>/,' type="'+g+'" data-'+a.mobile.ns+'type="'+c+'"'+(j?"":">")))}})})})(jQuery);(function(a,c){a.widget("mobile.dialog",a.mobile.widget,{options:{closeBtnText:"Close",overlayTheme:"a",initSelector:":jqmData(role='dialog')"},_create:function(){var b=this,c=this.element,f=a("<a href='#' data-"+a.mobile.ns+
-"icon='delete' data-"+a.mobile.ns+"iconpos='notext'>"+this.options.closeBtnText+"</a>"),d=a("<div/>",{role:"dialog","class":"ui-dialog-contain ui-corner-all ui-overlay-shadow"});c.addClass("ui-dialog ui-overlay-"+this.options.overlayTheme);c.wrapInner(d).children().find(":jqmData(role='header')").prepend(f).end().children(":first-child").addClass("ui-corner-top").end().children(":last-child").addClass("ui-corner-bottom");f.bind("click",function(){b.close()});c.bind("vclick submit",function(b){var b=
-a(b.target).closest(b.type==="vclick"?"a":"form"),c;b.length&&!b.jqmData("transition")&&(c=a.mobile.urlHistory.getActive()||{},b.attr("data-"+a.mobile.ns+"transition",c.transition||a.mobile.defaultDialogTransition).attr("data-"+a.mobile.ns+"direction","reverse"))}).bind("pagehide",function(){a(this).find("."+a.mobile.activeBtnClass).removeClass(a.mobile.activeBtnClass)}).bind("pagebeforeshow",function(){b.options.overlayTheme&&b.element.page("removeContainerBackground").page("setContainerBackground",
-b.options.overlayTheme)})},close:function(){c.history.back()}});a(k).delegate(a.mobile.dialog.prototype.options.initSelector,"pagecreate",function(){a.mobile.dialog.prototype.enhance(this)})})(jQuery,this);(function(a){a.fn.fieldcontain=function(){return this.addClass("ui-field-contain ui-body ui-br")};a(k).bind("pagecreate create",function(c){a(":jqmData(role='fieldcontain')",c.target).jqmEnhanceable().fieldcontain()})})(jQuery);(function(a){a.fn.grid=function(c){return this.each(function(){var b=
-a(this),e=a.extend({grid:null},c),f=b.children(),d={solo:1,a:2,b:3,c:4,d:5},e=e.grid;if(!e)if(f.length<=5)for(var g in d)d[g]===f.length&&(e=g);else e="a";d=d[e];b.addClass("ui-grid-"+e);f.filter(":nth-child("+d+"n+1)").addClass("ui-block-a");d>1&&f.filter(":nth-child("+d+"n+2)").addClass("ui-block-b");d>2&&f.filter(":nth-child(3n+3)").addClass("ui-block-c");d>3&&f.filter(":nth-child(4n+4)").addClass("ui-block-d");d>4&&f.filter(":nth-child(5n+5)").addClass("ui-block-e")})}})(jQuery);(function(a){a(k).bind("pagecreate create",
-function(c){a(":jqmData(role='nojs')",c.target).addClass("ui-nojs")})})(jQuery);(function(a,c){function b(a){for(var b;a;){if((b=typeof a.className==="string"&&a.className+" ")&&b.indexOf("ui-btn ")>-1&&b.indexOf("ui-disabled ")<0)break;a=a.parentNode}return a}a.fn.buttonMarkup=function(b){for(var b=b&&a.type(b)=="object"?b:{},d=0;d<this.length;d++){var g=this.eq(d),h=g[0],j=a.extend({},a.fn.buttonMarkup.defaults,{icon:b.icon!==c?b.icon:g.jqmData("icon"),iconpos:b.iconpos!==c?b.iconpos:g.jqmData("iconpos"),
-theme:b.theme!==c?b.theme:g.jqmData("theme")||a.mobile.getInheritedTheme(g,"c"),inline:b.inline!==c?b.inline:g.jqmData("inline"),shadow:b.shadow!==c?b.shadow:g.jqmData("shadow"),corners:b.corners!==c?b.corners:g.jqmData("corners"),iconshadow:b.iconshadow!==c?b.iconshadow:g.jqmData("iconshadow"),mini:b.mini!==c?b.mini:g.jqmData("mini")},b),o="ui-btn-inner",m,p,l,r,n,q;a.each(j,function(b,c){h.setAttribute("data-"+a.mobile.ns+b,c);g.jqmData(b,c)});(q=a.data(h.tagName==="INPUT"||h.tagName==="BUTTON"?
-h.parentNode:h,"buttonElements"))?(h=q.outer,g=a(h),l=q.inner,r=q.text,a(q.icon).remove(),q.icon=null):(l=k.createElement(j.wrapperEls),r=k.createElement(j.wrapperEls));n=j.icon?k.createElement("span"):null;e&&!q&&e();if(!j.theme)j.theme=a.mobile.getInheritedTheme(g,"c");m="ui-btn ui-btn-up-"+j.theme;m+=j.inline?" ui-btn-inline":"";m+=j.shadow?" ui-shadow":"";m+=j.corners?" ui-btn-corner-all":"";j.mini!==c&&(m+=j.mini?" ui-mini":" ui-fullsize");j.inline!==c&&(m+=j.inline===false?" ui-btn-block":" ui-btn-inline");
-if(j.icon)j.icon="ui-icon-"+j.icon,j.iconpos=j.iconpos||"left",p="ui-icon "+j.icon,j.iconshadow&&(p+=" ui-icon-shadow");j.iconpos&&(m+=" ui-btn-icon-"+j.iconpos,j.iconpos=="notext"&&!g.attr("title")&&g.attr("title",g.getEncodedText()));o+=j.corners?" ui-btn-corner-all":"";j.iconpos&&j.iconpos==="notext"&&!g.attr("title")&&g.attr("title",g.getEncodedText());q&&g.removeClass(q.bcls||"");g.removeClass("ui-link").addClass(m);l.className=o;r.className="ui-btn-text";q||l.appendChild(r);if(n&&(n.className=
-p,!q||!q.icon))n.appendChild(k.createTextNode("\u00a0")),l.appendChild(n);for(;h.firstChild&&!q;)r.appendChild(h.firstChild);q||h.appendChild(l);q={bcls:m,outer:h,inner:l,text:r,icon:n};a.data(h,"buttonElements",q);a.data(l,"buttonElements",q);a.data(r,"buttonElements",q);n&&a.data(n,"buttonElements",q)}return this};a.fn.buttonMarkup.defaults={corners:true,shadow:true,iconshadow:true,wrapperEls:"span"};var e=function(){var c=a.mobile.buttonMarkup.hoverDelay,d,g;a(k).bind({"vmousedown vmousecancel vmouseup vmouseover vmouseout focus blur scrollstart":function(e){var j,
-k=a(b(e.target)),e=e.type;if(k.length)if(j=k.attr("data-"+a.mobile.ns+"theme"),e==="vmousedown")a.support.touch?d=setTimeout(function(){k.removeClass("ui-btn-up-"+j).addClass("ui-btn-down-"+j)},c):k.removeClass("ui-btn-up-"+j).addClass("ui-btn-down-"+j);else if(e==="vmousecancel"||e==="vmouseup")k.removeClass("ui-btn-down-"+j).addClass("ui-btn-up-"+j);else if(e==="vmouseover"||e==="focus")a.support.touch?g=setTimeout(function(){k.removeClass("ui-btn-up-"+j).addClass("ui-btn-hover-"+j)},c):k.removeClass("ui-btn-up-"+
-j).addClass("ui-btn-hover-"+j);else if(e==="vmouseout"||e==="blur"||e==="scrollstart")k.removeClass("ui-btn-hover-"+j+" ui-btn-down-"+j).addClass("ui-btn-up-"+j),d&&clearTimeout(d),g&&clearTimeout(g)},"focusin focus":function(c){a(b(c.target)).addClass(a.mobile.focusClass)},"focusout blur":function(c){a(b(c.target)).removeClass(a.mobile.focusClass)}});e=null};a(k).bind("pagecreate create",function(b){a(":jqmData(role='button'), .ui-bar > a, .ui-header > a, .ui-footer > a, .ui-bar > :jqmData(role='controlgroup') > a",
-b.target).not(".ui-btn, :jqmData(role='none'), :jqmData(role='nojs')").buttonMarkup()})})(jQuery);(function(a){a.mobile.page.prototype.options.backBtnText="Back";a.mobile.page.prototype.options.addBackBtn=false;a.mobile.page.prototype.options.backBtnTheme=null;a.mobile.page.prototype.options.headerTheme="a";a.mobile.page.prototype.options.footerTheme="a";a.mobile.page.prototype.options.contentTheme=null;a(k).delegate(":jqmData(role='page'), :jqmData(role='dialog')","pagecreate",function(){var c=a(this),
-b=c.data("page").options,e=c.jqmData("role"),f=b.theme;a(":jqmData(role='header'), :jqmData(role='footer'), :jqmData(role='content')",this).jqmEnhanceable().each(function(){var d=a(this),g=d.jqmData("role"),h=d.jqmData("theme"),j=h||b.contentTheme||e==="dialog"&&f,k;d.addClass("ui-"+g);if(g==="header"||g==="footer"){var m=h||(g==="header"?b.headerTheme:b.footerTheme)||f;d.addClass("ui-bar-"+m).attr("role",g==="header"?"banner":"contentinfo");g==="header"&&(h=d.children("a"),k=h.hasClass("ui-btn-left"),
-j=h.hasClass("ui-btn-right"),k=k||h.eq(0).not(".ui-btn-right").addClass("ui-btn-left").length,j||h.eq(1).addClass("ui-btn-right"));b.addBackBtn&&g==="header"&&a(".ui-page").length>1&&c.jqmData("url")!==a.mobile.path.stripHash(location.hash)&&!k&&a("<a href='#' class='ui-btn-left' data-"+a.mobile.ns+"rel='back' data-"+a.mobile.ns+"icon='arrow-l'>"+b.backBtnText+"</a>").attr("data-"+a.mobile.ns+"theme",b.backBtnTheme||m).prependTo(d);d.children("h1, h2, h3, h4, h5, h6").addClass("ui-title").attr({role:"heading",
-"aria-level":"1"})}else g==="content"&&(j&&d.addClass("ui-body-"+j),d.attr("role","main"))})})})(jQuery);(function(a){a.widget("mobile.collapsible",a.mobile.widget,{options:{expandCueText:" click to expand contents",collapseCueText:" click to collapse contents",collapsed:true,heading:"h1,h2,h3,h4,h5,h6,legend",theme:null,contentTheme:null,iconTheme:"d",mini:false,initSelector:":jqmData(role='collapsible')"},_create:function(){var c=this.element,b=this.options,e=c.addClass("ui-collapsible"),f=c.children(b.heading).first(),
-d=e.wrapInner("<div class='ui-collapsible-content'></div>").find(".ui-collapsible-content"),g=c.closest(":jqmData(role='collapsible-set')").addClass("ui-collapsible-set");f.is("legend")&&(f=a("<div role='heading'>"+f.html()+"</div>").insertBefore(f),f.next().remove());if(g.length){if(!b.theme)b.theme=g.jqmData("theme")||a.mobile.getInheritedTheme(g,"c");if(!b.contentTheme)b.contentTheme=g.jqmData("content-theme");if(!b.iconPos)b.iconPos=g.jqmData("iconpos");if(!b.mini)b.mini=g.jqmData("mini")}d.addClass(b.contentTheme?
-"ui-body-"+b.contentTheme:"");f.insertBefore(d).addClass("ui-collapsible-heading").append("<span class='ui-collapsible-heading-status'></span>").wrapInner("<a href='#' class='ui-collapsible-heading-toggle'></a>").find("a").first().buttonMarkup({shadow:false,corners:false,iconpos:c.jqmData("iconpos")||b.iconPos||"left",icon:"plus",mini:b.mini,theme:b.theme}).add(".ui-btn-inner",c).addClass("ui-corner-top ui-corner-bottom");e.bind("expand collapse",function(c){if(!c.isDefaultPrevented()){c.preventDefault();
-var j=a(this),c=c.type==="collapse",k=b.contentTheme;f.toggleClass("ui-collapsible-heading-collapsed",c).find(".ui-collapsible-heading-status").text(c?b.expandCueText:b.collapseCueText).end().find(".ui-icon").toggleClass("ui-icon-minus",!c).toggleClass("ui-icon-plus",c);j.toggleClass("ui-collapsible-collapsed",c);d.toggleClass("ui-collapsible-content-collapsed",c).attr("aria-hidden",c);if(k&&(!g.length||e.jqmData("collapsible-last")))f.find("a").first().add(f.find(".ui-btn-inner")).toggleClass("ui-corner-bottom",
-c),d.toggleClass("ui-corner-bottom",!c);d.trigger("updatelayout")}}).trigger(b.collapsed?"collapse":"expand");f.bind("click",function(a){var b=f.is(".ui-collapsible-heading-collapsed")?"expand":"collapse";e.trigger(b);a.preventDefault()})}});a(k).bind("pagecreate create",function(c){a.mobile.collapsible.prototype.enhanceWithin(c.target)})})(jQuery);(function(a,c){a.widget("mobile.collapsibleset",a.mobile.widget,{options:{initSelector:":jqmData(role='collapsible-set')"},_create:function(){var b=this.element.addClass("ui-collapsible-set"),
-e=this.options;if(!e.theme)e.theme=a.mobile.getInheritedTheme(b,"c");if(!e.contentTheme)e.contentTheme=b.jqmData("content-theme");if(!e.corners)e.corners=b.jqmData("corners")===c?true:false;b.jqmData("collapsiblebound")||b.jqmData("collapsiblebound",true).bind("expand collapse",function(b){var c=b.type==="collapse",b=a(b.target).closest(".ui-collapsible"),e=b.data("collapsible");e.options.contentTheme&&b.jqmData("collapsible-last")&&(b.find(e.options.heading).first().find("a").first().add(".ui-btn-inner").toggleClass("ui-corner-bottom",
-c),b.find(".ui-collapsible-content").toggleClass("ui-corner-bottom",!c))}).bind("expand",function(b){a(b.target).closest(".ui-collapsible").siblings(".ui-collapsible").trigger("collapse")})},_init:function(){this.refresh()},refresh:function(){var b=this.options,c=this.element.children(":jqmData(role='collapsible')");a.mobile.collapsible.prototype.enhance(c.not(".ui-collapsible"));c.each(function(){a(this).find(a.mobile.collapsible.prototype.options.heading).find("a").first().add(".ui-btn-inner").removeClass("ui-corner-top ui-corner-bottom")});
-c.first().find("a").first().addClass(b.corners?"ui-corner-top":"").find(".ui-btn-inner").addClass("ui-corner-top");c.last().jqmData("collapsible-last",true).find("a").first().addClass(b.corners?"ui-corner-bottom":"").find(".ui-btn-inner").addClass("ui-corner-bottom")}});a(k).bind("pagecreate create",function(b){a.mobile.collapsibleset.prototype.enhanceWithin(b.target)})})(jQuery);(function(a,c){a.widget("mobile.navbar",a.mobile.widget,{options:{iconpos:"top",grid:null,initSelector:":jqmData(role='navbar')"},
-_create:function(){var b=this.element,e=b.find("a"),f=e.filter(":jqmData(icon)").length?this.options.iconpos:c;b.addClass("ui-navbar").attr("role","navigation").find("ul").jqmEnhanceable().grid({grid:this.options.grid});f||b.addClass("ui-navbar-noicons");e.buttonMarkup({corners:false,shadow:false,inline:true,iconpos:f});b.delegate("a","vclick",function(b){a(b.target).hasClass("ui-disabled")||(e.removeClass(a.mobile.activeBtnClass),a(this).addClass(a.mobile.activeBtnClass))});b.closest(".ui-page").bind("pagebeforeshow",
-function(){e.filter(".ui-state-persist").addClass(a.mobile.activeBtnClass)})}});a(k).bind("pagecreate create",function(b){a.mobile.navbar.prototype.enhanceWithin(b.target)})})(jQuery);(function(a){var c={};a.widget("mobile.listview",a.mobile.widget,{options:{theme:null,countTheme:"c",headerTheme:"b",dividerTheme:"b",splitIcon:"arrow-r",splitTheme:"b",mini:false,inset:false,initSelector:":jqmData(role='listview')"},_create:function(){var a="";a+=this.options.inset?" ui-listview-inset ui-corner-all ui-shadow ":
-"";a+=this.element.jqmData("mini")||this.options.mini===true?" ui-mini":"";this.element.addClass(function(c,f){return f+" ui-listview "+a});this.refresh(true)},_removeCorners:function(a,c){a=a.add(a.find(".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb"));c==="top"?a.removeClass("ui-corner-top ui-corner-tr ui-corner-tl"):c==="bottom"?a.removeClass("ui-corner-bottom ui-corner-br ui-corner-bl"):a.removeClass("ui-corner-top ui-corner-tr ui-corner-tl ui-corner-bottom ui-corner-br ui-corner-bl")},_refreshCorners:function(a){var c,
-f;this.options.inset&&(c=this.element.children("li"),f=a?c.not(".ui-screen-hidden"):c.filter(":visible"),this._removeCorners(c),c=f.first().addClass("ui-corner-top"),c.add(c.find(".ui-btn-inner").not(".ui-li-link-alt span:first-child")).addClass("ui-corner-top").end().find(".ui-li-link-alt, .ui-li-link-alt span:first-child").addClass("ui-corner-tr").end().find(".ui-li-thumb").not(".ui-li-icon").addClass("ui-corner-tl"),f=f.last().addClass("ui-corner-bottom"),f.add(f.find(".ui-btn-inner")).find(".ui-li-link-alt").addClass("ui-corner-br").end().find(".ui-li-thumb").not(".ui-li-icon").addClass("ui-corner-bl"));
-a||this.element.trigger("updatelayout")},_findFirstElementByTagName:function(a,c,f,d){var g={};for(g[f]=g[d]=true;a;){if(g[a.nodeName])return a;a=a[c]}return null},_getChildrenByTagName:function(b,c,f){var d=[],g={};g[c]=g[f]=true;for(b=b.firstChild;b;)g[b.nodeName]&&d.push(b),b=b.nextSibling;return a(d)},_addThumbClasses:function(b){var c,f,d=b.length;for(c=0;c<d;c++)f=a(this._findFirstElementByTagName(b[c].firstChild,"nextSibling","img","IMG")),f.length&&(f.addClass("ui-li-thumb"),a(this._findFirstElementByTagName(f[0].parentNode,
-"parentNode","li","LI")).addClass(f.is(".ui-li-icon")?"ui-li-has-icon":"ui-li-has-thumb"))},refresh:function(b){this.parentPage=this.element.closest(".ui-page");this._createSubPages();var c=this.options,f=this.element,d=f.jqmData("dividertheme")||c.dividerTheme,g=f.jqmData("splittheme"),h=f.jqmData("spliticon"),j=this._getChildrenByTagName(f[0],"li","LI"),o=a.support.cssPseudoElement||!a.nodeName(f[0],"ol")?0:1,m={},p,l,r,n,q,t,x;o&&f.find(".ui-li-dec").remove();if(!c.theme)c.theme=a.mobile.getInheritedTheme(this.element,
-"c");for(var u=0,w=j.length;u<w;u++){p=j.eq(u);l="ui-li";if(b||!p.hasClass("ui-li"))r=p.jqmData("theme")||c.theme,n=this._getChildrenByTagName(p[0],"a","A"),n.length?(t=p.jqmData("icon"),p.buttonMarkup({wrapperEls:"div",shadow:false,corners:false,iconpos:"right",icon:n.length>1||t===false?false:t||"arrow-r",theme:r}),t!=false&&n.length==1&&p.addClass("ui-li-has-arrow"),n.first().removeClass("ui-link").addClass("ui-link-inherit"),n.length>1&&(l+=" ui-li-has-alt",n=n.last(),q=g||n.jqmData("theme")||
-c.splitTheme,x=n.jqmData("icon"),n.appendTo(p).attr("title",n.getEncodedText()).addClass("ui-li-link-alt").empty().buttonMarkup({shadow:false,corners:false,theme:r,icon:false,iconpos:false}).find(".ui-btn-inner").append(a(k.createElement("span")).buttonMarkup({shadow:true,corners:true,theme:q,iconpos:"notext",icon:x||t||h||c.splitIcon})))):p.jqmData("role")==="list-divider"?(l+=" ui-li-divider ui-bar-"+d,p.attr("role","heading"),o&&(o=1)):l+=" ui-li-static ui-body-"+r;o&&l.indexOf("ui-li-divider")<
-0&&(r=p.is(".ui-li-static:first")?p:p.find(".ui-link-inherit"),r.addClass("ui-li-jsnumbering").prepend("<span class='ui-li-dec'>"+o++ +". </span>"));m[l]||(m[l]=[]);m[l].push(p[0])}for(l in m)a(m[l]).addClass(l).children(".ui-btn-inner").addClass(l);f.find("h1, h2, h3, h4, h5, h6").addClass("ui-li-heading").end().find("p, dl").addClass("ui-li-desc").end().find(".ui-li-aside").each(function(){var b=a(this);b.prependTo(b.parent())}).end().find(".ui-li-count").each(function(){a(this).closest("li").addClass("ui-li-has-count")}).addClass("ui-btn-up-"+
-(f.jqmData("counttheme")||this.options.countTheme)+" ui-btn-corner-all");this._addThumbClasses(j);this._addThumbClasses(f.find(".ui-link-inherit"));this._refreshCorners(b)},_idStringEscape:function(a){return a.replace(/[^a-zA-Z0-9]/g,"-")},_createSubPages:function(){var b=this.element,e=b.closest(".ui-page"),f=e.jqmData("url"),d=f||e[0][a.expando],g=b.attr("id"),h=this.options,j="data-"+a.mobile.ns,k=this,m=e.find(":jqmData(role='footer')").jqmData("id"),p;typeof c[d]==="undefined"&&(c[d]=-1);g=g||
-++c[d];a(b.find("li>ul, li>ol").toArray().reverse()).each(function(c){var d=a(this),e=d.attr("id")||g+"-"+c,c=d.parent(),k=a(d.prevAll().toArray().reverse()),k=k.length?k:a("<span>"+a.trim(c.contents()[0].nodeValue)+"</span>"),o=k.first().getEncodedText(),e=(f||"")+"&"+a.mobile.subPageUrlKey+"="+e,x=d.jqmData("theme")||h.theme,u=d.jqmData("counttheme")||b.jqmData("counttheme")||h.countTheme;p=true;d.detach().wrap("<div "+j+"role='page' "+j+"url='"+e+"' "+j+"theme='"+x+"' "+j+"count-theme='"+u+"'><div "+
-j+"role='content'></div></div>").parent().before("<div "+j+"role='header' "+j+"theme='"+h.headerTheme+"'><div class='ui-title'>"+o+"</div></div>").after(m?a("<div "+j+"role='footer' "+j+"id='"+m+"'>"):"").parent().appendTo(a.mobile.pageContainer).page();d=c.find("a:first");d.length||(d=a("<a/>").html(k||o).prependTo(c.empty()));d.attr("href","#"+e)}).listview();p&&e.is(":jqmData(external-page='true')")&&e.data("page").options.domCache===false&&e.unbind("pagehide.remove").bind("pagehide.remove",function(b,
-c){var d=c.nextPage;c.nextPage&&(d=d.jqmData("url"),d.indexOf(f+"&"+a.mobile.subPageUrlKey)!==0&&(k.childPages().remove(),e.remove()))})},childPages:function(){var b=this.parentPage.jqmData("url");return a(":jqmData(url^='"+b+"&"+a.mobile.subPageUrlKey+"')")}});a(k).bind("pagecreate create",function(b){a.mobile.listview.prototype.enhanceWithin(b.target)})})(jQuery);(function(a,c){a.widget("mobile.checkboxradio",a.mobile.widget,{options:{theme:null,initSelector:"input[type='checkbox'],input[type='radio']"},
-_create:function(){var b=this,e=this.element,f=a(e).closest("label"),d=f.length?f:a(e).closest("form,fieldset,:jqmData(role='page'),:jqmData(role='dialog')").find("label").filter("[for='"+e[0].id+"']"),g=e[0].type,f=e.jqmData("mini")||e.closest("form,fieldset").jqmData("mini"),h=g+"-on",j=g+"-off",o=e.parents(":jqmData(type='horizontal')").length?c:j,m=e.jqmData("iconpos")||e.closest("form,fieldset").jqmData("iconpos");if(!(g!=="checkbox"&&g!=="radio")){a.extend(this,{label:d,inputtype:g,checkedClass:"ui-"+
-h+(o?"":" "+a.mobile.activeBtnClass),uncheckedClass:"ui-"+j,checkedicon:"ui-icon-"+h,uncheckedicon:"ui-icon-"+j});if(!this.options.theme)this.options.theme=a.mobile.getInheritedTheme(this.element,"c");d.buttonMarkup({theme:this.options.theme,icon:o,shadow:false,mini:f,iconpos:m});f=k.createElement("div");f.className="ui-"+g;e.add(d).wrapAll(f);d.bind({vmouseover:function(b){a(this).parent().is(".ui-disabled")&&b.stopPropagation()},vclick:function(a){if(e.is(":disabled"))a.preventDefault();else return b._cacheVals(),
-e.prop("checked",g==="radio"&&true||!e.prop("checked")),e.triggerHandler("click"),b._getInputSet().not(e).prop("checked",false),b._updateAll(),false}});e.bind({vmousedown:function(){b._cacheVals()},vclick:function(){var c=a(this);c.is(":checked")?(c.prop("checked",true),b._getInputSet().not(c).prop("checked",false)):c.prop("checked",false);b._updateAll()},focus:function(){d.addClass(a.mobile.focusClass)},blur:function(){d.removeClass(a.mobile.focusClass)}});this.refresh()}},_cacheVals:function(){this._getInputSet().each(function(){a(this).jqmData("cacheVal",
-this.checked)})},_getInputSet:function(){return this.inputtype==="checkbox"?this.element:this.element.closest("form,fieldset,:jqmData(role='page')").find("input[name='"+this.element[0].name+"'][type='"+this.inputtype+"']")},_updateAll:function(){var b=this;this._getInputSet().each(function(){var c=a(this);(this.checked||b.inputtype==="checkbox")&&c.trigger("change")}).checkboxradio("refresh")},refresh:function(){var a=this.element[0],c=this.label,f=c.find(".ui-icon");a.checked?(c.addClass(this.checkedClass).removeClass(this.uncheckedClass),
-f.addClass(this.checkedicon).removeClass(this.uncheckedicon)):(c.removeClass(this.checkedClass).addClass(this.uncheckedClass),f.removeClass(this.checkedicon).addClass(this.uncheckedicon));a.disabled?this.disable():this.enable()},disable:function(){this.element.prop("disabled",true).parent().addClass("ui-disabled")},enable:function(){this.element.prop("disabled",false).parent().removeClass("ui-disabled")}});a(k).bind("pagecreate create",function(b){a.mobile.checkboxradio.prototype.enhanceWithin(b.target,
-true)})})(jQuery);(function(a,c){a.widget("mobile.button",a.mobile.widget,{options:{theme:null,icon:null,iconpos:null,inline:false,corners:true,shadow:true,iconshadow:true,initSelector:"button, [type='button'], [type='submit'], [type='reset'], [type='image']",mini:false},_create:function(){var b=this.element,e,f=this.options,d;d="";var g;if(b[0].tagName==="A")!b.hasClass("ui-btn")&&b.buttonMarkup();else{if(!this.options.theme)this.options.theme=a.mobile.getInheritedTheme(this.element,"c");~b[0].className.indexOf("ui-btn-left")&&
-(d="ui-btn-left");~b[0].className.indexOf("ui-btn-right")&&(d="ui-btn-right");e=this.button=a("<div></div>").text(b.text()||b.val()).insertBefore(b).buttonMarkup({theme:f.theme,icon:f.icon,iconpos:f.iconpos,inline:f.inline,corners:f.corners,shadow:f.shadow,iconshadow:f.iconshadow,mini:f.mini}).addClass(d).append(b.addClass("ui-btn-hidden"));f=b.attr("type");d=b.attr("name");f!=="button"&&f!=="reset"&&d&&b.bind("vclick",function(){g===c&&(g=a("<input>",{type:"hidden",name:b.attr("name"),value:b.attr("value")}).insertBefore(b),
-a(k).one("submit",function(){g.remove();g=c}))});b.bind({focus:function(){e.addClass(a.mobile.focusClass)},blur:function(){e.removeClass(a.mobile.focusClass)}});this.refresh()}},enable:function(){this.element.attr("disabled",false);this.button.removeClass("ui-disabled").attr("aria-disabled",false);return this._setOption("disabled",false)},disable:function(){this.element.attr("disabled",true);this.button.addClass("ui-disabled").attr("aria-disabled",true);return this._setOption("disabled",true)},refresh:function(){var b=
-this.element;b.prop("disabled")?this.disable():this.enable();a(this.button.data("buttonElements").text).text(b.text()||b.val())}});a(k).bind("pagecreate create",function(b){a.mobile.button.prototype.enhanceWithin(b.target,true)})})(jQuery);(function(a){a.fn.controlgroup=function(c){function b(a,b){a.removeClass("ui-btn-corner-all ui-shadow").eq(0).addClass(b[0]).end().last().addClass(b[1]).addClass("ui-controlgroup-last")}return this.each(function(){var e=a(this),f=a.extend({direction:e.jqmData("type")||
-"vertical",shadow:false,excludeInvisible:true,mini:e.jqmData("mini")},c),d=e.children("legend"),g=f.direction=="horizontal"?["ui-corner-left","ui-corner-right"]:["ui-corner-top","ui-corner-bottom"];e.find("input").first().attr("type");d.length&&(e.wrapInner("<div class='ui-controlgroup-controls'></div>"),a("<div role='heading' class='ui-controlgroup-label'>"+d.html()+"</div>").insertBefore(e.children(0)),d.remove());e.addClass("ui-corner-all ui-controlgroup ui-controlgroup-"+f.direction);b(e.find(".ui-btn"+
-(f.excludeInvisible?":visible":"")).not(".ui-slider-handle"),g);b(e.find(".ui-btn-inner"),g);f.shadow&&e.addClass("ui-shadow");f.mini&&e.addClass("ui-mini")})}})(jQuery);(function(a){a(k).bind("pagecreate create",function(c){a(c.target).find("a").jqmEnhanceable().not(".ui-btn, .ui-link-inherit, :jqmData(role='none'), :jqmData(role='nojs')").addClass("ui-link")})})(jQuery);(function(a){var c=a("meta[name=viewport]"),b=c.attr("content"),e=b+",maximum-scale=1, user-scalable=no",f=b+",maximum-scale=10, user-scalable=yes",
-d=/(user-scalable[\s]*=[\s]*no)|(maximum-scale[\s]*=[\s]*1)[$,\s]/.test(b);a.mobile.zoom=a.extend({},{enabled:!d,locked:false,disable:function(b){if(!d&&!a.mobile.zoom.locked)c.attr("content",e),a.mobile.zoom.enabled=false,a.mobile.zoom.locked=b||false},enable:function(b){if(!d&&(!a.mobile.zoom.locked||b===true))c.attr("content",f),a.mobile.zoom.enabled=true,a.mobile.zoom.locked=false},restore:function(){if(!d)c.attr("content",b),a.mobile.zoom.enabled=true}})})(jQuery);(function(a){a.widget("mobile.textinput",
-a.mobile.widget,{options:{theme:null,preventFocusZoom:/iPhone|iPad|iPod/.test(navigator.platform)&&navigator.userAgent.indexOf("AppleWebKit")>-1,initSelector:"input[type='text'], input[type='search'], :jqmData(type='search'), input[type='number'], :jqmData(type='number'), input[type='password'], input[type='email'], input[type='url'], input[type='tel'], textarea, input[type='time'], input[type='date'], input[type='month'], input[type='week'], input[type='datetime'], input[type='datetime-local'], input[type='color'], input:not([type])",
-clearSearchButtonText:"clear text"},_create:function(){var c=this.element,b=this.options,e=b.theme||a.mobile.getInheritedTheme(this.element,"c"),f=" ui-body-"+e,d=c.jqmData("mini")==true,g=d?" ui-mini":"",h,j;a("label[for='"+c.attr("id")+"']").addClass("ui-input-text");h=c.addClass("ui-input-text ui-body-"+e);typeof c[0].autocorrect!=="undefined"&&!a.support.touchOverflow&&(c[0].setAttribute("autocorrect","off"),c[0].setAttribute("autocomplete","off"));c.is("[type='search'],:jqmData(type='search')")?
-(h=c.wrap("<div class='ui-input-search ui-shadow-inset ui-btn-corner-all ui-btn-shadow ui-icon-searchfield"+f+g+"'></div>").parent(),j=a("<a href='#' class='ui-input-clear' title='"+b.clearSearchButtonText+"'>"+b.clearSearchButtonText+"</a>").bind("click",function(a){c.val("").focus().trigger("change");j.addClass("ui-input-clear-hidden");a.preventDefault()}).appendTo(h).buttonMarkup({icon:"delete",iconpos:"notext",corners:true,shadow:true,mini:d}),e=function(){setTimeout(function(){j.toggleClass("ui-input-clear-hidden",
-!c.val())},0)},e(),c.bind("paste cut keyup focus change blur",e)):c.addClass("ui-corner-all ui-shadow-inset"+f+g);c.focus(function(){h.addClass(a.mobile.focusClass)}).blur(function(){h.removeClass(a.mobile.focusClass)}).bind("focus",function(){b.preventFocusZoom&&a.mobile.zoom.disable(true)}).bind("blur",function(){b.preventFocusZoom&&a.mobile.zoom.enable(true)});if(c.is("textarea")){var o=function(){var a=c[0].scrollHeight;c[0].clientHeight<a&&c.height(a+15)},m;c.keyup(function(){clearTimeout(m);
-m=setTimeout(o,100)});a(k).one("pagechange",o);a.trim(c.val())&&a(s).load(o)}},disable:function(){(this.element.attr("disabled",true).is("[type='search'],:jqmData(type='search')")?this.element.parent():this.element).addClass("ui-disabled")},enable:function(){(this.element.attr("disabled",false).is("[type='search'],:jqmData(type='search')")?this.element.parent():this.element).removeClass("ui-disabled")}});a(k).bind("pagecreate create",function(c){a.mobile.textinput.prototype.enhanceWithin(c.target,
-true)})})(jQuery);(function(a){a.mobile.listview.prototype.options.filter=false;a.mobile.listview.prototype.options.filterPlaceholder="Filter items...";a.mobile.listview.prototype.options.filterTheme="c";a.mobile.listview.prototype.options.filterCallback=function(a,b){return a.toLowerCase().indexOf(b)===-1};a(k).delegate(":jqmData(role='listview')","listviewcreate",function(){var c=a(this),b=c.data("listview");if(b.options.filter){var e=a("<form>",{"class":"ui-listview-filter ui-bar-"+b.options.filterTheme,
-role:"search"});a("<input>",{placeholder:b.options.filterPlaceholder}).attr("data-"+a.mobile.ns+"type","search").jqmData("lastval","").bind("keyup change",function(){var e=a(this),d=this.value.toLowerCase(),g=null,g=e.jqmData("lastval")+"",h=false,j="";e.jqmData("lastval",d);g=d.length<g.length||d.indexOf(g)!==0?c.children():c.children(":not(.ui-screen-hidden)");if(d){for(var k=g.length-1;k>=0;k--)e=a(g[k]),j=e.jqmData("filtertext")||e.text(),e.is("li:jqmData(role=list-divider)")?(e.toggleClass("ui-filter-hidequeue",
-!h),h=false):b.options.filterCallback(j,d)?e.toggleClass("ui-filter-hidequeue",true):h=true;g.filter(":not(.ui-filter-hidequeue)").toggleClass("ui-screen-hidden",false);g.filter(".ui-filter-hidequeue").toggleClass("ui-screen-hidden",true).toggleClass("ui-filter-hidequeue",false)}else g.toggleClass("ui-screen-hidden",false);b._refreshCorners()}).appendTo(e).textinput();b.options.inset&&e.addClass("ui-listview-filter-inset");e.bind("submit",function(){return false}).insertBefore(c)}})})(jQuery);(function(a,
-c){a.widget("mobile.slider",a.mobile.widget,{options:{theme:null,trackTheme:null,disabled:false,initSelector:"input[type='range'], :jqmData(type='range'), :jqmData(role='slider')",mini:false},_create:function(){var b=this,e=this.element,f=a.mobile.getInheritedTheme(e,"c"),d=this.options.theme||f,f=this.options.trackTheme||f,g=e[0].nodeName.toLowerCase(),h=g=="select"?"ui-slider-switch":"",j=e.attr("id"),o=j+"-label",j=a("[for='"+j+"']").attr("id",o),m=function(){return g=="input"?parseFloat(e.val()):
-e[0].selectedIndex},p=g=="input"?parseFloat(e.attr("min")):0,l=g=="input"?parseFloat(e.attr("max")):e.find("option").length-1,r=s.parseFloat(e.attr("step")||1),n=this.options.inline||e.jqmData("inline")==true?" ui-slider-inline":"",q=this.options.mini||e.jqmData("mini")?" ui-slider-mini":"",t=k.createElement("a"),x=a(t),u=k.createElement("div"),w=a(u),v=e.jqmData("highlight")&&g!="select"?function(){var b=k.createElement("div");b.className="ui-slider-bg ui-btn-active ui-btn-corner-all";return a(b).prependTo(w)}():
-false;t.setAttribute("href","#");u.setAttribute("role","application");u.className=["ui-slider ",h," ui-btn-down-",f," ui-btn-corner-all",n,q].join("");t.className="ui-slider-handle";u.appendChild(t);x.buttonMarkup({corners:true,theme:d,shadow:true}).attr({role:"slider","aria-valuemin":p,"aria-valuemax":l,"aria-valuenow":m(),"aria-valuetext":m(),title:m(),"aria-labelledby":o});a.extend(this,{slider:w,handle:x,valuebg:v,dragging:false,beforeStart:null,userModified:false,mouseMoved:false});if(g=="select"){d=
-k.createElement("div");d.className="ui-slider-inneroffset";h=0;for(o=u.childNodes.length;h<o;h++)d.appendChild(u.childNodes[h]);u.appendChild(d);x.addClass("ui-slider-handle-snapping");u=e.find("option");d=0;for(h=u.length;d<h;d++)o=!d?"b":"a",n=!d?" ui-btn-down-"+f:" "+a.mobile.activeBtnClass,k.createElement("div"),q=k.createElement("span"),q.className=["ui-slider-label ui-slider-label-",o,n," ui-btn-corner-all"].join(""),q.setAttribute("role","img"),q.appendChild(k.createTextNode(u[d].innerHTML)),
-a(q).prependTo(w);b._labels=a(".ui-slider-label",w)}j.addClass("ui-slider");e.addClass(g==="input"?"ui-slider-input":"ui-slider-switch").change(function(){b.mouseMoved||b.refresh(m(),true)}).keyup(function(){b.refresh(m(),true,true)}).blur(function(){b.refresh(m(),true)});a(k).bind("vmousemove",function(a){if(b.dragging)return b.mouseMoved=true,g==="select"&&x.removeClass("ui-slider-handle-snapping"),b.refresh(a),b.userModified=b.beforeStart!==e[0].selectedIndex,false});w.bind("vmousedown",function(a){b.dragging=
-true;b.userModified=false;b.mouseMoved=false;if(g==="select")b.beforeStart=e[0].selectedIndex;b.refresh(a);return false}).bind("vclick",false);w.add(k).bind("vmouseup",function(){if(b.dragging)return b.dragging=false,g==="select"&&(x.addClass("ui-slider-handle-snapping"),b.mouseMoved?b.userModified?b.refresh(b.beforeStart==0?1:0):b.refresh(b.beforeStart):b.refresh(b.beforeStart==0?1:0)),b.mouseMoved=false});w.insertAfter(e);g=="select"&&this.handle.bind({focus:function(){w.addClass(a.mobile.focusClass)},
-blur:function(){w.removeClass(a.mobile.focusClass)}});this.handle.bind({vmousedown:function(){a(this).focus()},vclick:false,keydown:function(c){var d=m();if(!b.options.disabled){switch(c.keyCode){case a.mobile.keyCode.HOME:case a.mobile.keyCode.END:case a.mobile.keyCode.PAGE_UP:case a.mobile.keyCode.PAGE_DOWN:case a.mobile.keyCode.UP:case a.mobile.keyCode.RIGHT:case a.mobile.keyCode.DOWN:case a.mobile.keyCode.LEFT:if(c.preventDefault(),!b._keySliding)b._keySliding=true,a(this).addClass("ui-state-active")}switch(c.keyCode){case a.mobile.keyCode.HOME:b.refresh(p);
-break;case a.mobile.keyCode.END:b.refresh(l);break;case a.mobile.keyCode.PAGE_UP:case a.mobile.keyCode.UP:case a.mobile.keyCode.RIGHT:b.refresh(d+r);break;case a.mobile.keyCode.PAGE_DOWN:case a.mobile.keyCode.DOWN:case a.mobile.keyCode.LEFT:b.refresh(d-r)}}},keyup:function(){if(b._keySliding)b._keySliding=false,a(this).removeClass("ui-state-active")}});this.refresh(c,c,true)},refresh:function(b,c,f){(this.options.disabled||this.element.attr("disabled"))&&this.disable();var d=this.element,g=d[0].nodeName.toLowerCase(),
-h=g==="input"?parseFloat(d.attr("min")):0,j=g==="input"?parseFloat(d.attr("max")):d.find("option").length-1,k=g==="input"&&parseFloat(d.attr("step"))>0?parseFloat(d.attr("step")):1;if(typeof b==="object"){if(!this.dragging||b.pageX<this.slider.offset().left-8||b.pageX>this.slider.offset().left+this.slider.width()+8)return;b=Math.round((b.pageX-this.slider.offset().left)/this.slider.width()*100)}else b==null&&(b=g==="input"?parseFloat(d.val()||0):d[0].selectedIndex),b=(parseFloat(b)-h)/(j-h)*100;if(!isNaN(b)){b<
-0&&(b=0);b>100&&(b=100);var m=b/100*(j-h)+h,p=(m-h)%k;m-=p;Math.abs(p)*2>=k&&(m+=p>0?k:-k);m=parseFloat(m.toFixed(5));m<h&&(m=h);m>j&&(m=j);this.handle.css("left",b+"%");this.handle.attr({"aria-valuenow":g==="input"?m:d.find("option").eq(m).attr("value"),"aria-valuetext":g==="input"?m:d.find("option").eq(m).getEncodedText(),title:g==="input"?m:d.find("option").eq(m).getEncodedText()});this.valuebg&&this.valuebg.css("width",b+"%");if(this._labels){var h=this.handle.width()/this.slider.width()*100,
-l=b&&h+(100-h)*b/100,r=b===100?0:Math.min(h+100-l,100);this._labels.each(function(){var b=a(this).is(".ui-slider-label-a");a(this).width((b?l:r)+"%")})}if(!f)f=false,g==="input"?(f=d.val()!==m,d.val(m)):(f=d[0].selectedIndex!==m,d[0].selectedIndex=m),!c&&f&&d.trigger("change")}},enable:function(){this.element.attr("disabled",false);this.slider.removeClass("ui-disabled").attr("aria-disabled",false);return this._setOption("disabled",false)},disable:function(){this.element.attr("disabled",true);this.slider.addClass("ui-disabled").attr("aria-disabled",
-true);return this._setOption("disabled",true)}});a(k).bind("pagecreate create",function(b){a.mobile.slider.prototype.enhanceWithin(b.target,true)})})(jQuery);(function(a){a.widget("mobile.selectmenu",a.mobile.widget,{options:{theme:null,disabled:false,icon:"arrow-d",iconpos:"right",inline:false,corners:true,shadow:true,iconshadow:true,overlayTheme:"a",hidePlaceholderMenuItems:true,closeText:"Close",nativeMenu:true,preventFocusZoom:/iPhone|iPad|iPod/.test(navigator.platform)&&navigator.userAgent.indexOf("AppleWebKit")>
--1,initSelector:"select:not(:jqmData(role='slider'))",mini:false},_button:function(){return a("<div/>")},_setDisabled:function(a){this.element.attr("disabled",a);this.button.attr("aria-disabled",a);return this._setOption("disabled",a)},_focusButton:function(){var a=this;setTimeout(function(){a.button.focus()},40)},_selectOptions:function(){return this.select.find("option")},_preExtension:function(){var c="";~this.element[0].className.indexOf("ui-btn-left")&&(c=" ui-btn-left");~this.element[0].className.indexOf("ui-btn-right")&&
-(c=" ui-btn-right");this.select=this.element.wrap("<div class='ui-select"+c+"'>");this.selectID=this.select.attr("id");this.label=a("label[for='"+this.selectID+"']").addClass("ui-select");this.isMultiple=this.select[0].multiple;if(!this.options.theme)this.options.theme=a.mobile.getInheritedTheme(this.select,"c")},_create:function(){this._preExtension();this._trigger("beforeCreate");this.button=this._button();var c=this,b=this.options,e=this.button.text(a(this.select[0].options.item(this.select[0].selectedIndex==
--1?0:this.select[0].selectedIndex)).text()).insertBefore(this.select).buttonMarkup({theme:b.theme,icon:b.icon,iconpos:b.iconpos,inline:b.inline,corners:b.corners,shadow:b.shadow,iconshadow:b.iconshadow,mini:b.mini});b.nativeMenu&&s.opera&&s.opera.version&&this.select.addClass("ui-select-nativeonly");if(this.isMultiple)this.buttonCount=a("<span>").addClass("ui-li-count ui-btn-up-c ui-btn-corner-all").hide().appendTo(e.addClass("ui-li-has-count"));(b.disabled||this.element.attr("disabled"))&&this.disable();
-this.select.change(function(){c.refresh()});this.build()},build:function(){var c=this;this.select.appendTo(c.button).bind("vmousedown",function(){c.button.addClass(a.mobile.activeBtnClass)}).bind("focus",function(){c.button.addClass(a.mobile.focusClass)}).bind("blur",function(){c.button.removeClass(a.mobile.focusClass)}).bind("focus vmouseover",function(){c.button.trigger("vmouseover")}).bind("vmousemove",function(){c.button.removeClass(a.mobile.activeBtnClass)}).bind("change blur vmouseout",function(){c.button.trigger("vmouseout").removeClass(a.mobile.activeBtnClass)}).bind("change blur",
-function(){c.button.removeClass("ui-btn-down-"+c.options.theme)});c.button.bind("vmousedown",function(){c.options.preventFocusZoom&&a.mobile.zoom.disable(true)}).bind("mouseup",function(){c.options.preventFocusZoom&&a.mobile.zoom.enable(true)})},selected:function(){return this._selectOptions().filter(":selected")},selectedIndices:function(){var a=this;return this.selected().map(function(){return a._selectOptions().index(this)}).get()},setButtonText:function(){var c=this,b=this.selected();this.button.find(".ui-btn-text").text(function(){return!c.isMultiple?
-b.text():b.length?b.map(function(){return a(this).text()}).get().join(", "):c.placeholder})},setButtonCount:function(){var a=this.selected();this.isMultiple&&this.buttonCount[a.length>1?"show":"hide"]().text(a.length)},refresh:function(){this.setButtonText();this.setButtonCount()},open:a.noop,close:a.noop,disable:function(){this._setDisabled(true);this.button.addClass("ui-disabled")},enable:function(){this._setDisabled(false);this.button.removeClass("ui-disabled")}});a(k).bind("pagecreate create",
-function(c){a.mobile.selectmenu.prototype.enhanceWithin(c.target,true)})})(jQuery);(function(a){var c=function(b){var c=b.selectID,f=b.label,d=b.select.closest(".ui-page"),g=a("<div>",{"class":"ui-selectmenu-screen ui-screen-hidden"}).appendTo(d),h=b._selectOptions(),j=b.isMultiple=b.select[0].multiple,o=c+"-button",m=c+"-menu",p=a("<div data-"+a.mobile.ns+"role='dialog' data-"+a.mobile.ns+"theme='"+b.options.theme+"' data-"+a.mobile.ns+"overlay-theme='"+b.options.overlayTheme+"'><div data-"+a.mobile.ns+
-"role='header'><div class='ui-title'>"+f.getEncodedText()+"</div></div><div data-"+a.mobile.ns+"role='content'></div></div>"),l=a("<div>",{"class":"ui-selectmenu ui-selectmenu-hidden ui-overlay-shadow ui-corner-all ui-body-"+b.options.overlayTheme+" "+a.mobile.defaultDialogTransition}).insertAfter(g),r=a("<ul>",{"class":"ui-selectmenu-list",id:m,role:"listbox","aria-labelledby":o}).attr("data-"+a.mobile.ns+"theme",b.options.theme).appendTo(l),n=a("<div>",{"class":"ui-header ui-bar-"+b.options.theme}).prependTo(l),
-q=a("<h1>",{"class":"ui-title"}).appendTo(n),t;b.isMultiple&&(t=a("<a>",{text:b.options.closeText,href:"#","class":"ui-btn-left"}).attr("data-"+a.mobile.ns+"iconpos","notext").attr("data-"+a.mobile.ns+"icon","delete").appendTo(n).buttonMarkup());a.extend(b,{select:b.select,selectID:c,buttonId:o,menuId:m,thisPage:d,menuPage:p,label:f,screen:g,selectOptions:h,isMultiple:j,theme:b.options.theme,listbox:l,list:r,header:n,headerTitle:q,headerClose:t,menuPageContent:void 0,menuPageClose:void 0,placeholder:"",
-build:function(){var c=this;c.refresh();c.select.attr("tabindex","-1").focus(function(){a(this).blur();c.button.focus()});c.button.bind("vclick keydown",function(b){if(b.type=="vclick"||b.keyCode&&(b.keyCode===a.mobile.keyCode.ENTER||b.keyCode===a.mobile.keyCode.SPACE))c.open(),b.preventDefault()});c.list.attr("role","listbox").bind("focusin",function(b){a(b.target).attr("tabindex","0").trigger("vmouseover")}).bind("focusout",function(b){a(b.target).attr("tabindex","-1").trigger("vmouseout")}).delegate("li:not(.ui-disabled, .ui-li-divider)",
-"click",function(b){var d=c.select[0].selectedIndex,e=c.list.find("li:not(.ui-li-divider)").index(this),f=c._selectOptions().eq(e)[0];f.selected=c.isMultiple?!f.selected:true;c.isMultiple&&a(this).find(".ui-icon").toggleClass("ui-icon-checkbox-on",f.selected).toggleClass("ui-icon-checkbox-off",!f.selected);(c.isMultiple||d!==e)&&c.select.trigger("change");c.isMultiple||c.close();b.preventDefault()}).keydown(function(c){var d=a(c.target),e=d.closest("li");switch(c.keyCode){case 38:return c=e.prev().not(".ui-selectmenu-placeholder"),
-c.is(".ui-li-divider")&&(c=c.prev()),c.length&&(d.blur().attr("tabindex","-1"),c.addClass("ui-btn-down-"+b.options.theme).find("a").first().focus()),false;case 40:return c=e.next(),c.is(".ui-li-divider")&&(c=c.next()),c.length&&(d.blur().attr("tabindex","-1"),c.addClass("ui-btn-down-"+b.options.theme).find("a").first().focus()),false;case 13:case 32:return d.trigger("click"),false}});c.menuPage.bind("pagehide",function(){c.list.appendTo(c.listbox);c._focusButton();a.mobile._bindPageRemove.call(c.thisPage)});
-c.screen.bind("vclick",function(){c.close()});c.isMultiple&&c.headerClose.click(function(){if(c.menuType=="overlay")return c.close(),false});c.thisPage.addDependents(this.menuPage)},_isRebuildRequired:function(){var a=this.list.find("li");return this._selectOptions().text()!==a.text()},refresh:function(b){var c=this;this._selectOptions();this.selected();var d=this.selectedIndices();(b||this._isRebuildRequired())&&c._buildList();c.setButtonText();c.setButtonCount();c.list.find("li:not(.ui-li-divider)").removeClass(a.mobile.activeBtnClass).attr("aria-selected",
-false).each(function(b){a.inArray(b,d)>-1&&(b=a(this),b.attr("aria-selected",true),c.isMultiple?b.find(".ui-icon").removeClass("ui-icon-checkbox-off").addClass("ui-icon-checkbox-on"):b.is(".ui-selectmenu-placeholder")?b.next().addClass(a.mobile.activeBtnClass):b.addClass(a.mobile.activeBtnClass))})},close:function(){if(!this.options.disabled&&this.isOpen)this.menuType=="page"?s.history.back():(this.screen.addClass("ui-screen-hidden"),this.listbox.addClass("ui-selectmenu-hidden").removeAttr("style").removeClass("in"),
-this.list.appendTo(this.listbox),this._focusButton()),this.isOpen=false},open:function(){function b(){c.list.find("."+a.mobile.activeBtnClass+" a").focus()}if(!this.options.disabled){var c=this,d=a(s),e=c.list.parent(),f=e.outerHeight(),e=e.outerWidth();a(".ui-page-active");var g=d.scrollTop(),j=c.button.offset().top,h=d.height(),d=d.width();c.button.addClass(a.mobile.activeBtnClass);setTimeout(function(){c.button.removeClass(a.mobile.activeBtnClass)},300);if(f>h-80||!a.support.scrollTop){c.menuPage.appendTo(a.mobile.pageContainer).page();
-c.menuPageContent=p.find(".ui-content");c.menuPageClose=p.find(".ui-header a");c.thisPage.unbind("pagehide.remove");if(g==0&&j>h)c.thisPage.one("pagehide",function(){a(this).jqmData("lastScroll",j)});c.menuPage.one("pageshow",function(){b();c.isOpen=true});c.menuType="page";c.menuPageContent.append(c.list);c.menuPage.find("div .ui-title").text(c.label.text());a.mobile.changePage(c.menuPage,{transition:a.mobile.defaultDialogTransition})}else{c.menuType="overlay";c.screen.height(a(k).height()).removeClass("ui-screen-hidden");
-var l=j-g,m=g+h-j,n=f/2,o=parseFloat(c.list.parent().css("max-width")),f=l>f/2&&m>f/2?j+c.button.outerHeight()/2-n:l>m?g+h-f-30:g+30;e<o?g=(d-e)/2:(g=c.button.offset().left+c.button.outerWidth()/2-e/2,g<30?g=30:g+e>d&&(g=d-e-30));c.listbox.append(c.list).removeClass("ui-selectmenu-hidden").css({top:f,left:g}).addClass("in");b();c.isOpen=true}}},_buildList:function(){var b=this.options,c=this.placeholder,d=true,e=this.isMultiple?"checkbox-off":"false";this.list.empty().filter(".ui-listview").listview("destroy");
-var f=this.select.find("option"),g=f.length,j=this.select[0],h="data-"+a.mobile.ns,l=h+"option-index",m=h+"icon";h+="role";for(var n=k.createDocumentFragment(),o,p=0;p<g;p++){var r=f[p],q=a(r),s=r.parentNode,t=q.text(),D=k.createElement("a"),J=[];D.setAttribute("href","#");D.appendChild(k.createTextNode(t));s!==j&&s.nodeName.toLowerCase()==="optgroup"&&(s=s.getAttribute("label"),s!=o&&(o=k.createElement("li"),o.setAttribute(h,"list-divider"),o.setAttribute("role","option"),o.setAttribute("tabindex",
-"-1"),o.appendChild(k.createTextNode(s)),n.appendChild(o),o=s));if(d&&(!r.getAttribute("value")||t.length==0||q.jqmData("placeholder")))if(d=false,b.hidePlaceholderMenuItems&&J.push("ui-selectmenu-placeholder"),!c)c=this.placeholder=t;q=k.createElement("li");r.disabled&&(J.push("ui-disabled"),q.setAttribute("aria-disabled",true));q.setAttribute(l,p);q.setAttribute(m,e);q.className=J.join(" ");q.setAttribute("role","option");D.setAttribute("tabindex","-1");q.appendChild(D);n.appendChild(q)}this.list[0].appendChild(n);
-!this.isMultiple&&!c.length?this.header.hide():this.headerTitle.text(this.placeholder);this.list.listview()},_button:function(){return a("<a>",{href:"#",role:"button",id:this.buttonId,"aria-haspopup":"true","aria-owns":this.menuId})}})};a(k).bind("selectmenubeforecreate",function(b){b=a(b.target).data("selectmenu");b.options.nativeMenu||c(b)})})(jQuery);(function(a){a.widget("mobile.fixedtoolbar",a.mobile.widget,{options:{visibleOnPageShow:true,disablePageZoom:true,transition:"slide",fullscreen:false,
-tapToggle:true,tapToggleBlacklist:"a, input, select, textarea, .ui-header-fixed, .ui-footer-fixed",hideDuringFocus:"input, textarea, select",updatePagePadding:true,trackPersistentToolbars:true,supportBlacklist:function(){var a=s,b=navigator.userAgent,e=navigator.platform,f=b.match(/AppleWebKit\/([0-9]+)/),f=!!f&&f[1],d=b.match(/Fennec\/([0-9]+)/),d=!!d&&d[1],g=b.match(/Opera Mobi\/([0-9]+)/),h=!!g&&g[1];return(e.indexOf("iPhone")>-1||e.indexOf("iPad")>-1||e.indexOf("iPod")>-1)&&f&&f<534||a.operamini&&
-{}.toString.call(a.operamini)==="[object OperaMini]"||g&&h<7458||b.indexOf("Android")>-1&&f&&f<533||d&&d<6||"palmGetResource"in s&&f&&f<534||b.indexOf("MeeGo")>-1&&b.indexOf("NokiaBrowser/8.5.0")>-1?true:false},initSelector:":jqmData(position='fixed')"},_create:function(){var a=this.options,b=this.element,e=b.is(":jqmData(role='header')")?"header":"footer",f=b.closest(".ui-page");a.supportBlacklist()?this.destroy():(b.addClass("ui-"+e+"-fixed"),a.fullscreen?(b.addClass("ui-"+e+"-fullscreen"),f.addClass("ui-page-"+
-e+"-fullscreen")):f.addClass("ui-page-"+e+"-fixed"),this._addTransitionClass(),this._bindPageEvents(),this._bindToggleHandlers())},_addTransitionClass:function(){var a=this.options.transition;a&&a!=="none"&&(a==="slide"&&(a=this.element.is(".ui-header")?"slidedown":"slideup"),this.element.addClass(a))},_bindPageEvents:function(){var c=this,b=c.options;c.element.closest(".ui-page").bind("pagebeforeshow",function(){b.disablePageZoom&&a.mobile.zoom.disable(true);b.visibleOnPageShow||c.hide(true)}).bind("webkitAnimationStart animationstart updatelayout",
-function(){b.updatePagePadding&&c.updatePagePadding()}).bind("pageshow",function(){c.updatePagePadding();b.updatePagePadding&&a(s).bind("throttledresize."+c.widgetName,function(){c.updatePagePadding()})}).bind("pagebeforehide",function(e,f){b.disablePageZoom&&a.mobile.zoom.enable(true);b.updatePagePadding&&a(s).unbind("throttledresize."+c.widgetName);if(b.trackPersistentToolbars){var d=a(".ui-footer-fixed:jqmData(id)",this),g=a(".ui-header-fixed:jqmData(id)",this),h=d.length&&f.nextPage&&a(".ui-footer-fixed:jqmData(id='"+
-d.jqmData("id")+"')",f.nextPage),j=g.length&&f.nextPage&&a(".ui-header-fixed:jqmData(id='"+g.jqmData("id")+"')",f.nextPage),h=h||a();if(h.length||j.length)h.add(j).appendTo(a.mobile.pageContainer),f.nextPage.one("pageshow",function(){h.add(j).appendTo(this)})}})},_visible:true,updatePagePadding:function(){var a=this.element,b=a.is(".ui-header");this.options.fullscreen||a.closest(".ui-page").css("padding-"+(b?"top":"bottom"),a.outerHeight())},_useTransition:function(c){var b=this.element,e=a(s).scrollTop(),
-f=b.height(),d=b.closest(".ui-page").height(),g=a.mobile.getScreenHeight(),b=b.is(":jqmData(role='header')")?"header":"footer";return!c&&(this.options.transition&&this.options.transition!=="none"&&(b==="header"&&!this.options.fullscreen&&e>f||b==="footer"&&!this.options.fullscreen&&e+g<d-f)||this.options.fullscreen)},show:function(a){var b=this.element;this._useTransition(a)?b.removeClass("out ui-fixed-hidden").addClass("in"):b.removeClass("ui-fixed-hidden");this._visible=true},hide:function(a){var b=
-this.element,e="out"+(this.options.transition==="slide"?" reverse":"");this._useTransition(a)?b.addClass(e).removeClass("in").animationComplete(function(){b.addClass("ui-fixed-hidden").removeClass(e)}):b.addClass("ui-fixed-hidden").removeClass(e);this._visible=false},toggle:function(){this[this._visible?"hide":"show"]()},_bindToggleHandlers:function(){var c=this,b=c.options;c.element.closest(".ui-page").bind("vclick",function(e){b.tapToggle&&!a(e.target).closest(b.tapToggleBlacklist).length&&c.toggle()}).bind("focusin focusout",
-function(e){if(screen.width<500&&a(e.target).is(b.hideDuringFocus)&&!a(e.target).closest(".ui-header-fixed, .ui-footer-fixed").length)c[e.type==="focusin"&&c._visible?"hide":"show"]()})},destroy:function(){this.element.removeClass("ui-header-fixed ui-footer-fixed ui-header-fullscreen ui-footer-fullscreen in out fade slidedown slideup ui-fixed-hidden");this.element.closest(".ui-page").removeClass("ui-page-header-fixed ui-page-footer-fixed ui-page-header-fullscreen ui-page-footer-fullscreen")}});a(k).bind("pagecreate create",
-function(c){a(c.target).jqmData("fullscreen")&&a(a.mobile.fixedtoolbar.prototype.options.initSelector,c.target).not(":jqmData(fullscreen)").jqmData("fullscreen",true);a.mobile.fixedtoolbar.prototype.enhanceWithin(c.target)})})(jQuery);(function(a,c){if(/iPhone|iPad|iPod/.test(navigator.platform)&&navigator.userAgent.indexOf("AppleWebKit")>-1){var b=a.mobile.zoom,e,f,d,g,h;a(c).bind("orientationchange.iosorientationfix",b.enable).bind("devicemotion.iosorientationfix",function(a){e=a.originalEvent;
-h=e.accelerationIncludingGravity;f=Math.abs(h.x);d=Math.abs(h.y);g=Math.abs(h.z);!c.orientation&&(f>7||(g>6&&d<8||g<8&&d>6)&&f>5)?b.enabled&&b.disable():b.enabled||b.enable()})}})(jQuery,this);(function(a,c){function b(){var b=a("."+a.mobile.activeBtnClass).first();h.css({top:a.support.scrollTop&&g.scrollTop()+g.height()/2||b.length&&b.offset().top||100})}function e(){var c=h.offset(),d=g.scrollTop(),f=a.mobile.getScreenHeight();if(c.top<d||c.top-d>f)h.addClass("ui-loader-fakefix"),b(),g.unbind("scroll",
-e).bind("scroll",b)}function f(){d.removeClass("ui-mobile-rendering")}var d=a("html");a("head");var g=a(c);a(c.document).trigger("mobileinit");if(a.mobile.gradeA()){if(a.mobile.ajaxBlacklist)a.mobile.ajaxEnabled=false;d.addClass("ui-mobile ui-mobile-rendering");setTimeout(f,5E3);var h=a("<div class='ui-loader'><span class='ui-icon ui-icon-loading'></span><h1></h1></div>");a.extend(a.mobile,{showPageLoadingMsg:function(b,c,f){d.addClass("ui-loading");if(a.mobile.loadingMessage){var k=f||a.mobile.loadingMessageTextVisible;
-b=b||a.mobile.loadingMessageTheme;h.attr("class","ui-loader ui-corner-all ui-body-"+(b||"a")+" ui-loader-"+(k?"verbose":"default")+(f?" ui-loader-textonly":"")).find("h1").text(c||a.mobile.loadingMessage).end().appendTo(a.mobile.pageContainer);e();g.bind("scroll",e)}},hidePageLoadingMsg:function(){d.removeClass("ui-loading");a.mobile.loadingMessage&&h.removeClass("ui-loader-fakefix");a(c).unbind("scroll",b);a(c).unbind("scroll",e)},initializePage:function(){var b=a(":jqmData(role='page'), :jqmData(role='dialog')");
-b.length||(b=a("body").wrapInner("<div data-"+a.mobile.ns+"role='page'></div>").children(0));b.each(function(){var b=a(this);b.jqmData("url")||b.attr("data-"+a.mobile.ns+"url",b.attr("id")||location.pathname+location.search)});a.mobile.firstPage=b.first();a.mobile.pageContainer=b.first().parent().addClass("ui-mobile-viewport");g.trigger("pagecontainercreate");a.mobile.showPageLoadingMsg();f();!a.mobile.hashListeningEnabled||!a.mobile.path.stripHash(location.hash)?a.mobile.changePage(a.mobile.firstPage,
-{transition:"none",reverse:true,changeHash:false,fromHashChange:true}):g.trigger("hashchange",[true])}});a.mobile._registerInternalEvents();a(function(){c.scrollTo(0,1);a.mobile.defaultHomeScroll=!a.support.scrollTop||a(c).scrollTop()===1?0:1;a.fn.controlgroup&&a(k).bind("pagecreate create",function(b){a(":jqmData(role='controlgroup')",b.target).jqmEnhanceable().controlgroup({excludeInvisible:false})});a.mobile.autoInitializePage&&a.mobile.initializePage();g.load(a.mobile.silentScroll)})}})(jQuery,
-this)});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancExplorer/libs/jquery.mobile.min.css	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,2 @@
+/*! jQuery Mobile v1.1.0 db342b1f315c282692791aa870455901fdb46a55 jquerymobile.com | jquery.org/license */
+.ui-bar-a{border:1px solid #333;background:#111;color:#fff;font-weight:bold;text-shadow:0 -1px 1px #000;background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#111));background-image:-webkit-linear-gradient(#3c3c3c,#111);background-image:-moz-linear-gradient(#3c3c3c,#111);background-image:-ms-linear-gradient(#3c3c3c,#111);background-image:-o-linear-gradient(#3c3c3c,#111);background-image:linear-gradient(#3c3c3c,#111)}.ui-bar-a,.ui-bar-a input,.ui-bar-a select,.ui-bar-a textarea,.ui-bar-a button{font-family:Helvetica,Arial,sans-serif}.ui-bar-a .ui-link-inherit{color:#fff}.ui-bar-a .ui-link{color:#7cc4e7;font-weight:bold}.ui-bar-a .ui-link:hover{color:#2489ce}.ui-bar-a .ui-link:active{color:#2489ce}.ui-bar-a .ui-link:visited{color:#2489ce}.ui-body-a,.ui-overlay-a{border:1px solid #444;background:#222;color:#fff;text-shadow:0 1px 1px #111;font-weight:normal;background-image:-webkit-gradient(linear,left top,left bottom,from(#444),to(#222));background-image:-webkit-linear-gradient(#444,#222);background-image:-moz-linear-gradient(#444,#222);background-image:-ms-linear-gradient(#444,#222);background-image:-o-linear-gradient(#444,#222);background-image:linear-gradient(#444,#222)}.ui-overlay-a{background-image:none;border-width:0}.ui-body-a,.ui-body-a input,.ui-body-a select,.ui-body-a textarea,.ui-body-a button{font-family:Helvetica,Arial,sans-serif}.ui-body-a .ui-link-inherit{color:#fff}.ui-body-a .ui-link{color:#2489ce;font-weight:bold}.ui-body-a .ui-link:hover{color:#2489ce}.ui-body-a .ui-link:active{color:#2489ce}.ui-body-a .ui-link:visited{color:#2489ce}.ui-btn-up-a{border:1px solid #111;background:#333;font-weight:bold;color:#fff;text-shadow:0 1px 1px #111;background-image:-webkit-gradient(linear,left top,left bottom,from(#444),to(#2d2d2d));background-image:-webkit-linear-gradient(#444,#2d2d2d);background-image:-moz-linear-gradient(#444,#2d2d2d);background-image:-ms-linear-gradient(#444,#2d2d2d);background-image:-o-linear-gradient(#444,#2d2d2d);background-image:linear-gradient(#444,#2d2d2d)}.ui-btn-up-a a.ui-link-inherit{color:#fff}.ui-btn-hover-a{border:1px solid #000;background:#444;font-weight:bold;color:#fff;text-shadow:0 1px 1px #111;background-image:-webkit-gradient(linear,left top,left bottom,from(#555),to(#383838));background-image:-webkit-linear-gradient(#555,#383838);background-image:-moz-linear-gradient(#555,#383838);background-image:-ms-linear-gradient(#555,#383838);background-image:-o-linear-gradient(#555,#383838);background-image:linear-gradient(#555,#383838)}.ui-btn-hover-a a.ui-link-inherit{color:#fff}.ui-btn-down-a{border:1px solid #000;background:#222;font-weight:bold;color:#fff;text-shadow:0 1px 1px #111;background-image:-webkit-gradient(linear,left top,left bottom,from(#202020),to(#2c2c2c));background-image:-webkit-linear-gradient(#202020,#2c2c2c);background-image:-moz-linear-gradient(#202020,#2c2c2c);background-image:-ms-linear-gradient(#202020,#2c2c2c);background-image:-o-linear-gradient(#202020,#2c2c2c);background-image:linear-gradient(#202020,#2c2c2c)}.ui-btn-down-a a.ui-link-inherit{color:#fff}.ui-btn-up-a,.ui-btn-hover-a,.ui-btn-down-a{font-family:Helvetica,Arial,sans-serif;text-decoration:none}.ui-bar-b{border:1px solid #456f9a;background:#5e87b0;color:#fff;font-weight:bold;text-shadow:0 1px 1px #3e6790;background-image:-webkit-gradient(linear,left top,left bottom,from(#6facd5),to(#497bae));background-image:-webkit-linear-gradient(#6facd5,#497bae);background-image:-moz-linear-gradient(#6facd5,#497bae);background-image:-ms-linear-gradient(#6facd5,#497bae);background-image:-o-linear-gradient(#6facd5,#497bae);background-image:linear-gradient(#6facd5,#497bae)}.ui-bar-b,.ui-bar-b input,.ui-bar-b select,.ui-bar-b textarea,.ui-bar-b button{font-family:Helvetica,Arial,sans-serif}.ui-bar-b .ui-link-inherit{color:#fff}.ui-bar-b .ui-link{color:#ddf0f8;font-weight:bold}.ui-bar-b .ui-link:hover{color:#ddf0f8}.ui-bar-b .ui-link:active{color:#ddf0f8}.ui-bar-b .ui-link:visited{color:#ddf0f8}.ui-body-b,.ui-overlay-b{border:1px solid #999;background:#f3f3f3;color:#222;text-shadow:0 1px 0 #fff;font-weight:normal;background-image:-webkit-gradient(linear,left top,left bottom,from(#ddd),to(#ccc));background-image:-webkit-linear-gradient(#ddd,#ccc);background-image:-moz-linear-gradient(#ddd,#ccc);background-image:-ms-linear-gradient(#ddd,#ccc);background-image:-o-linear-gradient(#ddd,#ccc);background-image:linear-gradient(#ddd,#ccc)}.ui-overlay-b{background-image:none;border-width:0}.ui-body-b,.ui-body-b input,.ui-body-b select,.ui-body-b textarea,.ui-body-b button{font-family:Helvetica,Arial,sans-serif}.ui-body-b .ui-link-inherit{color:#333}.ui-body-b .ui-link{color:#2489ce;font-weight:bold}.ui-body-b .ui-link:hover{color:#2489ce}.ui-body-b .ui-link:active{color:#2489ce}.ui-body-b .ui-link:visited{color:#2489ce}.ui-btn-up-b{border:1px solid #044062;background:#396b9e;font-weight:bold;color:#fff;text-shadow:0 1px 1px #194b7e;background-image:-webkit-gradient(linear,left top,left bottom,from(#5f9cc5),to(#396b9e));background-image:-webkit-linear-gradient(#5f9cc5,#396b9e);background-image:-moz-linear-gradient(#5f9cc5,#396b9e);background-image:-ms-linear-gradient(#5f9cc5,#396b9e);background-image:-o-linear-gradient(#5f9cc5,#396b9e);background-image:linear-gradient(#5f9cc5,#396b9e)}.ui-btn-up-b a.ui-link-inherit{color:#fff}.ui-btn-hover-b{border:1px solid #00415e;background:#4b88b6;font-weight:bold;color:#fff;text-shadow:0 1px 1px #194b7e;background-image:-webkit-gradient(linear,left top,left bottom,from(#6facd5),to(#4272a4));background-image:-webkit-linear-gradient(#6facd5,#4272a4);background-image:-moz-linear-gradient(#6facd5,#4272a4);background-image:-ms-linear-gradient(#6facd5,#4272a4);background-image:-o-linear-gradient(#6facd5,#4272a4);background-image:linear-gradient(#6facd5,#4272a4)}.ui-btn-hover-b a.ui-link-inherit{color:#fff}.ui-btn-down-b{border:1px solid #225377;background:#4e89c5;font-weight:bold;color:#fff;text-shadow:0 1px 1px #194b7e;background-image:-webkit-gradient(linear,left top,left bottom,from(#295b8e),to(#3e79b5));background-image:-webkit-linear-gradient(#295b8e,#3e79b5);background-image:-moz-linear-gradient(#295b8e,#3e79b5);background-image:-ms-linear-gradient(#295b8e,#3e79b5);background-image:-o-linear-gradient(#295b8e,#3e79b5);background-image:linear-gradient(#295b8e,#3e79b5)}.ui-btn-down-b a.ui-link-inherit{color:#fff}.ui-btn-up-b,.ui-btn-hover-b,.ui-btn-down-b{font-family:Helvetica,Arial,sans-serif;text-decoration:none}.ui-bar-c{border:1px solid #b3b3b3;background:#eee;color:#3e3e3e;font-weight:bold;text-shadow:0 1px 1px #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#f0f0f0),to(#ddd));background-image:-webkit-linear-gradient(#f0f0f0,#ddd);background-image:-moz-linear-gradient(#f0f0f0,#ddd);background-image:-ms-linear-gradient(#f0f0f0,#ddd);background-image:-o-linear-gradient(#f0f0f0,#ddd);background-image:linear-gradient(#f0f0f0,#ddd)}.ui-bar-c .ui-link-inherit{color:#3e3e3e}.ui-bar-c .ui-link{color:#7cc4e7;font-weight:bold}.ui-bar-c .ui-link:hover{color:#2489ce}.ui-bar-c .ui-link:active{color:#2489ce}.ui-bar-c .ui-link:visited{color:#2489ce}.ui-bar-c,.ui-bar-c input,.ui-bar-c select,.ui-bar-c textarea,.ui-bar-c button{font-family:Helvetica,Arial,sans-serif}.ui-body-c,.ui-overlay-c{border:1px solid #aaa;color:#333;text-shadow:0 1px 0 #fff;background:#f9f9f9;background-image:-webkit-gradient(linear,left top,left bottom,from(#f9f9f9),to(#eee));background-image:-webkit-linear-gradient(#f9f9f9,#eee);background-image:-moz-linear-gradient(#f9f9f9,#eee);background-image:-ms-linear-gradient(#f9f9f9,#eee);background-image:-o-linear-gradient(#f9f9f9,#eee);background-image:linear-gradient(#f9f9f9,#eee)}.ui-overlay-c{background-image:none;border-width:0}.ui-body-c,.ui-body-c input,.ui-body-c select,.ui-body-c textarea,.ui-body-c button{font-family:Helvetica,Arial,sans-serif}.ui-body-c .ui-link-inherit{color:#333}.ui-body-c .ui-link{color:#2489ce;font-weight:bold}.ui-body-c .ui-link:hover{color:#2489ce}.ui-body-c .ui-link:active{color:#2489ce}.ui-body-c .ui-link:visited{color:#2489ce}.ui-btn-up-c{border:1px solid #ccc;background:#eee;font-weight:bold;color:#222;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f1f1f1));background-image:-webkit-linear-gradient(#fff,#f1f1f1);background-image:-moz-linear-gradient(#fff,#f1f1f1);background-image:-ms-linear-gradient(#fff,#f1f1f1);background-image:-o-linear-gradient(#fff,#f1f1f1);background-image:linear-gradient(#fff,#f1f1f1)}.ui-btn-up-c a.ui-link-inherit{color:#2f3e46}.ui-btn-hover-c{border:1px solid #bbb;background:#dfdfdf;font-weight:bold;color:#222;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#f6f6f6),to(#e0e0e0));background-image:-webkit-linear-gradient(#f9f9f9,#e0e0e0);background-image:-moz-linear-gradient(#f6f6f6,#e0e0e0);background-image:-ms-linear-gradient(#f6f6f6,#e0e0e0);background-image:-o-linear-gradient(#f6f6f6,#e0e0e0);background-image:linear-gradient(#f6f6f6,#e0e0e0)}.ui-btn-hover-c a.ui-link-inherit{color:#2f3e46}.ui-btn-down-c{border:1px solid #bbb;background:#d6d6d6;font-weight:bold;color:#222;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#d0d0d0),to(#dfdfdf));background-image:-webkit-linear-gradient(#d0d0d0,#dfdfdf);background-image:-moz-linear-gradient(#d0d0d0,#dfdfdf);background-image:-ms-linear-gradient(#d0d0d0,#dfdfdf);background-image:-o-linear-gradient(#d0d0d0,#dfdfdf);background-image:linear-gradient(#d0d0d0,#dfdfdf)}.ui-btn-down-c a.ui-link-inherit{color:#2f3e46}.ui-btn-up-c,.ui-btn-hover-c,.ui-btn-down-c{font-family:Helvetica,Arial,sans-serif;text-decoration:none}.ui-bar-d{border:1px solid #bbb;background:#bbb;color:#333;text-shadow:0 1px 0 #eee;background-image:-webkit-gradient(linear,left top,left bottom,from(#ddd),to(#bbb));background-image:-webkit-linear-gradient(#ddd,#bbb);background-image:-moz-linear-gradient(#ddd,#bbb);background-image:-ms-linear-gradient(#ddd,#bbb);background-image:-o-linear-gradient(#ddd,#bbb);background-image:linear-gradient(#ddd,#bbb)}.ui-bar-d,.ui-bar-d input,.ui-bar-d select,.ui-bar-d textarea,.ui-bar-d button{font-family:Helvetica,Arial,sans-serif}.ui-bar-d .ui-link-inherit{color:#333}.ui-bar-d .ui-link{color:#2489ce;font-weight:bold}.ui-bar-d .ui-link:hover{color:#2489ce}.ui-bar-d .ui-link:active{color:#2489ce}.ui-bar-d .ui-link:visited{color:#2489ce}.ui-body-d,.ui-overlay-d{border:1px solid #bbb;color:#333;text-shadow:0 1px 0 #fff;background:#fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#fff));background-image:-webkit-linear-gradient(#fff,#fff);background-image:-moz-linear-gradient(#fff,#fff);background-image:-ms-linear-gradient(#fff,#fff);background-image:-o-linear-gradient(#fff,#fff);background-image:linear-gradient(#fff,#fff)}.ui-overlay-d{background-image:none;border-width:0}.ui-body-d,.ui-body-d input,.ui-body-d select,.ui-body-d textarea,.ui-body-d button{font-family:Helvetica,Arial,sans-serif}.ui-body-d .ui-link-inherit{color:#333}.ui-body-d .ui-link{color:#2489ce;font-weight:bold}.ui-body-d .ui-link:hover{color:#2489ce}.ui-body-d .ui-link:active{color:#2489ce}.ui-body-d .ui-link:visited{color:#2489ce}.ui-btn-up-d{border:1px solid #bbb;background:#fff;font-weight:bold;color:#333;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fafafa),to(#f6f6f6));background-image:-webkit-linear-gradient(#fafafa,#f6f6f6);background-image:-moz-linear-gradient(#fafafa,#f6f6f6);background-image:-ms-linear-gradient(#fafafa,#f6f6f6);background-image:-o-linear-gradient(#fafafa,#f6f6f6);background-image:linear-gradient(#fafafa,#f6f6f6)}.ui-btn-up-d a.ui-link-inherit{color:#333}.ui-btn-hover-d{border:1px solid #aaa;background:#eee;font-weight:bold;color:#333;cursor:pointer;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#eee),to(#fff));background-image:-webkit-linear-gradient(#eee,#fff);background-image:-moz-linear-gradient(#eee,#fff);background-image:-ms-linear-gradient(#eee,#fff);background-image:-o-linear-gradient(#eee,#fff);background-image:linear-gradient(#eee,#fff)}.ui-btn-hover-d a.ui-link-inherit{color:#333}.ui-btn-down-d{border:1px solid #aaa;background:#eee;font-weight:bold;color:#333;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#e5e5e5),to(#f2f2f2));background-image:-webkit-linear-gradient(#e5e5e5,#f2f2f2);background-image:-moz-linear-gradient(#e5e5e5,#f2f2f2);background-image:-ms-linear-gradient(#e5e5e5,#f2f2f2);background-image:-o-linear-gradient(#e5e5e5,#f2f2f2);background-image:linear-gradient(#e5e5e5,#f2f2f2)}.ui-btn-down-d a.ui-link-inherit{color:#333}.ui-btn-up-d,.ui-btn-hover-d,.ui-btn-down-d{font-family:Helvetica,Arial,sans-serif;text-decoration:none}.ui-bar-e{border:1px solid #f7c942;background:#fadb4e;color:#333;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fceda7),to(#fbef7e));background-image:-webkit-linear-gradient(#fceda7,#fbef7e);background-image:-moz-linear-gradient(#fceda7,#fbef7e);background-image:-ms-linear-gradient(#fceda7,#fbef7e);background-image:-o-linear-gradient(#fceda7,#fbef7e);background-image:linear-gradient(#fceda7,#fbef7e)}.ui-bar-e,.ui-bar-e input,.ui-bar-e select,.ui-bar-e textarea,.ui-bar-e button{font-family:Helvetica,Arial,sans-serif}.ui-bar-e .ui-link-inherit{color:#333}.ui-bar-e .ui-link{color:#2489ce;font-weight:bold}.ui-bar-e .ui-link:hover{color:#2489ce}.ui-bar-e .ui-link:active{color:#2489ce}.ui-bar-e .ui-link:visited{color:#2489ce}.ui-body-e,.ui-overlay-e{border:1px solid #f7c942;color:#222;text-shadow:0 1px 0 #fff;background:#fff9df;background-image:-webkit-gradient(linear,left top,left bottom,from(#fffadf),to(#fff3a5));background-image:-webkit-linear-gradient(#fffadf,#fff3a5);background-image:-moz-linear-gradient(#fffadf,#fff3a5);background-image:-ms-linear-gradient(#fffadf,#fff3a5);background-image:-o-linear-gradient(#fffadf,#fff3a5);background-image:linear-gradient(#fffadf,#fff3a5)}.ui-overlay-e{background-image:none;border-width:0}.ui-body-e,.ui-body-e input,.ui-body-e select,.ui-body-e textarea,.ui-body-e button{font-family:Helvetica,Arial,sans-serif}.ui-body-e .ui-link-inherit{color:#333}.ui-body-e .ui-link{color:#2489ce;font-weight:bold}.ui-body-e .ui-link:hover{color:#2489ce}.ui-body-e .ui-link:active{color:#2489ce}.ui-body-e .ui-link:visited{color:#2489ce}.ui-btn-up-e{border:1px solid #f4c63f;background:#fadb4e;font-weight:bold;color:#222;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#ffefaa),to(#ffe155));background-image:-webkit-linear-gradient(#ffefaa,#ffe155);background-image:-moz-linear-gradient(#ffefaa,#ffe155);background-image:-ms-linear-gradient(#ffefaa,#ffe155);background-image:-o-linear-gradient(#ffefaa,#ffe155);background-image:linear-gradient(#ffefaa,#ffe155)}.ui-btn-up-e a.ui-link-inherit{color:#222}.ui-btn-hover-e{border:1px solid #f2c43d;background:#fbe26f;font-weight:bold;color:#111;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fff5ba),to(#fbdd52));background-image:-webkit-linear-gradient(#fff5ba,#fbdd52);background-image:-moz-linear-gradient(#fff5ba,#fbdd52);background-image:-ms-linear-gradient(#fff5ba,#fbdd52);background-image:-o-linear-gradient(#fff5ba,#fbdd52);background-image:linear-gradient(#fff5ba,#fbdd52)}.ui-btn-hover-e a.ui-link-inherit{color:#333}.ui-btn-down-e{border:1px solid #f2c43d;background:#fceda7;font-weight:bold;color:#111;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#f8d94c),to(#fadb4e));background-image:-webkit-linear-gradient(#f8d94c,#fadb4e);background-image:-moz-linear-gradient(#f8d94c,#fadb4e);background-image:-ms-linear-gradient(#f8d94c,#fadb4e);background-image:-o-linear-gradient(#f8d94c,#fadb4e);background-image:linear-gradient(#f8d94c,#fadb4e)}.ui-btn-down-e a.ui-link-inherit{color:#333}.ui-btn-up-e,.ui-btn-hover-e,.ui-btn-down-e{font-family:Helvetica,Arial,sans-serif;text-decoration:none}a.ui-link-inherit{text-decoration:none!important}.ui-btn-active{border:1px solid #2373a5;background:#5393c5;font-weight:bold;color:#fff;cursor:pointer;text-shadow:0 1px 1px #3373a5;text-decoration:none;background-image:-webkit-gradient(linear,left top,left bottom,from(#5393c5),to(#6facd5));background-image:-webkit-linear-gradient(#5393c5,#6facd5);background-image:-moz-linear-gradient(#5393c5,#6facd5);background-image:-ms-linear-gradient(#5393c5,#6facd5);background-image:-o-linear-gradient(#5393c5,#6facd5);background-image:linear-gradient(#5393c5,#6facd5);font-family:Helvetica,Arial,sans-serif}.ui-btn-active a.ui-link-inherit{color:#fff}.ui-btn-inner{border-top:1px solid #fff;border-color:rgba(255,255,255,.3)}.ui-corner-tl{-moz-border-radius-topleft:.6em;-webkit-border-top-left-radius:.6em;border-top-left-radius:.6em}.ui-corner-tr{-moz-border-radius-topright:.6em;-webkit-border-top-right-radius:.6em;border-top-right-radius:.6em}.ui-corner-bl{-moz-border-radius-bottomleft:.6em;-webkit-border-bottom-left-radius:.6em;border-bottom-left-radius:.6em}.ui-corner-br{-moz-border-radius-bottomright:.6em;-webkit-border-bottom-right-radius:.6em;border-bottom-right-radius:.6em}.ui-corner-top{-moz-border-radius-topleft:.6em;-webkit-border-top-left-radius:.6em;border-top-left-radius:.6em;-moz-border-radius-topright:.6em;-webkit-border-top-right-radius:.6em;border-top-right-radius:.6em}.ui-corner-bottom{-moz-border-radius-bottomleft:.6em;-webkit-border-bottom-left-radius:.6em;border-bottom-left-radius:.6em;-moz-border-radius-bottomright:.6em;-webkit-border-bottom-right-radius:.6em;border-bottom-right-radius:.6em}.ui-corner-right{-moz-border-radius-topright:.6em;-webkit-border-top-right-radius:.6em;border-top-right-radius:.6em;-moz-border-radius-bottomright:.6em;-webkit-border-bottom-right-radius:.6em;border-bottom-right-radius:.6em}.ui-corner-left{-moz-border-radius-topleft:.6em;-webkit-border-top-left-radius:.6em;border-top-left-radius:.6em;-moz-border-radius-bottomleft:.6em;-webkit-border-bottom-left-radius:.6em;border-bottom-left-radius:.6em}.ui-corner-all{-moz-border-radius:.6em;-webkit-border-radius:.6em;border-radius:.6em}.ui-corner-none{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.ui-br{border-bottom:#828282;border-bottom:rgba(130,130,130,.3);border-bottom-width:1px;border-bottom-style:solid}.ui-disabled{opacity:.3}.ui-disabled,.ui-disabled a{cursor:default!important;pointer-events:none}.ui-disabled .ui-btn-text{-ms-filter:"alpha(opacity=30)";filter:alpha(opacity=30);zoom:1}.ui-icon,.ui-icon-searchfield:after{background:#666;background:rgba(0,0,0,.4);background-image:url(images/icons-18-white.png);background-repeat:no-repeat;-moz-border-radius:9px;-webkit-border-radius:9px;border-radius:9px}.ui-icon-alt{background:#fff;background:rgba(255,255,255,.3);background-image:url(images/icons-18-black.png);background-repeat:no-repeat}@media only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min--moz-device-pixel-ratio:1.5),only screen and (min-resolution:240dpi){.ui-icon-plus,.ui-icon-minus,.ui-icon-delete,.ui-icon-arrow-r,.ui-icon-arrow-l,.ui-icon-arrow-u,.ui-icon-arrow-d,.ui-icon-check,.ui-icon-gear,.ui-icon-refresh,.ui-icon-forward,.ui-icon-back,.ui-icon-grid,.ui-icon-star,.ui-icon-alert,.ui-icon-info,.ui-icon-home,.ui-icon-search,.ui-icon-searchfield:after,.ui-icon-checkbox-off,.ui-icon-checkbox-on,.ui-icon-radio-off,.ui-icon-radio-on{background-image:url(images/icons-36-white.png);-moz-background-size:776px 18px;-o-background-size:776px 18px;-webkit-background-size:776px 18px;background-size:776px 18px}.ui-icon-alt{background-image:url(images/icons-36-black.png)}}.ui-icon-plus{background-position:-0 50%}.ui-icon-minus{background-position:-36px 50%}.ui-icon-delete{background-position:-72px 50%}.ui-icon-arrow-r{background-position:-108px 50%}.ui-icon-arrow-l{background-position:-144px 50%}.ui-icon-arrow-u{background-position:-180px 50%}.ui-icon-arrow-d{background-position:-216px 50%}.ui-icon-check{background-position:-252px 50%}.ui-icon-gear{background-position:-288px 50%}.ui-icon-refresh{background-position:-324px 50%}.ui-icon-forward{background-position:-360px 50%}.ui-icon-back{background-position:-396px 50%}.ui-icon-grid{background-position:-432px 50%}.ui-icon-star{background-position:-468px 50%}.ui-icon-alert{background-position:-504px 50%}.ui-icon-info{background-position:-540px 50%}.ui-icon-home{background-position:-576px 50%}.ui-icon-search,.ui-icon-searchfield:after{background-position:-612px 50%}.ui-icon-checkbox-off{background-position:-684px 50%}.ui-icon-checkbox-on{background-position:-648px 50%}.ui-icon-radio-off{background-position:-756px 50%}.ui-icon-radio-on{background-position:-720px 50%}.ui-checkbox .ui-icon{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}.ui-icon-checkbox-off,.ui-icon-radio-off{background-color:transparent}.ui-checkbox-on .ui-icon,.ui-radio-on .ui-icon{background-color:#4596ce}.ui-icon-loading{background:url(images/ajax-loader.gif);background-size:46px 46px}.ui-btn-corner-tl{-moz-border-radius-topleft:1em;-webkit-border-top-left-radius:1em;border-top-left-radius:1em}.ui-btn-corner-tr{-moz-border-radius-topright:1em;-webkit-border-top-right-radius:1em;border-top-right-radius:1em}.ui-btn-corner-bl{-moz-border-radius-bottomleft:1em;-webkit-border-bottom-left-radius:1em;border-bottom-left-radius:1em}.ui-btn-corner-br{-moz-border-radius-bottomright:1em;-webkit-border-bottom-right-radius:1em;border-bottom-right-radius:1em}.ui-btn-corner-top{-moz-border-radius-topleft:1em;-webkit-border-top-left-radius:1em;border-top-left-radius:1em;-moz-border-radius-topright:1em;-webkit-border-top-right-radius:1em;border-top-right-radius:1em}.ui-btn-corner-bottom{-moz-border-radius-bottomleft:1em;-webkit-border-bottom-left-radius:1em;border-bottom-left-radius:1em;-moz-border-radius-bottomright:1em;-webkit-border-bottom-right-radius:1em;border-bottom-right-radius:1em}.ui-btn-corner-right{-moz-border-radius-topright:1em;-webkit-border-top-right-radius:1em;border-top-right-radius:1em;-moz-border-radius-bottomright:1em;-webkit-border-bottom-right-radius:1em;border-bottom-right-radius:1em}.ui-btn-corner-left{-moz-border-radius-topleft:1em;-webkit-border-top-left-radius:1em;border-top-left-radius:1em;-moz-border-radius-bottomleft:1em;-webkit-border-bottom-left-radius:1em;border-bottom-left-radius:1em}.ui-btn-corner-all{-moz-border-radius:1em;-webkit-border-radius:1em;border-radius:1em}.ui-corner-tl,.ui-corner-tr,.ui-corner-bl,.ui-corner-br,.ui-corner-top,.ui-corner-bottom,.ui-corner-right,.ui-corner-left,.ui-corner-all,.ui-btn-corner-tl,.ui-btn-corner-tr,.ui-btn-corner-bl,.ui-btn-corner-br,.ui-btn-corner-top,.ui-btn-corner-bottom,.ui-btn-corner-right,.ui-btn-corner-left,.ui-btn-corner-all{-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.ui-overlay{background:#666;opacity:.5;filter:Alpha(Opacity=50);position:absolute;width:100%;height:100%}.ui-overlay-shadow{-moz-box-shadow:0 0 12px rgba(0,0,0,.6);-webkit-box-shadow:0 0 12px rgba(0,0,0,.6);box-shadow:0 0 12px rgba(0,0,0,.6)}.ui-shadow{-moz-box-shadow:0 1px 4px rgba(0,0,0,.3);-webkit-box-shadow:0 1px 4px rgba(0,0,0,.3);box-shadow:0 1px 4px rgba(0,0,0,.3)}.ui-bar-a .ui-shadow,.ui-bar-b .ui-shadow,.ui-bar-c .ui-shadow{-moz-box-shadow:0 1px 0 rgba(255,255,255,.3);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.3);box-shadow:0 1px 0 rgba(255,255,255,.3)}.ui-shadow-inset{-moz-box-shadow:inset 0 1px 4px rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 4px rgba(0,0,0,.2);box-shadow:inset 0 1px 4px rgba(0,0,0,.2)}.ui-icon-shadow{-moz-box-shadow:0 1px 0 rgba(255,255,255,.4);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.4);box-shadow:0 1px 0 rgba(255,255,255,.4)}.ui-btn:focus{outline:0}.ui-focus,.ui-btn:focus{-moz-box-shadow:0 0 12px #387bbe;-webkit-box-shadow:0 0 12px #387bbe;box-shadow:0 0 12px #387bbe}.ui-mobile-nosupport-boxshadow *{-moz-box-shadow:none!important;-webkit-box-shadow:none!important;box-shadow:none!important}.ui-mobile-nosupport-boxshadow .ui-focus,.ui-mobile-nosupport-boxshadow .ui-btn:focus{outline-width:1px;outline-style:dotted}.ui-mobile,.ui-mobile body{height:99.9%}.ui-mobile fieldset,.ui-page{padding:0;margin:0}.ui-mobile a img,.ui-mobile fieldset{border-width:0}.ui-mobile-viewport{margin:0;overflow-x:visible;-webkit-text-size-adjust:none;-ms-text-size-adjust:none;-webkit-tap-highlight-color:rgba(0,0,0,0)}body.ui-mobile-viewport,div.ui-mobile-viewport{overflow-x:hidden}.ui-mobile [data-role=page],.ui-mobile [data-role=dialog],.ui-page{top:0;left:0;width:100%;min-height:100%;position:absolute;display:none;border:0}.ui-mobile .ui-page-active{display:block;overflow:visible}.ui-page{outline:0}@media screen and (orientation:portrait){.ui-mobile,.ui-mobile .ui-page{min-height:420px}}@media screen and (orientation:landscape){.ui-mobile,.ui-mobile .ui-page{min-height:300px}}.ui-loading .ui-loader{display:block}.ui-loader{display:none;z-index:9999999;position:fixed;top:50%;box-shadow:0 1px 1px -1px #fff;left:50%;border:0}.ui-loader-default{background:0;opacity:.18;width:46px;height:46px;margin-left:-23px;margin-top:-23px}.ui-loader-verbose{width:200px;opacity:.88;height:auto;margin-left:-110px;margin-top:-43px;padding:10px}.ui-loader-default h1{font-size:0;width:0;height:0;overflow:hidden}.ui-loader-verbose h1{font-size:16px;margin:0;text-align:center}.ui-loader .ui-icon{background-color:#000;display:block;margin:0;width:44px;height:44px;padding:1px;-webkit-border-radius:36px;-moz-border-radius:36px;border-radius:36px}.ui-loader-verbose .ui-icon{margin:0 auto 10px;opacity:.75}.ui-loader-textonly{padding:15px;margin-left:-115px}.ui-loader-textonly .ui-icon{display:none}.ui-loader-fakefix{position:absolute}.ui-mobile-rendering>*{visibility:hidden}.ui-bar,.ui-body{position:relative;padding:.4em 15px;overflow:hidden;display:block;clear:both}.ui-bar{font-size:16px;margin:0}.ui-bar h1,.ui-bar h2,.ui-bar h3,.ui-bar h4,.ui-bar h5,.ui-bar h6{margin:0;padding:0;font-size:16px;display:inline-block}.ui-header,.ui-footer{position:relative;border-left-width:0;border-right-width:0}.ui-header .ui-btn-left,.ui-header .ui-btn-right,.ui-footer .ui-btn-left,.ui-footer .ui-btn-right{position:absolute;top:3px}.ui-header .ui-btn-left,.ui-footer .ui-btn-left{left:5px}.ui-header .ui-btn-right,.ui-footer .ui-btn-right{right:5px}.ui-footer .ui-btn-icon-notext,.ui-header .ui-btn-icon-notext{top:6px}.ui-header .ui-title,.ui-footer .ui-title{min-height:1.1em;text-align:center;font-size:16px;display:block;margin:.6em 30% .8em;padding:0;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;outline:0!important}.ui-footer .ui-title{margin:.6em 15px .8em}.ui-content{border-width:0;overflow:visible;overflow-x:hidden;padding:15px}.ui-icon{width:18px;height:18px}.ui-nojs{position:absolute;left:-9999px}.ui-hide-label label,.ui-hidden-accessible{position:absolute!important;left:-9999px;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}.ui-mobile-viewport-transitioning,.ui-mobile-viewport-transitioning .ui-page{width:100%;height:100%;overflow:hidden}.in{-webkit-animation-timing-function:ease-out;-webkit-animation-duration:350ms;-moz-animation-timing-function:ease-out;-moz-animation-duration:350ms}.out{-webkit-animation-timing-function:ease-in;-webkit-animation-duration:225ms;-moz-animation-timing-function:ease-in;-moz-animation-duration:225}@-webkit-keyframes fadein{from{opacity:0}to{opacity:1}}@-moz-keyframes fadein{from{opacity:0}to{opacity:1}}@-webkit-keyframes fadeout{from{opacity:1}to{opacity:0}}@-moz-keyframes fadeout{from{opacity:1}to{opacity:0}}.fade.out{opacity:0;-webkit-animation-duration:125ms;-webkit-animation-name:fadeout;-moz-animation-duration:125ms;-moz-animation-name:fadeout}.fade.in{opacity:1;-webkit-animation-duration:225ms;-webkit-animation-name:fadein;-moz-animation-duration:225ms;-moz-animation-name:fadein}.pop{-webkit-transform-origin:50% 50%;-moz-transform-origin:50% 50%}.pop.in{-webkit-transform:scale(1);-moz-transform:scale(1);opacity:1;-webkit-animation-name:popin;-moz-animation-name:popin;-webkit-animation-duration:350ms;-moz-animation-duration:350ms}.pop.out{-webkit-animation-name:fadeout;-moz-animation-name:fadeout;opacity:0;-webkit-animation-duration:100ms;-moz-animation-duration:100ms}.pop.in.reverse{-webkit-animation-name:fadein;-moz-animation-name:fadein}.pop.out.reverse{-webkit-transform:scale(.8);-moz-transform:scale(.8);-webkit-animation-name:popout;-moz-animation-name:popout}@-webkit-keyframes popin{from{-webkit-transform:scale(.8);opacity:0}to{-webkit-transform:scale(1);opacity:1}}@-moz-keyframes popin{from{-moz-transform:scale(.8);opacity:0}to{-moz-transform:scale(1);opacity:1}}@-webkit-keyframes popout{from{-webkit-transform:scale(1);opacity:1}to{-webkit-transform:scale(.8);opacity:0}}@-moz-keyframes popout{from{-moz-transform:scale(1);opacity:1}to{-moz-transform:scale(.8);opacity:0}}@-webkit-keyframes slideinfromright{from{-webkit-transform:translateX(100%)}to{-webkit-transform:translateX(0)}}@-moz-keyframes slideinfromright{from{-moz-transform:translateX(100%)}to{-moz-transform:translateX(0)}}@-webkit-keyframes slideinfromleft{from{-webkit-transform:translateX(-100%)}to{-webkit-transform:translateX(0)}}@-moz-keyframes slideinfromleft{from{-moz-transform:translateX(-100%)}to{-moz-transform:translateX(0)}}@-webkit-keyframes slideouttoleft{from{-webkit-transform:translateX(0)}to{-webkit-transform:translateX(-100%)}}@-moz-keyframes slideouttoleft{from{-moz-transform:translateX(0)}to{-moz-transform:translateX(-100%)}}@-webkit-keyframes slideouttoright{from{-webkit-transform:translateX(0)}to{-webkit-transform:translateX(100%)}}@-moz-keyframes slideouttoright{from{-moz-transform:translateX(0)}to{-moz-transform:translateX(100%)}}.slide.out,.slide.in{-webkit-animation-timing-function:ease-out;-webkit-animation-duration:350ms;-moz-animation-timing-function:ease-out;-moz-animation-duration:350ms}.slide.out{-webkit-transform:translateX(-100%);-webkit-animation-name:slideouttoleft;-moz-transform:translateX(-100%);-moz-animation-name:slideouttoleft}.slide.in{-webkit-transform:translateX(0);-webkit-animation-name:slideinfromright;-moz-transform:translateX(0);-moz-animation-name:slideinfromright}.slide.out.reverse{-webkit-transform:translateX(100%);-webkit-animation-name:slideouttoright;-moz-transform:translateX(100%);-moz-animation-name:slideouttoright}.slide.in.reverse{-webkit-transform:translateX(0);-webkit-animation-name:slideinfromleft;-moz-transform:translateX(0);-moz-animation-name:slideinfromleft}.slidefade.out{-webkit-transform:translateX(-100%);-webkit-animation-name:slideouttoleft;-moz-transform:translateX(-100%);-moz-animation-name:slideouttoleft;-webkit-animation-duration:225ms;-moz-animation-duration:225ms}.slidefade.in{-webkit-transform:translateX(0);-webkit-animation-name:fadein;-moz-transform:translateX(0);-moz-animation-name:fadein;-webkit-animation-duration:200ms;-moz-animation-duration:200ms}.slidefade.out.reverse{-webkit-transform:translateX(100%);-webkit-animation-name:slideouttoright;-moz-transform:translateX(100%);-moz-animation-name:slideouttoright;-webkit-animation-duration:200ms;-moz-animation-duration:200ms}.slidefade.in.reverse{-webkit-transform:translateX(0);-webkit-animation-name:fadein;-moz-transform:translateX(0);-moz-animation-name:fadein;-webkit-animation-duration:200ms;-moz-animation-duration:200ms}.slidedown.out{-webkit-animation-name:fadeout;-moz-animation-name:fadeout;-webkit-animation-duration:100ms;-moz-animation-duration:100ms}.slidedown.in{-webkit-transform:translateY(0);-webkit-animation-name:slideinfromtop;-moz-transform:translateY(0);-moz-animation-name:slideinfromtop;-webkit-animation-duration:250ms;-moz-animation-duration:250ms}.slidedown.in.reverse{-webkit-animation-name:fadein;-moz-animation-name:fadein;-webkit-animation-duration:150ms;-moz-animation-duration:150ms}.slidedown.out.reverse{-webkit-transform:translateY(-100%);-moz-transform:translateY(-100%);-webkit-animation-name:slideouttotop;-moz-animation-name:slideouttotop;-webkit-animation-duration:200ms;-moz-animation-duration:200ms}@-webkit-keyframes slideinfromtop{from{-webkit-transform:translateY(-100%)}to{-webkit-transform:translateY(0)}}@-moz-keyframes slideinfromtop{from{-moz-transform:translateY(-100%)}to{-moz-transform:translateY(0)}}@-webkit-keyframes slideouttotop{from{-webkit-transform:translateY(0)}to{-webkit-transform:translateY(-100%)}}@-moz-keyframes slideouttotop{from{-moz-transform:translateY(0)}to{-moz-transform:translateY(-100%)}}.slideup.out{-webkit-animation-name:fadeout;-moz-animation-name:fadeout;-webkit-animation-duration:100ms;-moz-animation-duration:100ms}.slideup.in{-webkit-transform:translateY(0);-webkit-animation-name:slideinfrombottom;-moz-transform:translateY(0);-moz-animation-name:slideinfrombottom;-webkit-animation-duration:250ms;-moz-animation-duration:250ms}.slideup.in.reverse{-webkit-animation-name:fadein;-moz-animation-name:fadein;-webkit-animation-duration:150ms;-moz-animation-duration:150ms}.slideup.out.reverse{-webkit-transform:translateY(100%);-moz-transform:translateY(100%);-webkit-animation-name:slideouttobottom;-moz-animation-name:slideouttobottom;-webkit-animation-duration:200ms;-moz-animation-duration:200ms}@-webkit-keyframes slideinfrombottom{from{-webkit-transform:translateY(100%)}to{-webkit-transform:translateY(0)}}@-moz-keyframes slideinfrombottom{from{-moz-transform:translateY(100%)}to{-moz-transform:translateY(0)}}@-webkit-keyframes slideouttobottom{from{-webkit-transform:translateY(0)}to{-webkit-transform:translateY(100%)}}@-moz-keyframes slideouttobottom{from{-moz-transform:translateY(0)}to{-moz-transform:translateY(100%)}}.viewport-flip{-webkit-perspective:1000;-moz-perspective:1000;position:absolute}.flip{-webkit-backface-visibility:hidden;-webkit-transform:translateX(0);-moz-backface-visibility:hidden;-moz-transform:translateX(0)}.flip.out{-webkit-transform:rotateY(-90deg) scale(.9);-webkit-animation-name:flipouttoleft;-webkit-animation-duration:175ms;-moz-transform:rotateY(-90deg) scale(.9);-moz-animation-name:flipouttoleft;-moz-animation-duration:175ms}.flip.in{-webkit-animation-name:flipintoright;-webkit-animation-duration:225ms;-moz-animation-name:flipintoright;-moz-animation-duration:225ms}.flip.out.reverse{-webkit-transform:rotateY(90deg) scale(.9);-webkit-animation-name:flipouttoright;-moz-transform:rotateY(90deg) scale(.9);-moz-animation-name:flipouttoright}.flip.in.reverse{-webkit-animation-name:flipintoleft;-moz-animation-name:flipintoleft}@-webkit-keyframes flipouttoleft{from{-webkit-transform:rotateY(0)}to{-webkit-transform:rotateY(-90deg) scale(.9)}}@-moz-keyframes flipouttoleft{from{-moz-transform:rotateY(0)}to{-moz-transform:rotateY(-90deg) scale(.9)}}@-webkit-keyframes flipouttoright{from{-webkit-transform:rotateY(0)}to{-webkit-transform:rotateY(90deg) scale(.9)}}@-moz-keyframes flipouttoright{from{-moz-transform:rotateY(0)}to{-moz-transform:rotateY(90deg) scale(.9)}}@-webkit-keyframes flipintoleft{from{-webkit-transform:rotateY(-90deg) scale(.9)}to{-webkit-transform:rotateY(0)}}@-moz-keyframes flipintoleft{from{-moz-transform:rotateY(-90deg) scale(.9)}to{-moz-transform:rotateY(0)}}@-webkit-keyframes flipintoright{from{-webkit-transform:rotateY(90deg) scale(.9)}to{-webkit-transform:rotateY(0)}}@-moz-keyframes flipintoright{from{-moz-transform:rotateY(90deg) scale(.9)}to{-moz-transform:rotateY(0)}}.viewport-turn{-webkit-perspective:1000;-moz-perspective:1000;position:absolute}.turn{-webkit-backface-visibility:hidden;-webkit-transform:translateX(0);-webkit-transform-origin:0 0;-moz-backface-visibility:hidden;-moz-transform:translateX(0);-moz-transform-origin:0 0}.turn.out{-webkit-transform:rotateY(-90deg) scale(.9);-webkit-animation-name:flipouttoleft;-moz-transform:rotateY(-90deg) scale(.9);-moz-animation-name:flipouttoleft;-webkit-animation-duration:125ms;-moz-animation-duration:125ms}.turn.in{-webkit-animation-name:flipintoright;-moz-animation-name:flipintoright;-webkit-animation-duration:250ms;-moz-animation-duration:250ms}.turn.out.reverse{-webkit-transform:rotateY(90deg) scale(.9);-webkit-animation-name:flipouttoright;-moz-transform:rotateY(90deg) scale(.9);-moz-animation-name:flipouttoright}.turn.in.reverse{-webkit-animation-name:flipintoleft;-moz-animation-name:flipintoleft}@-webkit-keyframes flipouttoleft{from{-webkit-transform:rotateY(0)}to{-webkit-transform:rotateY(-90deg) scale(.9)}}@-moz-keyframes flipouttoleft{from{-moz-transform:rotateY(0)}to{-moz-transform:rotateY(-90deg) scale(.9)}}@-webkit-keyframes flipouttoright{from{-webkit-transform:rotateY(0)}to{-webkit-transform:rotateY(90deg) scale(.9)}}@-moz-keyframes flipouttoright{from{-moz-transform:rotateY(0)}to{-moz-transform:rotateY(90deg) scale(.9)}}@-webkit-keyframes flipintoleft{from{-webkit-transform:rotateY(-90deg) scale(.9)}to{-webkit-transform:rotateY(0)}}@-moz-keyframes flipintoleft{from{-moz-transform:rotateY(-90deg) scale(.9)}to{-moz-transform:rotateY(0)}}@-webkit-keyframes flipintoright{from{-webkit-transform:rotateY(90deg) scale(.9)}to{-webkit-transform:rotateY(0)}}@-moz-keyframes flipintoright{from{-moz-transform:rotateY(90deg) scale(.9)}to{-moz-transform:rotateY(0)}}.flow{-webkit-transform-origin:50% 30%;-moz-transform-origin:50% 30%;-webkit-box-shadow:0 0 20px rgba(0,0,0,.4);-moz-box-shadow:0 0 20px rgba(0,0,0,.4)}.ui-dialog.flow{-webkit-transform-origin:none;-moz-transform-origin:none;-webkit-box-shadow:none;-moz-box-shadow:none}.flow.out{-webkit-transform:translateX(-100%) scale(.7);-webkit-animation-name:flowouttoleft;-webkit-animation-timing-function:ease;-webkit-animation-duration:350ms;-moz-transform:translateX(-100%) scale(.7);-moz-animation-name:flowouttoleft;-moz-animation-timing-function:ease;-moz-animation-duration:350ms}.flow.in{-webkit-transform:translateX(0) scale(1);-webkit-animation-name:flowinfromright;-webkit-animation-timing-function:ease;-webkit-animation-duration:350ms;-moz-transform:translateX(0) scale(1);-moz-animation-name:flowinfromright;-moz-animation-timing-function:ease;-moz-animation-duration:350ms}.flow.out.reverse{-webkit-transform:translateX(100%);-webkit-animation-name:flowouttoright;-moz-transform:translateX(100%);-moz-animation-name:flowouttoright}.flow.in.reverse{-webkit-animation-name:flowinfromleft;-moz-animation-name:flowinfromleft}@-webkit-keyframes flowouttoleft{0%{-webkit-transform:translateX(0) scale(1)}60%,70%{-webkit-transform:translateX(0) scale(.7)}100%{-webkit-transform:translateX(-100%) scale(.7)}}@-moz-keyframes flowouttoleft{0%{-moz-transform:translateX(0) scale(1)}60%,70%{-moz-transform:translateX(0) scale(.7)}100%{-moz-transform:translateX(-100%) scale(.7)}}@-webkit-keyframes flowouttoright{0%{-webkit-transform:translateX(0) scale(1)}60%,70%{-webkit-transform:translateX(0) scale(.7)}100%{-webkit-transform:translateX(100%) scale(.7)}}@-moz-keyframes flowouttoright{0%{-moz-transform:translateX(0) scale(1)}60%,70%{-moz-transform:translateX(0) scale(.7)}100%{-moz-transform:translateX(100%) scale(.7)}}@-webkit-keyframes flowinfromleft{0%{-webkit-transform:translateX(-100%) scale(.7)}30%,40%{-webkit-transform:translateX(0) scale(.7)}100%{-webkit-transform:translateX(0) scale(1)}}@-moz-keyframes flowinfromleft{0%{-moz-transform:translateX(-100%) scale(.7)}30%,40%{-moz-transform:translateX(0) scale(.7)}100%{-moz-transform:translateX(0) scale(1)}}@-webkit-keyframes flowinfromright{0%{-webkit-transform:translateX(100%) scale(.7)}30%,40%{-webkit-transform:translateX(0) scale(.7)}100%{-webkit-transform:translateX(0) scale(1)}}@-moz-keyframes flowinfromright{0%{-moz-transform:translateX(100%) scale(.7)}30%,40%{-moz-transform:translateX(0) scale(.7)}100%{-moz-transform:translateX(0) scale(1)}}.ui-grid-a,.ui-grid-b,.ui-grid-c,.ui-grid-d{overflow:hidden}.ui-block-a,.ui-block-b,.ui-block-c,.ui-block-d,.ui-block-e{margin:0;padding:0;border:0;float:left;min-height:1px}.ui-grid-solo .ui-block-a{width:100%;float:none}.ui-grid-a .ui-block-a,.ui-grid-a .ui-block-b{width:50%}.ui-grid-a .ui-block-a{clear:left}.ui-grid-b .ui-block-a,.ui-grid-b .ui-block-b,.ui-grid-b .ui-block-c{width:33.333%}.ui-grid-b .ui-block-a{clear:left}.ui-grid-c .ui-block-a,.ui-grid-c .ui-block-b,.ui-grid-c .ui-block-c,.ui-grid-c .ui-block-d{width:25%}.ui-grid-c .ui-block-a{clear:left}.ui-grid-d .ui-block-a,.ui-grid-d .ui-block-b,.ui-grid-d .ui-block-c,.ui-grid-d .ui-block-d,.ui-grid-d .ui-block-e{width:20%}.ui-grid-d .ui-block-a{clear:left}.ui-header-fixed,.ui-footer-fixed{left:0;right:0;width:100%;position:fixed;z-index:1000}.ui-header-fixed{top:0}.ui-footer-fixed{bottom:0}.ui-header-fullscreen,.ui-footer-fullscreen{opacity:.9}.ui-page-header-fixed{padding-top:2.5em}.ui-page-footer-fixed{padding-bottom:3em}.ui-page-header-fullscreen .ui-content,.ui-page-footer-fullscreen .ui-content{padding:0}.ui-fixed-hidden{position:absolute}.ui-page-header-fullscreen .ui-fixed-hidden,.ui-page-footer-fullscreen .ui-fixed-hidden{left:-99999em}.ui-header-fixed .ui-btn,.ui-footer-fixed .ui-btn{z-index:10}.ui-navbar{overflow:hidden}.ui-navbar ul,.ui-navbar-expanded ul{list-style:none;padding:0;margin:0;position:relative;display:block;border:0}.ui-navbar-collapsed ul{float:left;width:75%;margin-right:-2px}.ui-navbar-collapsed .ui-navbar-toggle{float:left;width:25%}.ui-navbar li.ui-navbar-truncate{position:absolute;left:-9999px;top:-9999px}.ui-navbar li .ui-btn,.ui-navbar .ui-navbar-toggle .ui-btn{display:block;font-size:12px;text-align:center;margin:0;border-right-width:0;max-width:100%}.ui-navbar li .ui-btn{margin-right:-1px}.ui-navbar li .ui-btn:last-child{margin-right:0}.ui-header .ui-navbar li .ui-btn,.ui-header .ui-navbar .ui-navbar-toggle .ui-btn,.ui-footer .ui-navbar li .ui-btn,.ui-footer .ui-navbar .ui-navbar-toggle .ui-btn{border-top-width:0;border-bottom-width:0}.ui-navbar .ui-btn-inner{padding-left:2px;padding-right:2px}.ui-navbar-noicons li .ui-btn .ui-btn-inner,.ui-navbar-noicons .ui-navbar-toggle .ui-btn-inner{padding-top:.8em;padding-bottom:.9em}.ui-navbar-expanded .ui-btn{margin:0;font-size:14px}.ui-navbar-expanded .ui-btn-inner{padding-left:5px;padding-right:5px}.ui-navbar-expanded .ui-btn-icon-top .ui-btn-inner{padding:45px 5px 15px;text-align:center}.ui-navbar-expanded .ui-btn-icon-top .ui-icon{top:15px}.ui-navbar-expanded .ui-btn-icon-bottom .ui-btn-inner{padding:15px 5px 45px;text-align:center}.ui-navbar-expanded .ui-btn-icon-bottom .ui-icon{bottom:15px}.ui-navbar-expanded li .ui-btn .ui-btn-inner{min-height:2.5em}.ui-navbar-expanded .ui-navbar-noicons .ui-btn .ui-btn-inner{padding-top:1.8em;padding-bottom:1.9em}.ui-btn{display:block;text-align:center;cursor:pointer;position:relative;margin:.5em 5px;padding:0}.ui-mini{margin:.25em 5px}.ui-btn-inner{padding:.6em 20px;min-width:.75em;display:block;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;position:relative;zoom:1}.ui-btn input,.ui-btn button{z-index:2}.ui-btn-left,.ui-btn-right,.ui-btn-inline{display:inline-block}.ui-btn-block{display:block}.ui-header .ui-btn,.ui-footer .ui-btn{display:inline-block;margin:0}.ui-header .ui-btn-inner,.ui-footer .ui-btn-inner,.ui-mini .ui-btn-inner{font-size:12.5px;padding:.55em 11px .5em}.ui-header .ui-fullsize .ui-btn-inner,.ui-footer .ui-fullsize .ui-btn-inner{font-size:16px;padding:.6em 25px}.ui-btn-icon-notext{width:24px;height:24px}.ui-btn-icon-notext .ui-btn-inner{padding:0;height:100%}.ui-btn-icon-notext .ui-btn-inner .ui-icon{margin:2px 1px 2px 3px}.ui-btn-text{position:relative;z-index:1;width:100%}.ui-btn-icon-notext .ui-btn-text{position:absolute;left:-9999px}.ui-btn-icon-left .ui-btn-inner{padding-left:40px}.ui-btn-icon-right .ui-btn-inner{padding-right:40px}.ui-btn-icon-top .ui-btn-inner{padding-top:40px}.ui-btn-icon-bottom .ui-btn-inner{padding-bottom:40px}.ui-header .ui-btn-icon-left .ui-btn-inner,.ui-footer .ui-btn-icon-left .ui-btn-inner,.ui-mini .ui-btn-icon-left .ui-btn-inner{padding-left:30px}.ui-header .ui-btn-icon-right .ui-btn-inner,.ui-footer .ui-btn-icon-right .ui-btn-inner,.ui-mini .ui-btn-icon-right .ui-btn-inner{padding-right:30px}.ui-header .ui-btn-icon-top .ui-btn-inner,.ui-footer .ui-btn-icon-top .ui-btn-inner,.ui-mini .ui-btn-icon-top .ui-btn-inner{padding:30px 3px .5em 3px}.ui-header .ui-btn-icon-bottom .ui-btn-inner,.ui-footer .ui-btn-icon-bottom .ui-btn-inner,.ui-mini .ui-btn-icon-bottom .ui-btn-inner{padding:.55em 3px 30px 3px}.ui-btn-icon-notext .ui-icon{display:block;z-index:0}.ui-btn-icon-left .ui-btn-inner .ui-icon,.ui-btn-icon-right .ui-btn-inner .ui-icon{position:absolute;top:50%;margin-top:-9px}.ui-btn-icon-top .ui-btn-inner .ui-icon,.ui-btn-icon-bottom .ui-btn-inner .ui-icon{position:absolute;left:50%;margin-left:-9px}.ui-btn-icon-left .ui-icon{left:10px}.ui-btn-icon-right .ui-icon{right:10px}.ui-btn-icon-top .ui-icon{top:10px}.ui-btn-icon-bottom .ui-icon{top:auto;bottom:10px}.ui-header .ui-btn-icon-left .ui-icon,.ui-footer .ui-btn-icon-left .ui-icon,.ui-mini.ui-btn-icon-left .ui-icon,.ui-mini .ui-btn-icon-left .ui-icon{left:5px}.ui-header .ui-btn-icon-right .ui-icon,.ui-footer .ui-btn-icon-right .ui-icon,.ui-mini.ui-btn-icon-right .ui-icon,.ui-mini .ui-btn-icon-right .ui-icon{right:5px}.ui-header .ui-btn-icon-top .ui-icon,.ui-footer .ui-btn-icon-top .ui-icon,.ui-mini.ui-btn-icon-top .ui-icon,.ui-mini .ui-btn-icon-top .ui-icon{top:5px}.ui-header .ui-btn-icon-bottom .ui-icon,.ui-footer .ui-btn-icon-bottom .ui-icon,.ui-mini.ui-btn-icon-bottom .ui-icon,.ui-mini .ui-btn-icon-bottom .ui-icon{bottom:5px}.ui-btn-hidden{position:absolute;top:0;left:0;width:100%;height:100%;-webkit-appearance:button;opacity:.1;cursor:pointer;background:#fff;background:rgba(255,255,255,0);filter:Alpha(Opacity=.0001);font-size:1px;border:0;text-indent:-9999px}.ui-collapsible{margin:.5em 0}.ui-collapsible-heading{font-size:16px;display:block;margin:0 -8px;padding:0;border-width:0 0 1px 0;position:relative}.ui-collapsible-heading a{text-align:left;margin:0}.ui-collapsible-heading .ui-btn-inner,.ui-collapsible-heading .ui-btn-icon-left .ui-btn-inner{padding-left:40px}.ui-collapsible-heading .ui-btn-icon-right .ui-btn-inner{padding-left:12px;padding-right:40px}.ui-collapsible-heading .ui-btn-icon-top .ui-btn-inner,.ui-collapsible-heading .ui-btn-icon-bottom .ui-btn-inner{padding-right:40px;text-align:center}.ui-collapsible-heading a span.ui-btn{position:absolute;left:6px;top:50%;margin:-12px 0 0 0;width:20px;height:20px;padding:1px 0 1px 2px;text-indent:-9999px}.ui-collapsible-heading a span.ui-btn .ui-btn-inner{padding:10px 0}.ui-collapsible-heading a span.ui-btn .ui-icon{left:0;margin-top:-10px}.ui-collapsible-heading-status{position:absolute;top:-9999px;left:0}.ui-collapsible-content{display:block;margin:0 -8px;padding:10px 16px;border-top:0;background-image:none;font-weight:normal}.ui-collapsible-content-collapsed{display:none}.ui-collapsible-set{margin:.5em 0}.ui-collapsible-set .ui-collapsible{margin:-1px 0 0}.ui-controlgroup,fieldset.ui-controlgroup{padding:0;margin:0 0 .5em;zoom:1}.ui-bar .ui-controlgroup{margin:0 .3em}.ui-controlgroup-label{font-size:16px;line-height:1.4;font-weight:normal;margin:0 0 .4em}.ui-controlgroup-controls{display:block;width:100%}.ui-controlgroup li{list-style:none}.ui-controlgroup-vertical .ui-btn,.ui-controlgroup-vertical .ui-checkbox,.ui-controlgroup-vertical .ui-radio{margin:0;border-bottom-width:0}.ui-controlgroup-controls label.ui-select{position:absolute;left:-9999px}.ui-controlgroup-vertical .ui-controlgroup-last{border-bottom-width:1px}.ui-controlgroup-horizontal{padding:0}.ui-controlgroup-horizontal .ui-btn-inner{text-align:center}.ui-controlgroup-horizontal .ui-btn,.ui-controlgroup-horizontal .ui-select{display:inline-block;margin:0 -6px 0 0}.ui-controlgroup-horizontal .ui-checkbox,.ui-controlgroup-horizontal .ui-radio{float:left;clear:none;margin:0 -1px 0 0}.ui-controlgroup-horizontal .ui-checkbox .ui-btn,.ui-controlgroup-horizontal .ui-radio .ui-btn,.ui-controlgroup-horizontal .ui-checkbox:last-child,.ui-controlgroup-horizontal .ui-radio:last-child{margin-right:0}.ui-controlgroup-horizontal .ui-controlgroup-last{margin-right:0}.ui-controlgroup .ui-checkbox label,.ui-controlgroup .ui-radio label{font-size:16px}@media all and (min-width:450px){.ui-field-contain .ui-controlgroup-label{vertical-align:top;display:inline-block;width:20%;margin:0 2% 0 0}.ui-field-contain .ui-controlgroup-controls{width:60%;display:inline-block}.ui-field-contain .ui-controlgroup .ui-select{width:100%}.ui-field-contain .ui-controlgroup-horizontal .ui-select{width:auto}}.ui-dialog{background:none!important}.ui-dialog-contain{width:92.5%;max-width:500px;margin:10% auto 15px auto;padding:0}.ui-dialog .ui-header{margin-top:15%;border:0;overflow:hidden}.ui-dialog .ui-header,.ui-dialog .ui-content,.ui-dialog .ui-footer{display:block;position:relative;width:auto}.ui-dialog .ui-header,.ui-dialog .ui-footer{z-index:10;padding:0}.ui-dialog .ui-footer{padding:0 15px}.ui-dialog .ui-content{padding:15px}.ui-dialog{margin-top:-15px}.ui-checkbox,.ui-radio{position:relative;clear:both;margin:.2em 0 .5em;z-index:1}.ui-checkbox .ui-btn,.ui-radio .ui-btn{margin:0;text-align:left;z-index:2}.ui-checkbox .ui-btn-inner,.ui-radio .ui-btn-inner{white-space:normal}.ui-checkbox .ui-btn-icon-left .ui-btn-inner,.ui-radio .ui-btn-icon-left .ui-btn-inner{padding-left:45px}.ui-checkbox .ui-mini.ui-btn-icon-left .ui-btn-inner,.ui-radio .ui-mini.ui-btn-icon-left .ui-btn-inner{padding-left:36px}.ui-checkbox .ui-btn-icon-right .ui-btn-inner,.ui-radio .ui-btn-icon-right .ui-btn-inner{padding-right:45px}.ui-checkbox .ui-mini.ui-btn-icon-right .ui-btn-inner,.ui-radio .ui-mini.ui-btn-icon-right .ui-btn-inner{padding-right:36px}.ui-checkbox .ui-btn-icon-top .ui-btn-inner,.ui-radio .ui-btn-icon-top .ui-btn-inner{padding-right:0;padding-left:0;text-align:center}.ui-checkbox .ui-btn-icon-bottom .ui-btn-inner,.ui-radio .ui-btn-icon-bottom .ui-btn-inner{padding-right:0;padding-left:0;text-align:center}.ui-checkbox .ui-icon,.ui-radio .ui-icon{top:1.1em}.ui-checkbox .ui-btn-icon-left .ui-icon,.ui-radio .ui-btn-icon-left .ui-icon{left:15px}.ui-checkbox .ui-mini.ui-btn-icon-left .ui-icon,.ui-radio .ui-mini.ui-btn-icon-left .ui-icon{left:9px}.ui-checkbox .ui-btn-icon-right .ui-icon,.ui-radio .ui-btn-icon-right .ui-icon{right:15px}.ui-checkbox .ui-mini.ui-btn-icon-right .ui-icon,.ui-radio .ui-mini.ui-btn-icon-right .ui-icon{right:9px}.ui-checkbox .ui-btn-icon-top .ui-icon,.ui-radio .ui-btn-icon-top .ui-icon{top:10px}.ui-checkbox .ui-btn-icon-bottom .ui-icon,.ui-radio .ui-btn-icon-bottom .ui-icon{top:auto;bottom:10px}.ui-checkbox .ui-btn-icon-right .ui-icon,.ui-radio .ui-btn-icon-right .ui-icon{right:15px}.ui-checkbox .ui-mini.ui-btn-icon-right .ui-icon,.ui-radio .ui-mini.ui-btn-icon-right .ui-icon{right:9px}.ui-checkbox input,.ui-radio input{position:absolute;left:20px;top:50%;width:10px;height:10px;margin:-5px 0 0 0;outline:0!important;z-index:1}.ui-field-contain,fieldset.ui-field-contain{padding:.8em 0;margin:0;border-width:0 0 1px 0;overflow:visible}.ui-field-contain:first-child{border-top-width:0}.ui-header .ui-field-contain-left,.ui-header .ui-field-contain-right{position:absolute;top:0;width:25%}.ui-header .ui-field-contain-left{left:1em}.ui-header .ui-field-contain-right{right:1em}@media all and (min-width:450px){.ui-field-contain,.ui-mobile fieldset.ui-field-contain{border-width:0;padding:0;margin:1em 0}}.ui-select{display:block;position:relative}.ui-select select{position:absolute;left:-9999px;top:-9999px}.ui-select .ui-btn{overflow:hidden;opacity:1;margin:0}.ui-select .ui-btn select{cursor:pointer;-webkit-appearance:button;left:0;top:0;width:100%;min-height:1.5em;min-height:100%;height:3em;max-height:100%;opacity:0;-ms-filter:"alpha(opacity=0)";filter:alpha(opacity=0);z-index:2}.ui-select .ui-disabled{opacity:.3}@-moz-document url-prefix(){.ui-select .ui-btn select{opacity:.0001}}.ui-select .ui-btn select.ui-select-nativeonly{opacity:1;text-indent:0}.ui-select .ui-btn-icon-right .ui-btn-inner{padding-right:45px}.ui-select .ui-btn-icon-right .ui-icon{right:15px}.ui-select .ui-mini.ui-btn-icon-right .ui-icon{right:7px}label.ui-select{font-size:16px;line-height:1.4;font-weight:normal;margin:0 0 .3em;display:block}.ui-select .ui-btn-text,.ui-selectmenu .ui-btn-text{display:block;min-height:1em;overflow:hidden!important}.ui-select .ui-btn-text{text-overflow:ellipsis}.ui-selectmenu{position:absolute;padding:0;z-index:1100!important;width:80%;max-width:350px;padding:6px}.ui-selectmenu .ui-listview{margin:0}.ui-selectmenu .ui-btn.ui-li-divider{cursor:default}.ui-selectmenu-hidden{top:-9999px;left:-9999px}.ui-selectmenu-screen{position:absolute;top:0;left:0;width:100%;height:100%;z-index:99}.ui-screen-hidden,.ui-selectmenu-list .ui-li .ui-icon{display:none}.ui-selectmenu-list .ui-li .ui-icon{display:block}.ui-li.ui-selectmenu-placeholder{display:none}.ui-selectmenu .ui-header .ui-title{margin:.6em 46px .8em}@media all and (min-width:450px){.ui-field-contain label.ui-select{vertical-align:top;display:inline-block;width:20%;margin:0 2% 0 0}.ui-field-contain .ui-select{width:60%;display:inline-block}}.ui-selectmenu .ui-header h1:after{content:'.';visibility:hidden}label.ui-input-text{font-size:16px;line-height:1.4;display:block;font-weight:normal;margin:0 0 .3em}input.ui-input-text,textarea.ui-input-text{background-image:none;padding:.4em;line-height:1.4;font-size:16px;display:block;width:97%;outline:0}.ui-header input.ui-input-text,.ui-footer input.ui-input-text{margin-left:1.25%;padding:.4em 1%;width:95.5%}input.ui-input-text{-webkit-appearance:none}textarea.ui-input-text{height:50px;-webkit-transition:height 200ms linear;-moz-transition:height 200ms linear;-o-transition:height 200ms linear;transition:height 200ms linear}.ui-input-search{padding:0 30px;background-image:none;position:relative}.ui-icon-searchfield:after{position:absolute;left:7px;top:50%;margin-top:-9px;content:"";width:18px;height:18px;opacity:.5}.ui-input-search input.ui-input-text{border:0;width:98%;padding:.4em 0;margin:0;display:block;background:transparent none;outline:0!important}.ui-input-search .ui-input-clear{position:absolute;right:0;top:50%;margin-top:-13px}.ui-mini .ui-input-clear{right:-3px}.ui-input-search .ui-input-clear-hidden{display:none}input.ui-mini,.ui-mini input,textarea.ui-mini{font-size:14px}textarea.ui-mini{height:45px}@media all and (min-width:450px){.ui-field-contain label.ui-input-text{vertical-align:top;display:inline-block;width:20%;margin:0 2% 0 0}.ui-field-contain input.ui-input-text,.ui-field-contain textarea.ui-input-text,.ui-field-contain .ui-input-search{width:60%;display:inline-block}.ui-field-contain .ui-input-search{width:50%}.ui-hide-label input.ui-input-text,.ui-hide-label textarea.ui-input-text,.ui-hide-label .ui-input-search{padding:.4em;width:97%}.ui-input-search input.ui-input-text{width:98%}}.ui-listview{margin:0;counter-reset:listnumbering}.ui-content .ui-listview{margin:-15px}.ui-content .ui-listview-inset{margin:1em 0}.ui-listview,.ui-li{list-style:none;padding:0}.ui-li,.ui-li.ui-field-contain{display:block;margin:0;position:relative;overflow:visible;text-align:left;border-width:0;border-top-width:1px}.ui-li .ui-btn-text a.ui-link-inherit{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.ui-li-divider,.ui-li-static{padding:.5em 15px;font-size:14px;font-weight:bold}.ui-li-divider{counter-reset:listnumbering}ol.ui-listview .ui-link-inherit:before,ol.ui-listview .ui-li-static:before,.ui-li-dec{font-size:.8em;display:inline-block;padding-right:.3em;font-weight:normal;counter-increment:listnumbering;content:counter(listnumbering) ". "}ol.ui-listview .ui-li-jsnumbering:before{content:""!important}.ui-listview-inset .ui-li{border-right-width:1px;border-left-width:1px}.ui-li:last-child,.ui-li.ui-field-contain:last-child{border-bottom-width:1px}.ui-li>.ui-btn-inner{display:block;position:relative;padding:0}.ui-li .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li{padding:.7em 15px .7em 15px;display:block}.ui-li-has-thumb .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-thumb{min-height:60px;padding-left:100px}.ui-li-has-icon .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-icon{min-height:20px;padding-left:40px}.ui-li-has-count .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-count{padding-right:45px}.ui-li-has-arrow .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-arrow{padding-right:30px}.ui-li-has-arrow.ui-li-has-count .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-arrow.ui-li-has-count{padding-right:75px}.ui-li-has-count .ui-btn-text{padding-right:15px}.ui-li-heading{font-size:16px;font-weight:bold;display:block;margin:.6em 0;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.ui-li-desc{font-size:12px;font-weight:normal;display:block;margin:-.5em 0 .6em;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.ui-li-thumb,.ui-listview .ui-li-icon{position:absolute;left:1px;top:0;max-height:80px;max-width:80px}.ui-listview .ui-li-icon{max-height:40px;max-width:40px;left:10px;top:.9em}.ui-li-thumb,.ui-listview .ui-li-icon,.ui-li-content{float:left;margin-right:10px}.ui-li-aside{float:right;width:50%;text-align:right;margin:.3em 0}@media all and (min-width:480px){.ui-li-aside{width:45%}}.ui-li-divider{cursor:default}.ui-li-has-alt .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-alt{padding-right:95px}.ui-li-has-count .ui-li-count{position:absolute;font-size:11px;font-weight:bold;padding:.2em .5em;top:50%;margin-top:-.9em;right:48px}.ui-li-divider .ui-li-count,.ui-li-static .ui-li-count{right:10px}.ui-li-has-alt .ui-li-count{right:55px}.ui-li-link-alt{position:absolute;width:40px;height:100%;border-width:0;border-left-width:1px;top:0;right:0;margin:0;padding:0;z-index:2}.ui-li-link-alt .ui-btn{overflow:hidden;position:absolute;right:8px;top:50%;margin:-11px 0 0 0;border-bottom-width:1px;z-index:-1}.ui-li-link-alt .ui-btn-inner{padding:0;height:100%;position:absolute;width:100%;top:0;left:0}.ui-li-link-alt .ui-btn .ui-icon{right:50%;margin-right:-9px}.ui-listview * .ui-btn-inner>.ui-btn>.ui-btn-inner{border-top:0}.ui-listview-filter{border-width:0;overflow:hidden;margin:-15px -15px 15px -15px}.ui-listview-filter .ui-input-search{margin:5px;width:auto;display:block}.ui-listview-filter-inset{margin:-15px -5px -15px -5px;background:transparent}.ui-li.ui-screen-hidden{display:none}@media only screen and (min-device-width:768px) and (max-device-width:1024px){.ui-li .ui-btn-text{overflow:visible}}label.ui-slider{font-size:16px;line-height:1.4;font-weight:normal;margin:0 0 .3em;display:block}input.ui-slider-input,.ui-field-contain input.ui-slider-input{display:inline-block;width:50px}select.ui-slider-switch{display:none}div.ui-slider{position:relative;display:inline-block;overflow:visible;height:15px;padding:0;margin:0 2% 0 20px;top:4px;width:65%}div.ui-slider-mini{height:12px;margin-left:10px}div.ui-slider-bg{border:0;height:100%;padding-right:8px}.ui-controlgroup a.ui-slider-handle,a.ui-slider-handle{position:absolute;z-index:1;top:50%;width:28px;height:28px;margin-top:-15px;margin-left:-15px;outline:0}a.ui-slider-handle .ui-btn-inner{padding:0;height:100%}div.ui-slider-mini a.ui-slider-handle{height:14px;width:14px;margin:-8px 0 0 -7px}div.ui-slider-mini a.ui-slider-handle .ui-btn-inner{height:30px;width:30px;padding:0;margin:-9px 0 0 -9px}@media all and (min-width:450px){.ui-field-contain label.ui-slider{vertical-align:top;display:inline-block;width:20%;margin:0 2% 0 0}.ui-field-contain div.ui-slider{width:43%}.ui-field-contain div.ui-slider-switch{width:5.5em}}div.ui-slider-switch{height:32px;margin-left:0;width:5.8em}a.ui-slider-handle-snapping{-webkit-transition:left 70ms linear;-moz-transition:left 70ms linear}div.ui-slider-switch .ui-slider-handle{margin-top:1px}.ui-slider-inneroffset{margin:0 16px;position:relative;z-index:1}div.ui-slider-switch.ui-slider-mini{width:5em;height:29px}div.ui-slider-switch.ui-slider-mini .ui-slider-inneroffset{margin:0 15px 0 14px}div.ui-slider-switch.ui-slider-mini .ui-slider-handle{width:25px;height:25px;margin:1px 0 0 -13px}div.ui-slider-switch.ui-slider-mini a.ui-slider-handle .ui-btn-inner{height:30px;width:30px;padding:0;margin:0}span.ui-slider-label{position:absolute;text-align:center;width:100%;overflow:hidden;font-size:16px;top:0;line-height:2;min-height:100%;border-width:0;white-space:nowrap}.ui-slider-mini span.ui-slider-label{font-size:14px}span.ui-slider-label-a{z-index:1;left:0;text-indent:-1.5em}span.ui-slider-label-b{z-index:0;right:0;text-indent:1.5em}.ui-slider-inline{width:120px;display:inline-block}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancExplorer/libs/jquery.mobile.min.js	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,177 @@
+/*! jQuery Mobile v1.1.0 db342b1f315c282692791aa870455901fdb46a55 jquerymobile.com | jquery.org/license */
+(function(D,s,k){typeof define==="function"&&define.amd?define(["jquery"],function(a){k(a,D,s);return a.mobile}):k(D.jQuery,D,s)})(this,document,function(D,s,k){(function(a,c,b,e){function f(a){for(;a&&typeof a.originalEvent!=="undefined";)a=a.originalEvent;return a}function d(b){for(var d={},f,g;b;){f=a.data(b,t);for(g in f)if(f[g])d[g]=d.hasVirtualBinding=true;b=b.parentNode}return d}function g(){y&&(clearTimeout(y),y=0);y=setTimeout(function(){F=y=0;C.length=0;z=false;G=true},a.vmouse.resetTimerDuration)}
+function h(b,d,g){var c,h;if(!(h=g&&g[b])){if(g=!g)a:{for(g=d.target;g;){if((h=a.data(g,t))&&(!b||h[b]))break a;g=g.parentNode}g=null}h=g}if(h){c=d;var g=c.type,z,j;c=a.Event(c);c.type=b;h=c.originalEvent;z=a.event.props;g.search(/^(mouse|click)/)>-1&&(z=w);if(h)for(j=z.length;j;)b=z[--j],c[b]=h[b];if(g.search(/mouse(down|up)|click/)>-1&&!c.which)c.which=1;if(g.search(/^touch/)!==-1&&(b=f(h),g=b.touches,b=b.changedTouches,g=g&&g.length?g[0]:b&&b.length?b[0]:e))for(h=0,len=u.length;h<len;h++)b=u[h],
+c[b]=g[b];a(d.target).trigger(c)}return c}function j(b){var d=a.data(b.target,x);if(!z&&(!F||F!==d))if(d=h("v"+b.type,b))d.isDefaultPrevented()&&b.preventDefault(),d.isPropagationStopped()&&b.stopPropagation(),d.isImmediatePropagationStopped()&&b.stopImmediatePropagation()}function o(b){var g=f(b).touches,c;if(g&&g.length===1&&(c=b.target,g=d(c),g.hasVirtualBinding))F=L++,a.data(c,x,F),y&&(clearTimeout(y),y=0),A=G=false,c=f(b).touches[0],s=c.pageX,E=c.pageY,h("vmouseover",b,g),h("vmousedown",b,g)}
+function m(a){G||(A||h("vmousecancel",a,d(a.target)),A=true,g())}function p(b){if(!G){var c=f(b).touches[0],e=A,z=a.vmouse.moveDistanceThreshold;A=A||Math.abs(c.pageX-s)>z||Math.abs(c.pageY-E)>z;flags=d(b.target);A&&!e&&h("vmousecancel",b,flags);h("vmousemove",b,flags);g()}}function l(a){if(!G){G=true;var b=d(a.target),c;h("vmouseup",a,b);if(!A&&(c=h("vclick",a,b))&&c.isDefaultPrevented())c=f(a).changedTouches[0],C.push({touchID:F,x:c.clientX,y:c.clientY}),z=true;h("vmouseout",a,b);A=false;g()}}function r(b){var b=
+a.data(b,t),d;if(b)for(d in b)if(b[d])return true;return false}function n(){}function k(b){var d=b.substr(1);return{setup:function(){r(this)||a.data(this,t,{});a.data(this,t)[b]=true;v[b]=(v[b]||0)+1;v[b]===1&&H.bind(d,j);a(this).bind(d,n);if(K)v.touchstart=(v.touchstart||0)+1,v.touchstart===1&&H.bind("touchstart",o).bind("touchend",l).bind("touchmove",p).bind("scroll",m)},teardown:function(){--v[b];v[b]||H.unbind(d,j);K&&(--v.touchstart,v.touchstart||H.unbind("touchstart",o).unbind("touchmove",p).unbind("touchend",
+l).unbind("scroll",m));var f=a(this),g=a.data(this,t);g&&(g[b]=false);f.unbind(d,n);r(this)||f.removeData(t)}}}var t="virtualMouseBindings",x="virtualTouchID",c="vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split(" "),u="clientX clientY pageX pageY screenX screenY".split(" "),w=a.event.props.concat(a.event.mouseHooks?a.event.mouseHooks.props:[]),v={},y=0,s=0,E=0,A=false,C=[],z=false,G=false,K="addEventListener"in b,H=a(b),L=1,F=0;a.vmouse={moveDistanceThreshold:10,clickDistanceThreshold:10,
+resetTimerDuration:1500};for(var I=0;I<c.length;I++)a.event.special[c[I]]=k(c[I]);K&&b.addEventListener("click",function(b){var d=C.length,f=b.target,g,c,e,h,z;if(d){g=b.clientX;c=b.clientY;threshold=a.vmouse.clickDistanceThreshold;for(e=f;e;){for(h=0;h<d;h++)if(z=C[h],e===f&&Math.abs(z.x-g)<threshold&&Math.abs(z.y-c)<threshold||a.data(e,x)===z.touchID){b.preventDefault();b.stopPropagation();return}e=e.parentNode}}},true)})(jQuery,s,k);(function(a,c,b){function e(a){a=a||location.href;return"#"+a.replace(/^[^#]*#?(.*)$/,
+"$1")}var f="hashchange",d=k,g,h=a.event.special,j=d.documentMode,o="on"+f in c&&(j===b||j>7);a.fn[f]=function(a){return a?this.bind(f,a):this.trigger(f)};a.fn[f].delay=50;h[f]=a.extend(h[f],{setup:function(){if(o)return false;a(g.start)},teardown:function(){if(o)return false;a(g.stop)}});g=function(){function g(){var b=e(),d=t(r);if(b!==r)k(r=b,d),a(c).trigger(f);else if(d!==r)location.href=location.href.replace(/#.*/,"")+d;j=setTimeout(g,a.fn[f].delay)}var h={},j,r=e(),n=function(a){return a},k=
+n,t=n;h.start=function(){j||g()};h.stop=function(){j&&clearTimeout(j);j=b};a.browser.msie&&!o&&function(){var b,c;h.start=function(){if(!b)c=(c=a.fn[f].src)&&c+e(),b=a('<iframe tabindex="-1" title="empty"/>').hide().one("load",function(){c||k(e());g()}).attr("src",c||"javascript:0").insertAfter("body")[0].contentWindow,d.onpropertychange=function(){try{if(event.propertyName==="title")b.document.title=d.title}catch(a){}}};h.stop=n;t=function(){return e(b.location.href)};k=function(g,c){var e=b.document,
+h=a.fn[f].domain;if(g!==c)e.title=d.title,e.open(),h&&e.write('<script>document.domain="'+h+'"<\/script>'),e.close(),b.location.hash=g}}();return h}()})(jQuery,this);(function(a,c){if(a.cleanData){var b=a.cleanData;a.cleanData=function(f){for(var d=0,g;(g=f[d])!=null;d++)a(g).triggerHandler("remove");b(f)}}else{var e=a.fn.remove;a.fn.remove=function(b,d){return this.each(function(){d||(!b||a.filter(b,[this]).length)&&a("*",this).add([this]).each(function(){a(this).triggerHandler("remove")});return e.call(a(this),
+b,d)})}}a.widget=function(b,d,g){var c=b.split(".")[0],e,b=b.split(".")[1];e=c+"-"+b;if(!g)g=d,d=a.Widget;a.expr[":"][e]=function(d){return!!a.data(d,b)};a[c]=a[c]||{};a[c][b]=function(a,b){arguments.length&&this._createWidget(a,b)};d=new d;d.options=a.extend(true,{},d.options);a[c][b].prototype=a.extend(true,d,{namespace:c,widgetName:b,widgetEventPrefix:a[c][b].prototype.widgetEventPrefix||b,widgetBaseClass:e},g);a.widget.bridge(b,a[c][b])};a.widget.bridge=function(b,d){a.fn[b]=function(g){var e=
+typeof g==="string",j=Array.prototype.slice.call(arguments,1),o=this,g=!e&&j.length?a.extend.apply(null,[true,g].concat(j)):g;if(e&&g.charAt(0)==="_")return o;e?this.each(function(){var d=a.data(this,b);if(!d)throw"cannot call methods on "+b+" prior to initialization; attempted to call method '"+g+"'";if(!a.isFunction(d[g]))throw"no such method '"+g+"' for "+b+" widget instance";var e=d[g].apply(d,j);if(e!==d&&e!==c)return o=e,false}):this.each(function(){var c=a.data(this,b);c?c.option(g||{})._init():
+a.data(this,b,new d(g,this))});return o}};a.Widget=function(a,b){arguments.length&&this._createWidget(a,b)};a.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(b,d){a.data(d,this.widgetName,this);this.element=a(d);this.options=a.extend(true,{},this.options,this._getCreateOptions(),b);var g=this;this.element.bind("remove."+this.widgetName,function(){g.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){var b=
+{};a.metadata&&(b=a.metadata.get(element)[this.widgetName]);return b},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(b,d){var g=b;if(arguments.length===0)return a.extend({},this.options);if(typeof b==="string"){if(d===c)return this.options[b];
+g={};g[b]=d}this._setOptions(g);return this},_setOptions:function(b){var d=this;a.each(b,function(a,b){d._setOption(a,b)});return this},_setOption:function(a,b){this.options[a]=b;a==="disabled"&&this.widget()[b?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",b);return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(b,d,g){var c=this.options[b],d=a.Event(d);
+d.type=(b===this.widgetEventPrefix?b:this.widgetEventPrefix+b).toLowerCase();g=g||{};if(d.originalEvent)for(var b=a.event.props.length,e;b;)e=a.event.props[--b],d[e]=d.originalEvent[e];this.element.trigger(d,g);return!(a.isFunction(c)&&c.call(this.element[0],d,g)===false||d.isDefaultPrevented())}}})(jQuery);(function(a,c){a.widget("mobile.widget",{_createWidget:function(){a.Widget.prototype._createWidget.apply(this,arguments);this._trigger("init")},_getCreateOptions:function(){var b=this.element,
+e={};a.each(this.options,function(a){var d=b.jqmData(a.replace(/[A-Z]/g,function(a){return"-"+a.toLowerCase()}));d!==c&&(e[a]=d)});return e},enhanceWithin:function(b,c){this.enhance(a(this.options.initSelector,a(b)),c)},enhance:function(b,c){var f,d=a(b),d=a.mobile.enhanceable(d);c&&d.length&&(f=(f=a.mobile.closestPageData(d))&&f.keepNativeSelector()||"",d=d.not(f));d[this.widgetName]()},raise:function(a){throw"Widget ["+this.widgetName+"]: "+a;}})})(jQuery);(function(a,c){var b={};a.mobile=a.extend({},
+{version:"1.1.0",ns:"",subPageUrlKey:"ui-page",activePageClass:"ui-page-active",activeBtnClass:"ui-btn-active",focusClass:"ui-focus",ajaxEnabled:true,hashListeningEnabled:true,linkBindingEnabled:true,defaultPageTransition:"fade",maxTransitionWidth:false,minScrollBack:250,touchOverflowEnabled:false,defaultDialogTransition:"pop",loadingMessage:"loading",pageLoadErrorMessage:"Error Loading Page",loadingMessageTextVisible:false,loadingMessageTheme:"a",pageLoadErrorMessageTheme:"e",autoInitializePage:true,
+pushStateEnabled:true,ignoreContentEnabled:false,orientationChangeEnabled:true,buttonMarkup:{hoverDelay:200},keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91},silentScroll:function(b){if(a.type(b)!==
+"number")b=a.mobile.defaultHomeScroll;a.event.special.scrollstart.enabled=false;setTimeout(function(){c.scrollTo(0,b);a(k).trigger("silentscroll",{x:0,y:b})},20);setTimeout(function(){a.event.special.scrollstart.enabled=true},150)},nsNormalizeDict:b,nsNormalize:function(d){return!d?void 0:b[d]||(b[d]=a.camelCase(a.mobile.ns+d))},getInheritedTheme:function(a,b){for(var c=a[0],e="",f=/ui-(bar|body|overlay)-([a-z])\b/,m,p;c;){m=c.className||"";if((p=f.exec(m))&&(e=p[2]))break;c=c.parentNode}return e||
+b||"a"},closestPageData:function(a){return a.closest(':jqmData(role="page"), :jqmData(role="dialog")').data("page")},enhanceable:function(a){return this.haveParents(a,"enhance")},hijackable:function(a){return this.haveParents(a,"ajax")},haveParents:function(b,c){if(!a.mobile.ignoreContentEnabled)return b;for(var e=b.length,f=a(),o,m,p,l=0;l<e;l++){m=b.eq(l);p=false;for(o=b[l];o;){if((o.getAttribute?o.getAttribute("data-"+a.mobile.ns+c):"")==="false"){p=true;break}o=o.parentNode}p||(f=f.add(m))}return f}},
+a.mobile);a.fn.jqmData=function(b,c){var f;typeof b!="undefined"&&(b&&(b=a.mobile.nsNormalize(b)),f=this.data.apply(this,arguments.length<2?[b]:[b,c]));return f};a.jqmData=function(b,c,f){var e;typeof c!="undefined"&&(e=a.data(b,c?a.mobile.nsNormalize(c):c,f));return e};a.fn.jqmRemoveData=function(b){return this.removeData(a.mobile.nsNormalize(b))};a.jqmRemoveData=function(b,c){return a.removeData(b,a.mobile.nsNormalize(c))};a.fn.removeWithDependents=function(){a.removeWithDependents(this)};a.removeWithDependents=
+function(b){b=a(b);(b.jqmData("dependents")||a()).remove();b.remove()};a.fn.addDependents=function(b){a.addDependents(a(this),b)};a.addDependents=function(b,c){var f=a(b).jqmData("dependents")||a();a(b).jqmData("dependents",a.merge(f,c))};a.fn.getEncodedText=function(){return a("<div/>").text(a(this).text()).html()};a.fn.jqmEnhanceable=function(){return a.mobile.enhanceable(this)};a.fn.jqmHijackable=function(){return a.mobile.hijackable(this)};var e=a.find,f=/:jqmData\(([^)]*)\)/g;a.find=function(b,
+c,h,j){b=b.replace(f,"[data-"+(a.mobile.ns||"")+"$1]");return e.call(this,b,c,h,j)};a.extend(a.find,e);a.find.matches=function(b,c){return a.find(b,null,null,c)};a.find.matchesSelector=function(b,c){return a.find(c,null,null,[b]).length>0}})(jQuery,this);(function(a){a(s);var c=a("html");a.mobile.media=function(){var b={},e=a("<div id='jquery-mediatest'>"),f=a("<body>").append(e);return function(a){if(!(a in b)){var g=k.createElement("style"),h="@media "+a+" { #jquery-mediatest { position:absolute; } }";
+g.type="text/css";g.styleSheet?g.styleSheet.cssText=h:g.appendChild(k.createTextNode(h));c.prepend(f).prepend(g);b[a]=e.css("position")==="absolute";f.add(g).remove()}return b[a]}}()})(jQuery);(function(a,c){function b(a){var b=a.charAt(0).toUpperCase()+a.substr(1),a=(a+" "+g.join(b+" ")+b).split(" "),f;for(f in a)if(d[a[f]]!==c)return true}function e(a,b,c){var d=k.createElement("div"),c=c?[c]:g,f;for(i=0;i<c.length;i++){var e=c[i],h="-"+e.charAt(0).toLowerCase()+e.substr(1)+"-"+a+": "+b+";",e=e.charAt(0).toUpperCase()+
+e.substr(1)+(a.charAt(0).toUpperCase()+a.substr(1));d.setAttribute("style",h);d.style[e]&&(f=true)}return!!f}var f=a("<body>").prependTo("html"),d=f[0].style,g=["Webkit","Moz","O"],h="palmGetResource"in s,j=s.operamini&&{}.toString.call(s.operamini)==="[object OperaMini]",o=s.blackberry;a.extend(a.mobile,{browser:{}});a.mobile.browser.ie=function(){for(var a=3,b=k.createElement("div"),c=b.all||[];b.innerHTML="<\!--[if gt IE "+ ++a+"]><br><![endif]--\>",c[0];);return a>4?a:!a}();a.extend(a.support,
+{orientation:"orientation"in s&&"onorientationchange"in s,touch:"ontouchend"in k,cssTransitions:"WebKitTransitionEvent"in s||e("transition","height 100ms linear"),pushState:"pushState"in history&&"replaceState"in history,mediaquery:a.mobile.media("only all"),cssPseudoElement:!!b("content"),touchOverflow:!!b("overflowScrolling"),cssTransform3d:e("perspective","10px","moz")||a.mobile.media("(-"+g.join("-transform-3d),(-")+"-transform-3d),(transform-3d)"),boxShadow:!!b("boxShadow")&&!o,scrollTop:("pageXOffset"in
+s||"scrollTop"in k.documentElement||"scrollTop"in f[0])&&!h&&!j,dynamicBaseTag:function(){var b=location.protocol+"//"+location.host+location.pathname+"ui-dir/",c=a("head base"),d=null,e="",g;c.length?e=c.attr("href"):c=d=a("<base>",{href:b}).appendTo("head");g=a("<a href='testurl' />").prependTo(f)[0].href;c[0].href=e||location.pathname;d&&d.remove();return g.indexOf(b)===0}()});f.remove();h=function(){var a=s.navigator.userAgent;return a.indexOf("Nokia")>-1&&(a.indexOf("Symbian/3")>-1||a.indexOf("Series60/5")>
+-1)&&a.indexOf("AppleWebKit")>-1&&a.match(/(BrowserNG|NokiaBrowser)\/7\.[0-3]/)}();a.mobile.gradeA=function(){return a.support.mediaquery||a.mobile.browser.ie&&a.mobile.browser.ie>=7};a.mobile.ajaxBlacklist=s.blackberry&&!s.WebKitPoint||j||h;h&&a(function(){a("head link[rel='stylesheet']").attr("rel","alternate stylesheet").attr("rel","stylesheet")});a.support.boxShadow||a("html").addClass("ui-mobile-nosupport-boxshadow")})(jQuery);(function(a,c,b){function e(b,c,d){var f=d.type;d.type=c;a.event.handle.call(b,
+d);d.type=f}a.each("touchstart touchmove touchend orientationchange throttledresize tap taphold swipe swipeleft swiperight scrollstart scrollstop".split(" "),function(b,c){a.fn[c]=function(a){return a?this.bind(c,a):this.trigger(c)};a.attrFn[c]=true});var f=a.support.touch,d=f?"touchstart":"mousedown",g=f?"touchend":"mouseup",h=f?"touchmove":"mousemove";a.event.special.scrollstart={enabled:true,setup:function(){function b(a,f){d=f;e(c,d?"scrollstart":"scrollstop",a)}var c=this,d,f;a(c).bind("touchmove scroll",
+function(c){a.event.special.scrollstart.enabled&&(d||b(c,true),clearTimeout(f),f=setTimeout(function(){b(c,false)},50))})}};a.event.special.tap={setup:function(){var b=this,c=a(b);c.bind("vmousedown",function(d){function f(){clearTimeout(q)}function g(){f();c.unbind("vclick",h).unbind("vmouseup",f);a(k).unbind("vmousecancel",g)}function h(a){g();n==a.target&&e(b,"tap",a)}if(d.which&&d.which!==1)return false;var n=d.target,q;c.bind("vmouseup",f).bind("vclick",h);a(k).bind("vmousecancel",g);q=setTimeout(function(){e(b,
+"taphold",a.Event("taphold",{target:n}))},750)})}};a.event.special.swipe={scrollSupressionThreshold:10,durationThreshold:1E3,horizontalDistanceThreshold:30,verticalDistanceThreshold:75,setup:function(){var c=a(this);c.bind(d,function(d){function f(b){if(l){var c=b.originalEvent.touches?b.originalEvent.touches[0]:b;k={time:(new Date).getTime(),coords:[c.pageX,c.pageY]};Math.abs(l.coords[0]-k.coords[0])>a.event.special.swipe.scrollSupressionThreshold&&b.preventDefault()}}var e=d.originalEvent.touches?
+d.originalEvent.touches[0]:d,l={time:(new Date).getTime(),coords:[e.pageX,e.pageY],origin:a(d.target)},k;c.bind(h,f).one(g,function(){c.unbind(h,f);l&&k&&k.time-l.time<a.event.special.swipe.durationThreshold&&Math.abs(l.coords[0]-k.coords[0])>a.event.special.swipe.horizontalDistanceThreshold&&Math.abs(l.coords[1]-k.coords[1])<a.event.special.swipe.verticalDistanceThreshold&&l.origin.trigger("swipe").trigger(l.coords[0]>k.coords[0]?"swipeleft":"swiperight");l=k=b})})}};(function(a,b){function c(){var a=
+f();a!==e&&(e=a,d.trigger("orientationchange"))}var d=a(b),f,e,g,h,t={0:true,180:true};if(a.support.orientation&&(g=b.innerWidth||a(b).width(),h=b.innerHeight||a(b).height(),g=g>h&&g-h>50,h=t[b.orientation],g&&h||!g&&!h))t={"-90":true,90:true};a.event.special.orientationchange={setup:function(){if(a.support.orientation&&a.mobile.orientationChangeEnabled)return false;e=f();d.bind("throttledresize",c)},teardown:function(){if(a.support.orientation&&a.mobile.orientationChangeEnabled)return false;d.unbind("throttledresize",
+c)},add:function(a){var b=a.handler;a.handler=function(a){a.orientation=f();return b.apply(this,arguments)}}};a.event.special.orientationchange.orientation=f=function(){var c=true,c=k.documentElement;return(c=a.support.orientation?t[b.orientation]:c&&c.clientWidth/c.clientHeight<1.1)?"portrait":"landscape"}})(jQuery,c);(function(){a.event.special.throttledresize={setup:function(){a(this).bind("resize",b)},teardown:function(){a(this).unbind("resize",b)}};var b=function(){f=(new Date).getTime();g=f-
+c;g>=250?(c=f,a(this).trigger("throttledresize")):(d&&clearTimeout(d),d=setTimeout(b,250-g))},c=0,d,f,g})();a.each({scrollstop:"scrollstart",taphold:"tap",swipeleft:"swipe",swiperight:"swipe"},function(b,c){a.event.special[b]={setup:function(){a(this).bind(c,a.noop)}}})})(jQuery,this);(function(a){a.widget("mobile.page",a.mobile.widget,{options:{theme:"c",domCache:false,keepNativeDefault:":jqmData(role='none'), :jqmData(role='nojs')"},_create:function(){var a=this;if(a._trigger("beforecreate")===
+false)return false;a.element.attr("tabindex","0").addClass("ui-page ui-body-"+a.options.theme).bind("pagebeforehide",function(){a.removeContainerBackground()}).bind("pagebeforeshow",function(){a.setContainerBackground()})},removeContainerBackground:function(){a.mobile.pageContainer.removeClass("ui-overlay-"+a.mobile.getInheritedTheme(this.element.parent()))},setContainerBackground:function(c){this.options.theme&&a.mobile.pageContainer.addClass("ui-overlay-"+(c||this.options.theme))},keepNativeSelector:function(){var c=
+this.options;return c.keepNative&&a.trim(c.keepNative)&&c.keepNative!==c.keepNativeDefault?[c.keepNative,c.keepNativeDefault].join(", "):c.keepNativeDefault}})})(jQuery);(function(a,c,b){var e=function(d){d===b&&(d=true);return function(b,f,e,o){var k=new a.Deferred,p=f?" reverse":"",l=a.mobile.urlHistory.getActive().lastScroll||a.mobile.defaultHomeScroll,r=a.mobile.getScreenHeight(),n=a.mobile.maxTransitionWidth!==false&&a(c).width()>a.mobile.maxTransitionWidth,q=!a.support.cssTransitions||n||!b||
+b==="none",t=function(){a.mobile.pageContainer.toggleClass("ui-mobile-viewport-transitioning viewport-"+b)},x=function(){a.event.special.scrollstart.enabled=false;c.scrollTo(0,l);setTimeout(function(){a.event.special.scrollstart.enabled=true},150)},u=function(){o.removeClass(a.mobile.activePageClass+" out in reverse "+b).height("")},n=function(){o&&d&&u();e.addClass(a.mobile.activePageClass);a.mobile.focusPage(e);e.height(r+l);x();q||e.animationComplete(w);e.addClass(b+" in"+p);q&&w()},w=function(){d||
+o&&u();e.removeClass("out in reverse "+b).height("");t();a(c).scrollTop()!==l&&x();k.resolve(b,f,e,o,true)};t();o&&!q?(d?o.animationComplete(n):n(),o.height(r+a(c).scrollTop()).addClass(b+" out"+p)):n();return k.promise()}},f=e(),e=e(false);a.mobile.defaultTransitionHandler=f;a.mobile.transitionHandlers={"default":a.mobile.defaultTransitionHandler,sequential:f,simultaneous:e};a.mobile.transitionFallbacks={}})(jQuery,this);(function(a,c){function b(b){r&&(!r.closest(".ui-page-active").length||b)&&
+r.removeClass(a.mobile.activeBtnClass);r=null}function e(){t=false;q.length>0&&a.mobile.changePage.apply(null,q.pop())}function f(b,c,d,f){c&&c.data("page")._trigger("beforehide",null,{nextPage:b});b.data("page")._trigger("beforeshow",null,{prevPage:c||a("")});a.mobile.hidePageLoadingMsg();d&&!a.support.cssTransform3d&&a.mobile.transitionFallbacks[d]&&(d=a.mobile.transitionFallbacks[d]);d=(a.mobile.transitionHandlers[d||"default"]||a.mobile.defaultTransitionHandler)(d,f,b,c);d.done(function(){c&&
+c.data("page")._trigger("hide",null,{nextPage:b});b.data("page")._trigger("show",null,{prevPage:c||a("")})});return d}function d(){return s.innerHeight||a(s).height()}function g(){var b=a("."+a.mobile.activePageClass),c=parseFloat(b.css("padding-top")),f=parseFloat(b.css("padding-bottom"));b.css("min-height",d()-c-f)}function h(b,c){c&&b.attr("data-"+a.mobile.ns+"role",c);b.page()}function j(a){for(;a;){if(typeof a.nodeName==="string"&&a.nodeName.toLowerCase()=="a")break;a=a.parentNode}return a}function o(b){var b=
+a(b).closest(".ui-page").jqmData("url"),c=v.hrefNoHash;if(!b||!l.isPath(b))b=c;return l.makeUrlAbsolute(b,c)}var m=a(s);a("html");var p=a("head"),l={urlParseRE:/^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,parseUrl:function(b){if(a.type(b)==="object")return b;b=l.urlParseRE.exec(b||"")||[];return{href:b[0]||"",hrefNoHash:b[1]||"",hrefNoSearch:b[2]||"",domain:b[3]||"",
+protocol:b[4]||"",doubleSlash:b[5]||"",authority:b[6]||"",username:b[8]||"",password:b[9]||"",host:b[10]||"",hostname:b[11]||"",port:b[12]||"",pathname:b[13]||"",directory:b[14]||"",filename:b[15]||"",search:b[16]||"",hash:b[17]||""}},makePathAbsolute:function(a,b){if(a&&a.charAt(0)==="/")return a;for(var a=a||"",c=(b=b?b.replace(/^\/|(\/[^\/]*|[^\/]+)$/g,""):"")?b.split("/"):[],d=a.split("/"),f=0;f<d.length;f++){var e=d[f];switch(e){case ".":break;case "..":c.length&&c.pop();break;default:c.push(e)}}return"/"+
+c.join("/")},isSameDomain:function(a,b){return l.parseUrl(a).domain===l.parseUrl(b).domain},isRelativeUrl:function(a){return l.parseUrl(a).protocol===""},isAbsoluteUrl:function(a){return l.parseUrl(a).protocol!==""},makeUrlAbsolute:function(a,b){if(!l.isRelativeUrl(a))return a;var c=l.parseUrl(a),d=l.parseUrl(b),f=c.protocol||d.protocol,e=c.protocol?c.doubleSlash:c.doubleSlash||d.doubleSlash,g=c.authority||d.authority,h=c.pathname!=="",j=l.makePathAbsolute(c.pathname||d.filename,d.pathname);return f+
+e+g+j+(c.search||!h&&d.search||"")+c.hash},addSearchParams:function(b,c){var d=l.parseUrl(b),f=typeof c==="object"?a.param(c):c,e=d.search||"?";return d.hrefNoSearch+e+(e.charAt(e.length-1)!=="?"?"&":"")+f+(d.hash||"")},convertUrlToDataUrl:function(a){var b=l.parseUrl(a);if(l.isEmbeddedPage(b))return b.hash.split(x)[0].replace(/^#/,"");else if(l.isSameDomain(b,v))return b.hrefNoHash.replace(v.domain,"");return a},get:function(a){if(a===c)a=location.hash;return l.stripHash(a).replace(/[^\/]*\.[^\/*]+$/,
+"")},getFilePath:function(b){var c="&"+a.mobile.subPageUrlKey;return b&&b.split(c)[0].split(x)[0]},set:function(a){location.hash=a},isPath:function(a){return/\//.test(a)},clean:function(a){return a.replace(v.domain,"")},stripHash:function(a){return a.replace(/^#/,"")},cleanHash:function(a){return l.stripHash(a.replace(/\?.*$/,"").replace(x,""))},isExternal:function(a){a=l.parseUrl(a);return a.protocol&&a.domain!==w.domain?true:false},hasProtocol:function(a){return/^(:?\w+:)/.test(a)},isFirstPageUrl:function(b){var b=
+l.parseUrl(l.makeUrlAbsolute(b,v)),d=a.mobile.firstPage,d=d&&d[0]?d[0].id:c;return(b.hrefNoHash===w.hrefNoHash||y&&b.hrefNoHash===v.hrefNoHash)&&(!b.hash||b.hash==="#"||d&&b.hash.replace(/^#/,"")===d)},isEmbeddedPage:function(a){a=l.parseUrl(a);return a.protocol!==""?a.hash&&(a.hrefNoHash===w.hrefNoHash||y&&a.hrefNoHash===v.hrefNoHash):/^#/.test(a.href)}},r=null,n={stack:[],activeIndex:0,getActive:function(){return n.stack[n.activeIndex]},getPrev:function(){return n.stack[n.activeIndex-1]},getNext:function(){return n.stack[n.activeIndex+
+1]},addNew:function(a,b,c,d,f){n.getNext()&&n.clearForward();n.stack.push({url:a,transition:b,title:c,pageUrl:d,role:f});n.activeIndex=n.stack.length-1},clearForward:function(){n.stack=n.stack.slice(0,n.activeIndex+1)},directHashChange:function(b){var d,f,e;this.getActive();a.each(n.stack,function(a,c){b.currentUrl===c.url&&(d=a<n.activeIndex,f=!d,e=a)});this.activeIndex=e!==c?e:this.activeIndex;d?(b.either||b.isBack)(true):f&&(b.either||b.isForward)(false)},ignoreNextHashChange:false},q=[],t=false,
+x="&ui-state=dialog",u=p.children("base"),w=l.parseUrl(location.href),v=u.length?l.parseUrl(l.makeUrlAbsolute(u.attr("href"),w.href)):w,y=w.hrefNoHash!==v.hrefNoHash,B=a.support.dynamicBaseTag?{element:u.length?u:a("<base>",{href:v.hrefNoHash}).prependTo(p),set:function(a){B.element.attr("href",l.makeUrlAbsolute(a,v))},reset:function(){B.element.attr("href",v.hrefNoHash)}}:c;a.mobile.focusPage=function(a){var b=a.find("[autofocus]"),c=a.find(".ui-title:eq(0)");b.length?b.focus():c.length?c.focus():
+a.focus()};var E=true,A,C;A=function(){if(E){var b=a.mobile.urlHistory.getActive();if(b){var c=m.scrollTop();b.lastScroll=c<a.mobile.minScrollBack?a.mobile.defaultHomeScroll:c}}};C=function(){setTimeout(A,100)};m.bind(a.support.pushState?"popstate":"hashchange",function(){E=false});m.one(a.support.pushState?"popstate":"hashchange",function(){E=true});m.one("pagecontainercreate",function(){a.mobile.pageContainer.bind("pagechange",function(){E=true;m.unbind("scrollstop",C);m.bind("scrollstop",C)})});
+m.bind("scrollstop",C);a.mobile.getScreenHeight=d;a.fn.animationComplete=function(b){return a.support.cssTransitions?a(this).one("webkitAnimationEnd animationend",b):(setTimeout(b,0),a(this))};a.mobile.path=l;a.mobile.base=B;a.mobile.urlHistory=n;a.mobile.dialogHashKey=x;a.mobile.allowCrossDomainPages=false;a.mobile.getDocumentUrl=function(b){return b?a.extend({},w):w.href};a.mobile.getDocumentBase=function(b){return b?a.extend({},v):v.href};a.mobile._bindPageRemove=function(){var b=a(this);!b.data("page").options.domCache&&
+b.is(":jqmData(external-page='true')")&&b.bind("pagehide.remove",function(){var b=a(this),c=new a.Event("pageremove");b.trigger(c);c.isDefaultPrevented()||b.removeWithDependents()})};a.mobile.loadPage=function(b,d){var f=a.Deferred(),e=a.extend({},a.mobile.loadPage.defaults,d),g=null,j=null,k=l.makeUrlAbsolute(b,a.mobile.activePage&&o(a.mobile.activePage)||v.hrefNoHash);if(e.data&&e.type==="get")k=l.addSearchParams(k,e.data),e.data=c;if(e.data&&e.type==="post")e.reloadPage=true;var u=l.getFilePath(k),
+n=l.convertUrlToDataUrl(k);e.pageContainer=e.pageContainer||a.mobile.pageContainer;g=e.pageContainer.children(":jqmData(url='"+n+"')");g.length===0&&n&&!l.isPath(n)&&(g=e.pageContainer.children("#"+n).attr("data-"+a.mobile.ns+"url",n));if(g.length===0)if(a.mobile.firstPage&&l.isFirstPageUrl(u))a.mobile.firstPage.parent().length&&(g=a(a.mobile.firstPage));else if(l.isEmbeddedPage(u))return f.reject(k,d),f.promise();B&&B.reset();if(g.length){if(!e.reloadPage)return h(g,e.role),f.resolve(k,d,g),f.promise();
+j=g}var m=e.pageContainer,x=new a.Event("pagebeforeload"),p={url:b,absUrl:k,dataUrl:n,deferred:f,options:e};m.trigger(x,p);if(x.isDefaultPrevented())return f.promise();if(e.showLoadMsg)var r=setTimeout(function(){a.mobile.showPageLoadingMsg()},e.loadMsgDelay);!a.mobile.allowCrossDomainPages&&!l.isSameDomain(w,k)?f.reject(k,d):a.ajax({url:u,type:e.type,data:e.data,dataType:"html",success:function(c,o,m){var x=a("<div></div>"),v=c.match(/<title[^>]*>([^<]*)/)&&RegExp.$1,w=RegExp("\\bdata-"+a.mobile.ns+
+"url=[\"']?([^\"'>]*)[\"']?");RegExp("(<[^>]+\\bdata-"+a.mobile.ns+"role=[\"']?page[\"']?[^>]*>)").test(c)&&RegExp.$1&&w.test(RegExp.$1)&&RegExp.$1&&(b=u=l.getFilePath(RegExp.$1));B&&B.set(u);x.get(0).innerHTML=c;g=x.find(":jqmData(role='page'), :jqmData(role='dialog')").first();g.length||(g=a("<div data-"+a.mobile.ns+"role='page'>"+c.split(/<\/?body[^>]*>/gmi)[1]+"</div>"));v&&!g.jqmData("title")&&(~v.indexOf("&")&&(v=a("<div>"+v+"</div>").text()),g.jqmData("title",v));if(!a.support.dynamicBaseTag){var q=
+l.get(u);g.find("[src], link[href], a[rel='external'], :jqmData(ajax='false'), a[target]").each(function(){var b=a(this).is("[href]")?"href":a(this).is("[src]")?"src":"action",c=a(this).attr(b),c=c.replace(location.protocol+"//"+location.host+location.pathname,"");/^(\w+:|#|\/)/.test(c)||a(this).attr(b,q+c)})}g.attr("data-"+a.mobile.ns+"url",l.convertUrlToDataUrl(u)).attr("data-"+a.mobile.ns+"external-page",true).appendTo(e.pageContainer);g.one("pagecreate",a.mobile._bindPageRemove);h(g,e.role);k.indexOf("&"+
+a.mobile.subPageUrlKey)>-1&&(g=e.pageContainer.children(":jqmData(url='"+n+"')"));e.showLoadMsg&&(clearTimeout(r),a.mobile.hidePageLoadingMsg());p.xhr=m;p.textStatus=o;p.page=g;e.pageContainer.trigger("pageload",p);f.resolve(k,d,g,j)},error:function(b,c,g){B&&B.set(l.get());p.xhr=b;p.textStatus=c;p.errorThrown=g;b=new a.Event("pageloadfailed");e.pageContainer.trigger(b,p);b.isDefaultPrevented()||(e.showLoadMsg&&(clearTimeout(r),a.mobile.hidePageLoadingMsg(),a.mobile.showPageLoadingMsg(a.mobile.pageLoadErrorMessageTheme,
+a.mobile.pageLoadErrorMessage,true),setTimeout(a.mobile.hidePageLoadingMsg,1500)),f.reject(k,d))}});return f.promise()};a.mobile.loadPage.defaults={type:"get",data:c,reloadPage:false,role:c,showLoadMsg:false,pageContainer:c,loadMsgDelay:50};a.mobile.changePage=function(d,g){if(t)q.unshift(arguments);else{var j=a.extend({},a.mobile.changePage.defaults,g);j.pageContainer=j.pageContainer||a.mobile.pageContainer;j.fromPage=j.fromPage||a.mobile.activePage;var u=j.pageContainer,o=new a.Event("pagebeforechange"),
+m={toPage:d,options:j};u.trigger(o,m);if(!o.isDefaultPrevented())if(d=m.toPage,t=true,typeof d=="string")a.mobile.loadPage(d,j).done(function(b,c,d,e){t=false;c.duplicateCachedPage=e;a.mobile.changePage(d,c)}).fail(function(){t=false;b(true);e();j.pageContainer.trigger("pagechangefailed",m)});else{if(d[0]===a.mobile.firstPage[0]&&!j.dataUrl)j.dataUrl=w.hrefNoHash;var o=j.fromPage,p=j.dataUrl&&l.convertUrlToDataUrl(j.dataUrl)||d.jqmData("url"),v=p;l.getFilePath(p);var r=n.getActive(),s=n.activeIndex===
+0,y=0,B=k.title,A=j.role==="dialog"||d.jqmData("role")==="dialog";if(o&&o[0]===d[0]&&!j.allowSamePageTransition)t=false,u.trigger("pagechange",m);else{h(d,j.role);j.fromHashChange&&n.directHashChange({currentUrl:p,isBack:function(){y=-1},isForward:function(){y=1}});try{k.activeElement&&k.activeElement.nodeName.toLowerCase()!="body"?a(k.activeElement).blur():a("input:focus, textarea:focus, select:focus").blur()}catch(E){}A&&r&&(p=(r.url||"")+x);if(j.changeHash!==false&&p)n.ignoreNextHashChange=true,
+l.set(p);var C=!r?B:d.jqmData("title")||d.children(":jqmData(role='header')").find(".ui-title").getEncodedText();C&&B==k.title&&(B=C);d.jqmData("title")||d.jqmData("title",B);j.transition=j.transition||(y&&!s?r.transition:c)||(A?a.mobile.defaultDialogTransition:a.mobile.defaultPageTransition);y||n.addNew(p,j.transition,B,v,j.role);k.title=n.getActive().title;a.mobile.activePage=d;j.reverse=j.reverse||y<0;f(d,o,j.transition,j.reverse).done(function(c,f,g,h,l){b();j.duplicateCachedPage&&j.duplicateCachedPage.remove();
+l||a.mobile.focusPage(d);e();u.trigger("pagechange",m)})}}}};a.mobile.changePage.defaults={transition:c,reverse:false,changeHash:true,fromHashChange:false,role:c,duplicateCachedPage:c,pageContainer:c,showLoadMsg:true,dataUrl:c,fromPage:c,allowSamePageTransition:false};a.mobile._registerInternalEvents=function(){a(k).delegate("form","submit",function(b){var c=a(this);if(a.mobile.ajaxEnabled&&!c.is(":jqmData(ajax='false')")&&c.jqmHijackable().length){var d=c.attr("method"),e=c.attr("target"),f=c.attr("action");
+if(!f&&(f=o(c),f===v.hrefNoHash))f=w.hrefNoSearch;f=l.makeUrlAbsolute(f,o(c));!l.isExternal(f)&&!e&&(a.mobile.changePage(f,{type:d&&d.length&&d.toLowerCase()||"get",data:c.serialize(),transition:c.jqmData("transition"),direction:c.jqmData("direction"),reloadPage:true}),b.preventDefault())}});a(k).bind("vclick",function(c){if(!(c.which>1)&&a.mobile.linkBindingEnabled&&(c=j(c.target),a(c).jqmHijackable().length&&c&&l.parseUrl(c.getAttribute("href")||"#").hash!=="#"))b(true),r=a(c).closest(".ui-btn").not(".ui-disabled"),
+r.addClass(a.mobile.activeBtnClass),a("."+a.mobile.activePageClass+" .ui-btn").not(c).blur(),a(c).jqmData("href",a(c).attr("href")).attr("href","#")});a(k).bind("click",function(d){if(a.mobile.linkBindingEnabled){var f=j(d.target),e=a(f),g;if(f&&!(d.which>1)&&e.jqmHijackable().length){g=function(){s.setTimeout(function(){b(true)},200)};e.jqmData("href")&&e.attr("href",e.jqmData("href"));if(e.is(":jqmData(rel='back')"))return s.history.back(),false;var h=o(e),f=l.makeUrlAbsolute(e.attr("href")||"#",
+h);if(!a.mobile.ajaxEnabled&&!l.isEmbeddedPage(f))g();else{if(f.search("#")!=-1)if(f=f.replace(/[^#]*#/,""))f=l.isPath(f)?l.makeUrlAbsolute(f,h):l.makeUrlAbsolute("#"+f,w.hrefNoHash);else{d.preventDefault();return}var h=e.is("[rel='external']")||e.is(":jqmData(ajax='false')")||e.is("[target]"),k=a.mobile.allowCrossDomainPages&&w.protocol==="file:"&&f.search(/^https?:/)!=-1;h||l.isExternal(f)&&!k?g():(g=e.jqmData("transition"),h=(h=e.jqmData("direction"))&&h==="reverse"||e.jqmData("back"),e=e.attr("data-"+
+a.mobile.ns+"rel")||c,a.mobile.changePage(f,{transition:g,reverse:h,role:e}),d.preventDefault())}}}});a(k).delegate(".ui-page","pageshow.prefetch",function(){var b=[];a(this).find("a:jqmData(prefetch)").each(function(){var c=a(this),d=c.attr("href");d&&a.inArray(d,b)===-1&&(b.push(d),a.mobile.loadPage(d,{role:c.attr("data-"+a.mobile.ns+"rel")}))})});a.mobile._handleHashChange=function(b){var d=l.stripHash(b),f={transition:a.mobile.urlHistory.stack.length===0?"none":c,changeHash:false,fromHashChange:true};
+if(!a.mobile.hashListeningEnabled||n.ignoreNextHashChange)n.ignoreNextHashChange=false;else{if(n.stack.length>1&&d.indexOf(x)>-1)if(a.mobile.activePage.is(".ui-dialog"))n.directHashChange({currentUrl:d,either:function(b){var c=a.mobile.urlHistory.getActive();d=c.pageUrl;a.extend(f,{role:c.role,transition:c.transition,reverse:b})}});else{n.directHashChange({currentUrl:d,isBack:function(){s.history.back()},isForward:function(){s.history.forward()}});return}d?(d=typeof d==="string"&&!l.isPath(d)?l.makeUrlAbsolute("#"+
+d,v):d,a.mobile.changePage(d,f)):a.mobile.changePage(a.mobile.firstPage,f)}};m.bind("hashchange",function(){a.mobile._handleHashChange(location.hash)});a(k).bind("pageshow",g);a(s).bind("throttledresize",g)}})(jQuery);(function(a,c){var b={},e=a(c),f=a.mobile.path.parseUrl(location.href);a.extend(b,{initialFilePath:f.pathname+f.search,initialHref:f.hrefNoHash,state:function(){return{hash:location.hash||"#"+b.initialFilePath,title:k.title,initialHref:b.initialHref}},resetUIKeys:function(b){var c="&"+
+a.mobile.subPageUrlKey,f=b.indexOf(a.mobile.dialogHashKey);f>-1?b=b.slice(0,f)+"#"+b.slice(f):b.indexOf(c)>-1&&(b=b.split(c).join("#"+c));return b},hashValueAfterReset:function(c){c=b.resetUIKeys(c);return a.mobile.path.parseUrl(c).hash},nextHashChangePrevented:function(c){a.mobile.urlHistory.ignoreNextHashChange=c;b.onHashChangeDisabled=c},onHashChange:function(){if(!b.onHashChangeDisabled){var c,f;c=location.hash;var e=a.mobile.path.isPath(c),j=e?location.href:a.mobile.getDocumentUrl();c=e?c.replace("#",
+""):c;f=b.state();c=a.mobile.path.makeUrlAbsolute(c,j);e&&(c=b.resetUIKeys(c));history.replaceState(f,k.title,c)}},onPopState:function(c){var c=c.originalEvent.state,f,h;if(c){f=b.hashValueAfterReset(a.mobile.urlHistory.getActive().url);h=b.hashValueAfterReset(c.hash.replace("#",""));if(f=f!==h)e.one("hashchange.pushstate",function(){b.nextHashChangePrevented(false)});b.nextHashChangePrevented(false);a.mobile._handleHashChange(c.hash);f&&b.nextHashChangePrevented(true)}},init:function(){e.bind("hashchange",
+b.onHashChange);e.bind("popstate",b.onPopState);location.hash===""&&history.replaceState(b.state(),k.title,location.href)}});a(function(){a.mobile.pushStateEnabled&&a.support.pushState&&b.init()})})(jQuery,this);jQuery.mobile.transitionFallbacks.pop="fade";(function(a){a.mobile.transitionHandlers.slide=a.mobile.transitionHandlers.simultaneous;a.mobile.transitionFallbacks.slide="fade"})(jQuery,this);jQuery.mobile.transitionFallbacks.slidedown="fade";jQuery.mobile.transitionFallbacks.slideup="fade";
+jQuery.mobile.transitionFallbacks.flip="fade";jQuery.mobile.transitionFallbacks.flow="fade";jQuery.mobile.transitionFallbacks.turn="fade";(function(a){a.mobile.page.prototype.options.degradeInputs={color:false,date:false,datetime:false,"datetime-local":false,email:false,month:false,number:false,range:"number",search:"text",tel:false,time:false,url:false,week:false};a(k).bind("pagecreate create",function(c){var b=a.mobile.closestPageData(a(c.target)),e;if(b)e=b.options,a(c.target).find("input").not(b.keepNativeSelector()).each(function(){var b=
+a(this),c=this.getAttribute("type"),g=e.degradeInputs[c]||"text";if(e.degradeInputs[c]){var h=a("<div>").html(b.clone()).html(),j=h.indexOf(" type=")>-1;b.replaceWith(h.replace(j?/\s+type=["']?\w+['"]?/:/\/?>/,' type="'+g+'" data-'+a.mobile.ns+'type="'+c+'"'+(j?"":">")))}})})})(jQuery);(function(a,c){a.widget("mobile.dialog",a.mobile.widget,{options:{closeBtnText:"Close",overlayTheme:"a",initSelector:":jqmData(role='dialog')"},_create:function(){var b=this,c=this.element,f=a("<a href='#' data-"+a.mobile.ns+
+"icon='delete' data-"+a.mobile.ns+"iconpos='notext'>"+this.options.closeBtnText+"</a>"),d=a("<div/>",{role:"dialog","class":"ui-dialog-contain ui-corner-all ui-overlay-shadow"});c.addClass("ui-dialog ui-overlay-"+this.options.overlayTheme);c.wrapInner(d).children().find(":jqmData(role='header')").prepend(f).end().children(":first-child").addClass("ui-corner-top").end().children(":last-child").addClass("ui-corner-bottom");f.bind("click",function(){b.close()});c.bind("vclick submit",function(b){var b=
+a(b.target).closest(b.type==="vclick"?"a":"form"),c;b.length&&!b.jqmData("transition")&&(c=a.mobile.urlHistory.getActive()||{},b.attr("data-"+a.mobile.ns+"transition",c.transition||a.mobile.defaultDialogTransition).attr("data-"+a.mobile.ns+"direction","reverse"))}).bind("pagehide",function(){a(this).find("."+a.mobile.activeBtnClass).removeClass(a.mobile.activeBtnClass)}).bind("pagebeforeshow",function(){b.options.overlayTheme&&b.element.page("removeContainerBackground").page("setContainerBackground",
+b.options.overlayTheme)})},close:function(){c.history.back()}});a(k).delegate(a.mobile.dialog.prototype.options.initSelector,"pagecreate",function(){a.mobile.dialog.prototype.enhance(this)})})(jQuery,this);(function(a){a.fn.fieldcontain=function(){return this.addClass("ui-field-contain ui-body ui-br")};a(k).bind("pagecreate create",function(c){a(":jqmData(role='fieldcontain')",c.target).jqmEnhanceable().fieldcontain()})})(jQuery);(function(a){a.fn.grid=function(c){return this.each(function(){var b=
+a(this),e=a.extend({grid:null},c),f=b.children(),d={solo:1,a:2,b:3,c:4,d:5},e=e.grid;if(!e)if(f.length<=5)for(var g in d)d[g]===f.length&&(e=g);else e="a";d=d[e];b.addClass("ui-grid-"+e);f.filter(":nth-child("+d+"n+1)").addClass("ui-block-a");d>1&&f.filter(":nth-child("+d+"n+2)").addClass("ui-block-b");d>2&&f.filter(":nth-child(3n+3)").addClass("ui-block-c");d>3&&f.filter(":nth-child(4n+4)").addClass("ui-block-d");d>4&&f.filter(":nth-child(5n+5)").addClass("ui-block-e")})}})(jQuery);(function(a){a(k).bind("pagecreate create",
+function(c){a(":jqmData(role='nojs')",c.target).addClass("ui-nojs")})})(jQuery);(function(a,c){function b(a){for(var b;a;){if((b=typeof a.className==="string"&&a.className+" ")&&b.indexOf("ui-btn ")>-1&&b.indexOf("ui-disabled ")<0)break;a=a.parentNode}return a}a.fn.buttonMarkup=function(b){for(var b=b&&a.type(b)=="object"?b:{},d=0;d<this.length;d++){var g=this.eq(d),h=g[0],j=a.extend({},a.fn.buttonMarkup.defaults,{icon:b.icon!==c?b.icon:g.jqmData("icon"),iconpos:b.iconpos!==c?b.iconpos:g.jqmData("iconpos"),
+theme:b.theme!==c?b.theme:g.jqmData("theme")||a.mobile.getInheritedTheme(g,"c"),inline:b.inline!==c?b.inline:g.jqmData("inline"),shadow:b.shadow!==c?b.shadow:g.jqmData("shadow"),corners:b.corners!==c?b.corners:g.jqmData("corners"),iconshadow:b.iconshadow!==c?b.iconshadow:g.jqmData("iconshadow"),mini:b.mini!==c?b.mini:g.jqmData("mini")},b),o="ui-btn-inner",m,p,l,r,n,q;a.each(j,function(b,c){h.setAttribute("data-"+a.mobile.ns+b,c);g.jqmData(b,c)});(q=a.data(h.tagName==="INPUT"||h.tagName==="BUTTON"?
+h.parentNode:h,"buttonElements"))?(h=q.outer,g=a(h),l=q.inner,r=q.text,a(q.icon).remove(),q.icon=null):(l=k.createElement(j.wrapperEls),r=k.createElement(j.wrapperEls));n=j.icon?k.createElement("span"):null;e&&!q&&e();if(!j.theme)j.theme=a.mobile.getInheritedTheme(g,"c");m="ui-btn ui-btn-up-"+j.theme;m+=j.inline?" ui-btn-inline":"";m+=j.shadow?" ui-shadow":"";m+=j.corners?" ui-btn-corner-all":"";j.mini!==c&&(m+=j.mini?" ui-mini":" ui-fullsize");j.inline!==c&&(m+=j.inline===false?" ui-btn-block":" ui-btn-inline");
+if(j.icon)j.icon="ui-icon-"+j.icon,j.iconpos=j.iconpos||"left",p="ui-icon "+j.icon,j.iconshadow&&(p+=" ui-icon-shadow");j.iconpos&&(m+=" ui-btn-icon-"+j.iconpos,j.iconpos=="notext"&&!g.attr("title")&&g.attr("title",g.getEncodedText()));o+=j.corners?" ui-btn-corner-all":"";j.iconpos&&j.iconpos==="notext"&&!g.attr("title")&&g.attr("title",g.getEncodedText());q&&g.removeClass(q.bcls||"");g.removeClass("ui-link").addClass(m);l.className=o;r.className="ui-btn-text";q||l.appendChild(r);if(n&&(n.className=
+p,!q||!q.icon))n.appendChild(k.createTextNode("\u00a0")),l.appendChild(n);for(;h.firstChild&&!q;)r.appendChild(h.firstChild);q||h.appendChild(l);q={bcls:m,outer:h,inner:l,text:r,icon:n};a.data(h,"buttonElements",q);a.data(l,"buttonElements",q);a.data(r,"buttonElements",q);n&&a.data(n,"buttonElements",q)}return this};a.fn.buttonMarkup.defaults={corners:true,shadow:true,iconshadow:true,wrapperEls:"span"};var e=function(){var c=a.mobile.buttonMarkup.hoverDelay,d,g;a(k).bind({"vmousedown vmousecancel vmouseup vmouseover vmouseout focus blur scrollstart":function(e){var j,
+k=a(b(e.target)),e=e.type;if(k.length)if(j=k.attr("data-"+a.mobile.ns+"theme"),e==="vmousedown")a.support.touch?d=setTimeout(function(){k.removeClass("ui-btn-up-"+j).addClass("ui-btn-down-"+j)},c):k.removeClass("ui-btn-up-"+j).addClass("ui-btn-down-"+j);else if(e==="vmousecancel"||e==="vmouseup")k.removeClass("ui-btn-down-"+j).addClass("ui-btn-up-"+j);else if(e==="vmouseover"||e==="focus")a.support.touch?g=setTimeout(function(){k.removeClass("ui-btn-up-"+j).addClass("ui-btn-hover-"+j)},c):k.removeClass("ui-btn-up-"+
+j).addClass("ui-btn-hover-"+j);else if(e==="vmouseout"||e==="blur"||e==="scrollstart")k.removeClass("ui-btn-hover-"+j+" ui-btn-down-"+j).addClass("ui-btn-up-"+j),d&&clearTimeout(d),g&&clearTimeout(g)},"focusin focus":function(c){a(b(c.target)).addClass(a.mobile.focusClass)},"focusout blur":function(c){a(b(c.target)).removeClass(a.mobile.focusClass)}});e=null};a(k).bind("pagecreate create",function(b){a(":jqmData(role='button'), .ui-bar > a, .ui-header > a, .ui-footer > a, .ui-bar > :jqmData(role='controlgroup') > a",
+b.target).not(".ui-btn, :jqmData(role='none'), :jqmData(role='nojs')").buttonMarkup()})})(jQuery);(function(a){a.mobile.page.prototype.options.backBtnText="Back";a.mobile.page.prototype.options.addBackBtn=false;a.mobile.page.prototype.options.backBtnTheme=null;a.mobile.page.prototype.options.headerTheme="a";a.mobile.page.prototype.options.footerTheme="a";a.mobile.page.prototype.options.contentTheme=null;a(k).delegate(":jqmData(role='page'), :jqmData(role='dialog')","pagecreate",function(){var c=a(this),
+b=c.data("page").options,e=c.jqmData("role"),f=b.theme;a(":jqmData(role='header'), :jqmData(role='footer'), :jqmData(role='content')",this).jqmEnhanceable().each(function(){var d=a(this),g=d.jqmData("role"),h=d.jqmData("theme"),j=h||b.contentTheme||e==="dialog"&&f,k;d.addClass("ui-"+g);if(g==="header"||g==="footer"){var m=h||(g==="header"?b.headerTheme:b.footerTheme)||f;d.addClass("ui-bar-"+m).attr("role",g==="header"?"banner":"contentinfo");g==="header"&&(h=d.children("a"),k=h.hasClass("ui-btn-left"),
+j=h.hasClass("ui-btn-right"),k=k||h.eq(0).not(".ui-btn-right").addClass("ui-btn-left").length,j||h.eq(1).addClass("ui-btn-right"));b.addBackBtn&&g==="header"&&a(".ui-page").length>1&&c.jqmData("url")!==a.mobile.path.stripHash(location.hash)&&!k&&a("<a href='#' class='ui-btn-left' data-"+a.mobile.ns+"rel='back' data-"+a.mobile.ns+"icon='arrow-l'>"+b.backBtnText+"</a>").attr("data-"+a.mobile.ns+"theme",b.backBtnTheme||m).prependTo(d);d.children("h1, h2, h3, h4, h5, h6").addClass("ui-title").attr({role:"heading",
+"aria-level":"1"})}else g==="content"&&(j&&d.addClass("ui-body-"+j),d.attr("role","main"))})})})(jQuery);(function(a){a.widget("mobile.collapsible",a.mobile.widget,{options:{expandCueText:" click to expand contents",collapseCueText:" click to collapse contents",collapsed:true,heading:"h1,h2,h3,h4,h5,h6,legend",theme:null,contentTheme:null,iconTheme:"d",mini:false,initSelector:":jqmData(role='collapsible')"},_create:function(){var c=this.element,b=this.options,e=c.addClass("ui-collapsible"),f=c.children(b.heading).first(),
+d=e.wrapInner("<div class='ui-collapsible-content'></div>").find(".ui-collapsible-content"),g=c.closest(":jqmData(role='collapsible-set')").addClass("ui-collapsible-set");f.is("legend")&&(f=a("<div role='heading'>"+f.html()+"</div>").insertBefore(f),f.next().remove());if(g.length){if(!b.theme)b.theme=g.jqmData("theme")||a.mobile.getInheritedTheme(g,"c");if(!b.contentTheme)b.contentTheme=g.jqmData("content-theme");if(!b.iconPos)b.iconPos=g.jqmData("iconpos");if(!b.mini)b.mini=g.jqmData("mini")}d.addClass(b.contentTheme?
+"ui-body-"+b.contentTheme:"");f.insertBefore(d).addClass("ui-collapsible-heading").append("<span class='ui-collapsible-heading-status'></span>").wrapInner("<a href='#' class='ui-collapsible-heading-toggle'></a>").find("a").first().buttonMarkup({shadow:false,corners:false,iconpos:c.jqmData("iconpos")||b.iconPos||"left",icon:"plus",mini:b.mini,theme:b.theme}).add(".ui-btn-inner",c).addClass("ui-corner-top ui-corner-bottom");e.bind("expand collapse",function(c){if(!c.isDefaultPrevented()){c.preventDefault();
+var j=a(this),c=c.type==="collapse",k=b.contentTheme;f.toggleClass("ui-collapsible-heading-collapsed",c).find(".ui-collapsible-heading-status").text(c?b.expandCueText:b.collapseCueText).end().find(".ui-icon").toggleClass("ui-icon-minus",!c).toggleClass("ui-icon-plus",c);j.toggleClass("ui-collapsible-collapsed",c);d.toggleClass("ui-collapsible-content-collapsed",c).attr("aria-hidden",c);if(k&&(!g.length||e.jqmData("collapsible-last")))f.find("a").first().add(f.find(".ui-btn-inner")).toggleClass("ui-corner-bottom",
+c),d.toggleClass("ui-corner-bottom",!c);d.trigger("updatelayout")}}).trigger(b.collapsed?"collapse":"expand");f.bind("click",function(a){var b=f.is(".ui-collapsible-heading-collapsed")?"expand":"collapse";e.trigger(b);a.preventDefault()})}});a(k).bind("pagecreate create",function(c){a.mobile.collapsible.prototype.enhanceWithin(c.target)})})(jQuery);(function(a,c){a.widget("mobile.collapsibleset",a.mobile.widget,{options:{initSelector:":jqmData(role='collapsible-set')"},_create:function(){var b=this.element.addClass("ui-collapsible-set"),
+e=this.options;if(!e.theme)e.theme=a.mobile.getInheritedTheme(b,"c");if(!e.contentTheme)e.contentTheme=b.jqmData("content-theme");if(!e.corners)e.corners=b.jqmData("corners")===c?true:false;b.jqmData("collapsiblebound")||b.jqmData("collapsiblebound",true).bind("expand collapse",function(b){var c=b.type==="collapse",b=a(b.target).closest(".ui-collapsible"),e=b.data("collapsible");e.options.contentTheme&&b.jqmData("collapsible-last")&&(b.find(e.options.heading).first().find("a").first().add(".ui-btn-inner").toggleClass("ui-corner-bottom",
+c),b.find(".ui-collapsible-content").toggleClass("ui-corner-bottom",!c))}).bind("expand",function(b){a(b.target).closest(".ui-collapsible").siblings(".ui-collapsible").trigger("collapse")})},_init:function(){this.refresh()},refresh:function(){var b=this.options,c=this.element.children(":jqmData(role='collapsible')");a.mobile.collapsible.prototype.enhance(c.not(".ui-collapsible"));c.each(function(){a(this).find(a.mobile.collapsible.prototype.options.heading).find("a").first().add(".ui-btn-inner").removeClass("ui-corner-top ui-corner-bottom")});
+c.first().find("a").first().addClass(b.corners?"ui-corner-top":"").find(".ui-btn-inner").addClass("ui-corner-top");c.last().jqmData("collapsible-last",true).find("a").first().addClass(b.corners?"ui-corner-bottom":"").find(".ui-btn-inner").addClass("ui-corner-bottom")}});a(k).bind("pagecreate create",function(b){a.mobile.collapsibleset.prototype.enhanceWithin(b.target)})})(jQuery);(function(a,c){a.widget("mobile.navbar",a.mobile.widget,{options:{iconpos:"top",grid:null,initSelector:":jqmData(role='navbar')"},
+_create:function(){var b=this.element,e=b.find("a"),f=e.filter(":jqmData(icon)").length?this.options.iconpos:c;b.addClass("ui-navbar").attr("role","navigation").find("ul").jqmEnhanceable().grid({grid:this.options.grid});f||b.addClass("ui-navbar-noicons");e.buttonMarkup({corners:false,shadow:false,inline:true,iconpos:f});b.delegate("a","vclick",function(b){a(b.target).hasClass("ui-disabled")||(e.removeClass(a.mobile.activeBtnClass),a(this).addClass(a.mobile.activeBtnClass))});b.closest(".ui-page").bind("pagebeforeshow",
+function(){e.filter(".ui-state-persist").addClass(a.mobile.activeBtnClass)})}});a(k).bind("pagecreate create",function(b){a.mobile.navbar.prototype.enhanceWithin(b.target)})})(jQuery);(function(a){var c={};a.widget("mobile.listview",a.mobile.widget,{options:{theme:null,countTheme:"c",headerTheme:"b",dividerTheme:"b",splitIcon:"arrow-r",splitTheme:"b",mini:false,inset:false,initSelector:":jqmData(role='listview')"},_create:function(){var a="";a+=this.options.inset?" ui-listview-inset ui-corner-all ui-shadow ":
+"";a+=this.element.jqmData("mini")||this.options.mini===true?" ui-mini":"";this.element.addClass(function(c,f){return f+" ui-listview "+a});this.refresh(true)},_removeCorners:function(a,c){a=a.add(a.find(".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb"));c==="top"?a.removeClass("ui-corner-top ui-corner-tr ui-corner-tl"):c==="bottom"?a.removeClass("ui-corner-bottom ui-corner-br ui-corner-bl"):a.removeClass("ui-corner-top ui-corner-tr ui-corner-tl ui-corner-bottom ui-corner-br ui-corner-bl")},_refreshCorners:function(a){var c,
+f;this.options.inset&&(c=this.element.children("li"),f=a?c.not(".ui-screen-hidden"):c.filter(":visible"),this._removeCorners(c),c=f.first().addClass("ui-corner-top"),c.add(c.find(".ui-btn-inner").not(".ui-li-link-alt span:first-child")).addClass("ui-corner-top").end().find(".ui-li-link-alt, .ui-li-link-alt span:first-child").addClass("ui-corner-tr").end().find(".ui-li-thumb").not(".ui-li-icon").addClass("ui-corner-tl"),f=f.last().addClass("ui-corner-bottom"),f.add(f.find(".ui-btn-inner")).find(".ui-li-link-alt").addClass("ui-corner-br").end().find(".ui-li-thumb").not(".ui-li-icon").addClass("ui-corner-bl"));
+a||this.element.trigger("updatelayout")},_findFirstElementByTagName:function(a,c,f,d){var g={};for(g[f]=g[d]=true;a;){if(g[a.nodeName])return a;a=a[c]}return null},_getChildrenByTagName:function(b,c,f){var d=[],g={};g[c]=g[f]=true;for(b=b.firstChild;b;)g[b.nodeName]&&d.push(b),b=b.nextSibling;return a(d)},_addThumbClasses:function(b){var c,f,d=b.length;for(c=0;c<d;c++)f=a(this._findFirstElementByTagName(b[c].firstChild,"nextSibling","img","IMG")),f.length&&(f.addClass("ui-li-thumb"),a(this._findFirstElementByTagName(f[0].parentNode,
+"parentNode","li","LI")).addClass(f.is(".ui-li-icon")?"ui-li-has-icon":"ui-li-has-thumb"))},refresh:function(b){this.parentPage=this.element.closest(".ui-page");this._createSubPages();var c=this.options,f=this.element,d=f.jqmData("dividertheme")||c.dividerTheme,g=f.jqmData("splittheme"),h=f.jqmData("spliticon"),j=this._getChildrenByTagName(f[0],"li","LI"),o=a.support.cssPseudoElement||!a.nodeName(f[0],"ol")?0:1,m={},p,l,r,n,q,t,x;o&&f.find(".ui-li-dec").remove();if(!c.theme)c.theme=a.mobile.getInheritedTheme(this.element,
+"c");for(var u=0,w=j.length;u<w;u++){p=j.eq(u);l="ui-li";if(b||!p.hasClass("ui-li"))r=p.jqmData("theme")||c.theme,n=this._getChildrenByTagName(p[0],"a","A"),n.length?(t=p.jqmData("icon"),p.buttonMarkup({wrapperEls:"div",shadow:false,corners:false,iconpos:"right",icon:n.length>1||t===false?false:t||"arrow-r",theme:r}),t!=false&&n.length==1&&p.addClass("ui-li-has-arrow"),n.first().removeClass("ui-link").addClass("ui-link-inherit"),n.length>1&&(l+=" ui-li-has-alt",n=n.last(),q=g||n.jqmData("theme")||
+c.splitTheme,x=n.jqmData("icon"),n.appendTo(p).attr("title",n.getEncodedText()).addClass("ui-li-link-alt").empty().buttonMarkup({shadow:false,corners:false,theme:r,icon:false,iconpos:false}).find(".ui-btn-inner").append(a(k.createElement("span")).buttonMarkup({shadow:true,corners:true,theme:q,iconpos:"notext",icon:x||t||h||c.splitIcon})))):p.jqmData("role")==="list-divider"?(l+=" ui-li-divider ui-bar-"+d,p.attr("role","heading"),o&&(o=1)):l+=" ui-li-static ui-body-"+r;o&&l.indexOf("ui-li-divider")<
+0&&(r=p.is(".ui-li-static:first")?p:p.find(".ui-link-inherit"),r.addClass("ui-li-jsnumbering").prepend("<span class='ui-li-dec'>"+o++ +". </span>"));m[l]||(m[l]=[]);m[l].push(p[0])}for(l in m)a(m[l]).addClass(l).children(".ui-btn-inner").addClass(l);f.find("h1, h2, h3, h4, h5, h6").addClass("ui-li-heading").end().find("p, dl").addClass("ui-li-desc").end().find(".ui-li-aside").each(function(){var b=a(this);b.prependTo(b.parent())}).end().find(".ui-li-count").each(function(){a(this).closest("li").addClass("ui-li-has-count")}).addClass("ui-btn-up-"+
+(f.jqmData("counttheme")||this.options.countTheme)+" ui-btn-corner-all");this._addThumbClasses(j);this._addThumbClasses(f.find(".ui-link-inherit"));this._refreshCorners(b)},_idStringEscape:function(a){return a.replace(/[^a-zA-Z0-9]/g,"-")},_createSubPages:function(){var b=this.element,e=b.closest(".ui-page"),f=e.jqmData("url"),d=f||e[0][a.expando],g=b.attr("id"),h=this.options,j="data-"+a.mobile.ns,k=this,m=e.find(":jqmData(role='footer')").jqmData("id"),p;typeof c[d]==="undefined"&&(c[d]=-1);g=g||
+++c[d];a(b.find("li>ul, li>ol").toArray().reverse()).each(function(c){var d=a(this),e=d.attr("id")||g+"-"+c,c=d.parent(),k=a(d.prevAll().toArray().reverse()),k=k.length?k:a("<span>"+a.trim(c.contents()[0].nodeValue)+"</span>"),o=k.first().getEncodedText(),e=(f||"")+"&"+a.mobile.subPageUrlKey+"="+e,x=d.jqmData("theme")||h.theme,u=d.jqmData("counttheme")||b.jqmData("counttheme")||h.countTheme;p=true;d.detach().wrap("<div "+j+"role='page' "+j+"url='"+e+"' "+j+"theme='"+x+"' "+j+"count-theme='"+u+"'><div "+
+j+"role='content'></div></div>").parent().before("<div "+j+"role='header' "+j+"theme='"+h.headerTheme+"'><div class='ui-title'>"+o+"</div></div>").after(m?a("<div "+j+"role='footer' "+j+"id='"+m+"'>"):"").parent().appendTo(a.mobile.pageContainer).page();d=c.find("a:first");d.length||(d=a("<a/>").html(k||o).prependTo(c.empty()));d.attr("href","#"+e)}).listview();p&&e.is(":jqmData(external-page='true')")&&e.data("page").options.domCache===false&&e.unbind("pagehide.remove").bind("pagehide.remove",function(b,
+c){var d=c.nextPage;c.nextPage&&(d=d.jqmData("url"),d.indexOf(f+"&"+a.mobile.subPageUrlKey)!==0&&(k.childPages().remove(),e.remove()))})},childPages:function(){var b=this.parentPage.jqmData("url");return a(":jqmData(url^='"+b+"&"+a.mobile.subPageUrlKey+"')")}});a(k).bind("pagecreate create",function(b){a.mobile.listview.prototype.enhanceWithin(b.target)})})(jQuery);(function(a,c){a.widget("mobile.checkboxradio",a.mobile.widget,{options:{theme:null,initSelector:"input[type='checkbox'],input[type='radio']"},
+_create:function(){var b=this,e=this.element,f=a(e).closest("label"),d=f.length?f:a(e).closest("form,fieldset,:jqmData(role='page'),:jqmData(role='dialog')").find("label").filter("[for='"+e[0].id+"']"),g=e[0].type,f=e.jqmData("mini")||e.closest("form,fieldset").jqmData("mini"),h=g+"-on",j=g+"-off",o=e.parents(":jqmData(type='horizontal')").length?c:j,m=e.jqmData("iconpos")||e.closest("form,fieldset").jqmData("iconpos");if(!(g!=="checkbox"&&g!=="radio")){a.extend(this,{label:d,inputtype:g,checkedClass:"ui-"+
+h+(o?"":" "+a.mobile.activeBtnClass),uncheckedClass:"ui-"+j,checkedicon:"ui-icon-"+h,uncheckedicon:"ui-icon-"+j});if(!this.options.theme)this.options.theme=a.mobile.getInheritedTheme(this.element,"c");d.buttonMarkup({theme:this.options.theme,icon:o,shadow:false,mini:f,iconpos:m});f=k.createElement("div");f.className="ui-"+g;e.add(d).wrapAll(f);d.bind({vmouseover:function(b){a(this).parent().is(".ui-disabled")&&b.stopPropagation()},vclick:function(a){if(e.is(":disabled"))a.preventDefault();else return b._cacheVals(),
+e.prop("checked",g==="radio"&&true||!e.prop("checked")),e.triggerHandler("click"),b._getInputSet().not(e).prop("checked",false),b._updateAll(),false}});e.bind({vmousedown:function(){b._cacheVals()},vclick:function(){var c=a(this);c.is(":checked")?(c.prop("checked",true),b._getInputSet().not(c).prop("checked",false)):c.prop("checked",false);b._updateAll()},focus:function(){d.addClass(a.mobile.focusClass)},blur:function(){d.removeClass(a.mobile.focusClass)}});this.refresh()}},_cacheVals:function(){this._getInputSet().each(function(){a(this).jqmData("cacheVal",
+this.checked)})},_getInputSet:function(){return this.inputtype==="checkbox"?this.element:this.element.closest("form,fieldset,:jqmData(role='page')").find("input[name='"+this.element[0].name+"'][type='"+this.inputtype+"']")},_updateAll:function(){var b=this;this._getInputSet().each(function(){var c=a(this);(this.checked||b.inputtype==="checkbox")&&c.trigger("change")}).checkboxradio("refresh")},refresh:function(){var a=this.element[0],c=this.label,f=c.find(".ui-icon");a.checked?(c.addClass(this.checkedClass).removeClass(this.uncheckedClass),
+f.addClass(this.checkedicon).removeClass(this.uncheckedicon)):(c.removeClass(this.checkedClass).addClass(this.uncheckedClass),f.removeClass(this.checkedicon).addClass(this.uncheckedicon));a.disabled?this.disable():this.enable()},disable:function(){this.element.prop("disabled",true).parent().addClass("ui-disabled")},enable:function(){this.element.prop("disabled",false).parent().removeClass("ui-disabled")}});a(k).bind("pagecreate create",function(b){a.mobile.checkboxradio.prototype.enhanceWithin(b.target,
+true)})})(jQuery);(function(a,c){a.widget("mobile.button",a.mobile.widget,{options:{theme:null,icon:null,iconpos:null,inline:false,corners:true,shadow:true,iconshadow:true,initSelector:"button, [type='button'], [type='submit'], [type='reset'], [type='image']",mini:false},_create:function(){var b=this.element,e,f=this.options,d;d="";var g;if(b[0].tagName==="A")!b.hasClass("ui-btn")&&b.buttonMarkup();else{if(!this.options.theme)this.options.theme=a.mobile.getInheritedTheme(this.element,"c");~b[0].className.indexOf("ui-btn-left")&&
+(d="ui-btn-left");~b[0].className.indexOf("ui-btn-right")&&(d="ui-btn-right");e=this.button=a("<div></div>").text(b.text()||b.val()).insertBefore(b).buttonMarkup({theme:f.theme,icon:f.icon,iconpos:f.iconpos,inline:f.inline,corners:f.corners,shadow:f.shadow,iconshadow:f.iconshadow,mini:f.mini}).addClass(d).append(b.addClass("ui-btn-hidden"));f=b.attr("type");d=b.attr("name");f!=="button"&&f!=="reset"&&d&&b.bind("vclick",function(){g===c&&(g=a("<input>",{type:"hidden",name:b.attr("name"),value:b.attr("value")}).insertBefore(b),
+a(k).one("submit",function(){g.remove();g=c}))});b.bind({focus:function(){e.addClass(a.mobile.focusClass)},blur:function(){e.removeClass(a.mobile.focusClass)}});this.refresh()}},enable:function(){this.element.attr("disabled",false);this.button.removeClass("ui-disabled").attr("aria-disabled",false);return this._setOption("disabled",false)},disable:function(){this.element.attr("disabled",true);this.button.addClass("ui-disabled").attr("aria-disabled",true);return this._setOption("disabled",true)},refresh:function(){var b=
+this.element;b.prop("disabled")?this.disable():this.enable();a(this.button.data("buttonElements").text).text(b.text()||b.val())}});a(k).bind("pagecreate create",function(b){a.mobile.button.prototype.enhanceWithin(b.target,true)})})(jQuery);(function(a){a.fn.controlgroup=function(c){function b(a,b){a.removeClass("ui-btn-corner-all ui-shadow").eq(0).addClass(b[0]).end().last().addClass(b[1]).addClass("ui-controlgroup-last")}return this.each(function(){var e=a(this),f=a.extend({direction:e.jqmData("type")||
+"vertical",shadow:false,excludeInvisible:true,mini:e.jqmData("mini")},c),d=e.children("legend"),g=f.direction=="horizontal"?["ui-corner-left","ui-corner-right"]:["ui-corner-top","ui-corner-bottom"];e.find("input").first().attr("type");d.length&&(e.wrapInner("<div class='ui-controlgroup-controls'></div>"),a("<div role='heading' class='ui-controlgroup-label'>"+d.html()+"</div>").insertBefore(e.children(0)),d.remove());e.addClass("ui-corner-all ui-controlgroup ui-controlgroup-"+f.direction);b(e.find(".ui-btn"+
+(f.excludeInvisible?":visible":"")).not(".ui-slider-handle"),g);b(e.find(".ui-btn-inner"),g);f.shadow&&e.addClass("ui-shadow");f.mini&&e.addClass("ui-mini")})}})(jQuery);(function(a){a(k).bind("pagecreate create",function(c){a(c.target).find("a").jqmEnhanceable().not(".ui-btn, .ui-link-inherit, :jqmData(role='none'), :jqmData(role='nojs')").addClass("ui-link")})})(jQuery);(function(a){var c=a("meta[name=viewport]"),b=c.attr("content"),e=b+",maximum-scale=1, user-scalable=no",f=b+",maximum-scale=10, user-scalable=yes",
+d=/(user-scalable[\s]*=[\s]*no)|(maximum-scale[\s]*=[\s]*1)[$,\s]/.test(b);a.mobile.zoom=a.extend({},{enabled:!d,locked:false,disable:function(b){if(!d&&!a.mobile.zoom.locked)c.attr("content",e),a.mobile.zoom.enabled=false,a.mobile.zoom.locked=b||false},enable:function(b){if(!d&&(!a.mobile.zoom.locked||b===true))c.attr("content",f),a.mobile.zoom.enabled=true,a.mobile.zoom.locked=false},restore:function(){if(!d)c.attr("content",b),a.mobile.zoom.enabled=true}})})(jQuery);(function(a){a.widget("mobile.textinput",
+a.mobile.widget,{options:{theme:null,preventFocusZoom:/iPhone|iPad|iPod/.test(navigator.platform)&&navigator.userAgent.indexOf("AppleWebKit")>-1,initSelector:"input[type='text'], input[type='search'], :jqmData(type='search'), input[type='number'], :jqmData(type='number'), input[type='password'], input[type='email'], input[type='url'], input[type='tel'], textarea, input[type='time'], input[type='date'], input[type='month'], input[type='week'], input[type='datetime'], input[type='datetime-local'], input[type='color'], input:not([type])",
+clearSearchButtonText:"clear text"},_create:function(){var c=this.element,b=this.options,e=b.theme||a.mobile.getInheritedTheme(this.element,"c"),f=" ui-body-"+e,d=c.jqmData("mini")==true,g=d?" ui-mini":"",h,j;a("label[for='"+c.attr("id")+"']").addClass("ui-input-text");h=c.addClass("ui-input-text ui-body-"+e);typeof c[0].autocorrect!=="undefined"&&!a.support.touchOverflow&&(c[0].setAttribute("autocorrect","off"),c[0].setAttribute("autocomplete","off"));c.is("[type='search'],:jqmData(type='search')")?
+(h=c.wrap("<div class='ui-input-search ui-shadow-inset ui-btn-corner-all ui-btn-shadow ui-icon-searchfield"+f+g+"'></div>").parent(),j=a("<a href='#' class='ui-input-clear' title='"+b.clearSearchButtonText+"'>"+b.clearSearchButtonText+"</a>").bind("click",function(a){c.val("").focus().trigger("change");j.addClass("ui-input-clear-hidden");a.preventDefault()}).appendTo(h).buttonMarkup({icon:"delete",iconpos:"notext",corners:true,shadow:true,mini:d}),e=function(){setTimeout(function(){j.toggleClass("ui-input-clear-hidden",
+!c.val())},0)},e(),c.bind("paste cut keyup focus change blur",e)):c.addClass("ui-corner-all ui-shadow-inset"+f+g);c.focus(function(){h.addClass(a.mobile.focusClass)}).blur(function(){h.removeClass(a.mobile.focusClass)}).bind("focus",function(){b.preventFocusZoom&&a.mobile.zoom.disable(true)}).bind("blur",function(){b.preventFocusZoom&&a.mobile.zoom.enable(true)});if(c.is("textarea")){var o=function(){var a=c[0].scrollHeight;c[0].clientHeight<a&&c.height(a+15)},m;c.keyup(function(){clearTimeout(m);
+m=setTimeout(o,100)});a(k).one("pagechange",o);a.trim(c.val())&&a(s).load(o)}},disable:function(){(this.element.attr("disabled",true).is("[type='search'],:jqmData(type='search')")?this.element.parent():this.element).addClass("ui-disabled")},enable:function(){(this.element.attr("disabled",false).is("[type='search'],:jqmData(type='search')")?this.element.parent():this.element).removeClass("ui-disabled")}});a(k).bind("pagecreate create",function(c){a.mobile.textinput.prototype.enhanceWithin(c.target,
+true)})})(jQuery);(function(a){a.mobile.listview.prototype.options.filter=false;a.mobile.listview.prototype.options.filterPlaceholder="Filter items...";a.mobile.listview.prototype.options.filterTheme="c";a.mobile.listview.prototype.options.filterCallback=function(a,b){return a.toLowerCase().indexOf(b)===-1};a(k).delegate(":jqmData(role='listview')","listviewcreate",function(){var c=a(this),b=c.data("listview");if(b.options.filter){var e=a("<form>",{"class":"ui-listview-filter ui-bar-"+b.options.filterTheme,
+role:"search"});a("<input>",{placeholder:b.options.filterPlaceholder}).attr("data-"+a.mobile.ns+"type","search").jqmData("lastval","").bind("keyup change",function(){var e=a(this),d=this.value.toLowerCase(),g=null,g=e.jqmData("lastval")+"",h=false,j="";e.jqmData("lastval",d);g=d.length<g.length||d.indexOf(g)!==0?c.children():c.children(":not(.ui-screen-hidden)");if(d){for(var k=g.length-1;k>=0;k--)e=a(g[k]),j=e.jqmData("filtertext")||e.text(),e.is("li:jqmData(role=list-divider)")?(e.toggleClass("ui-filter-hidequeue",
+!h),h=false):b.options.filterCallback(j,d)?e.toggleClass("ui-filter-hidequeue",true):h=true;g.filter(":not(.ui-filter-hidequeue)").toggleClass("ui-screen-hidden",false);g.filter(".ui-filter-hidequeue").toggleClass("ui-screen-hidden",true).toggleClass("ui-filter-hidequeue",false)}else g.toggleClass("ui-screen-hidden",false);b._refreshCorners()}).appendTo(e).textinput();b.options.inset&&e.addClass("ui-listview-filter-inset");e.bind("submit",function(){return false}).insertBefore(c)}})})(jQuery);(function(a,
+c){a.widget("mobile.slider",a.mobile.widget,{options:{theme:null,trackTheme:null,disabled:false,initSelector:"input[type='range'], :jqmData(type='range'), :jqmData(role='slider')",mini:false},_create:function(){var b=this,e=this.element,f=a.mobile.getInheritedTheme(e,"c"),d=this.options.theme||f,f=this.options.trackTheme||f,g=e[0].nodeName.toLowerCase(),h=g=="select"?"ui-slider-switch":"",j=e.attr("id"),o=j+"-label",j=a("[for='"+j+"']").attr("id",o),m=function(){return g=="input"?parseFloat(e.val()):
+e[0].selectedIndex},p=g=="input"?parseFloat(e.attr("min")):0,l=g=="input"?parseFloat(e.attr("max")):e.find("option").length-1,r=s.parseFloat(e.attr("step")||1),n=this.options.inline||e.jqmData("inline")==true?" ui-slider-inline":"",q=this.options.mini||e.jqmData("mini")?" ui-slider-mini":"",t=k.createElement("a"),x=a(t),u=k.createElement("div"),w=a(u),v=e.jqmData("highlight")&&g!="select"?function(){var b=k.createElement("div");b.className="ui-slider-bg ui-btn-active ui-btn-corner-all";return a(b).prependTo(w)}():
+false;t.setAttribute("href","#");u.setAttribute("role","application");u.className=["ui-slider ",h," ui-btn-down-",f," ui-btn-corner-all",n,q].join("");t.className="ui-slider-handle";u.appendChild(t);x.buttonMarkup({corners:true,theme:d,shadow:true}).attr({role:"slider","aria-valuemin":p,"aria-valuemax":l,"aria-valuenow":m(),"aria-valuetext":m(),title:m(),"aria-labelledby":o});a.extend(this,{slider:w,handle:x,valuebg:v,dragging:false,beforeStart:null,userModified:false,mouseMoved:false});if(g=="select"){d=
+k.createElement("div");d.className="ui-slider-inneroffset";h=0;for(o=u.childNodes.length;h<o;h++)d.appendChild(u.childNodes[h]);u.appendChild(d);x.addClass("ui-slider-handle-snapping");u=e.find("option");d=0;for(h=u.length;d<h;d++)o=!d?"b":"a",n=!d?" ui-btn-down-"+f:" "+a.mobile.activeBtnClass,k.createElement("div"),q=k.createElement("span"),q.className=["ui-slider-label ui-slider-label-",o,n," ui-btn-corner-all"].join(""),q.setAttribute("role","img"),q.appendChild(k.createTextNode(u[d].innerHTML)),
+a(q).prependTo(w);b._labels=a(".ui-slider-label",w)}j.addClass("ui-slider");e.addClass(g==="input"?"ui-slider-input":"ui-slider-switch").change(function(){b.mouseMoved||b.refresh(m(),true)}).keyup(function(){b.refresh(m(),true,true)}).blur(function(){b.refresh(m(),true)});a(k).bind("vmousemove",function(a){if(b.dragging)return b.mouseMoved=true,g==="select"&&x.removeClass("ui-slider-handle-snapping"),b.refresh(a),b.userModified=b.beforeStart!==e[0].selectedIndex,false});w.bind("vmousedown",function(a){b.dragging=
+true;b.userModified=false;b.mouseMoved=false;if(g==="select")b.beforeStart=e[0].selectedIndex;b.refresh(a);return false}).bind("vclick",false);w.add(k).bind("vmouseup",function(){if(b.dragging)return b.dragging=false,g==="select"&&(x.addClass("ui-slider-handle-snapping"),b.mouseMoved?b.userModified?b.refresh(b.beforeStart==0?1:0):b.refresh(b.beforeStart):b.refresh(b.beforeStart==0?1:0)),b.mouseMoved=false});w.insertAfter(e);g=="select"&&this.handle.bind({focus:function(){w.addClass(a.mobile.focusClass)},
+blur:function(){w.removeClass(a.mobile.focusClass)}});this.handle.bind({vmousedown:function(){a(this).focus()},vclick:false,keydown:function(c){var d=m();if(!b.options.disabled){switch(c.keyCode){case a.mobile.keyCode.HOME:case a.mobile.keyCode.END:case a.mobile.keyCode.PAGE_UP:case a.mobile.keyCode.PAGE_DOWN:case a.mobile.keyCode.UP:case a.mobile.keyCode.RIGHT:case a.mobile.keyCode.DOWN:case a.mobile.keyCode.LEFT:if(c.preventDefault(),!b._keySliding)b._keySliding=true,a(this).addClass("ui-state-active")}switch(c.keyCode){case a.mobile.keyCode.HOME:b.refresh(p);
+break;case a.mobile.keyCode.END:b.refresh(l);break;case a.mobile.keyCode.PAGE_UP:case a.mobile.keyCode.UP:case a.mobile.keyCode.RIGHT:b.refresh(d+r);break;case a.mobile.keyCode.PAGE_DOWN:case a.mobile.keyCode.DOWN:case a.mobile.keyCode.LEFT:b.refresh(d-r)}}},keyup:function(){if(b._keySliding)b._keySliding=false,a(this).removeClass("ui-state-active")}});this.refresh(c,c,true)},refresh:function(b,c,f){(this.options.disabled||this.element.attr("disabled"))&&this.disable();var d=this.element,g=d[0].nodeName.toLowerCase(),
+h=g==="input"?parseFloat(d.attr("min")):0,j=g==="input"?parseFloat(d.attr("max")):d.find("option").length-1,k=g==="input"&&parseFloat(d.attr("step"))>0?parseFloat(d.attr("step")):1;if(typeof b==="object"){if(!this.dragging||b.pageX<this.slider.offset().left-8||b.pageX>this.slider.offset().left+this.slider.width()+8)return;b=Math.round((b.pageX-this.slider.offset().left)/this.slider.width()*100)}else b==null&&(b=g==="input"?parseFloat(d.val()||0):d[0].selectedIndex),b=(parseFloat(b)-h)/(j-h)*100;if(!isNaN(b)){b<
+0&&(b=0);b>100&&(b=100);var m=b/100*(j-h)+h,p=(m-h)%k;m-=p;Math.abs(p)*2>=k&&(m+=p>0?k:-k);m=parseFloat(m.toFixed(5));m<h&&(m=h);m>j&&(m=j);this.handle.css("left",b+"%");this.handle.attr({"aria-valuenow":g==="input"?m:d.find("option").eq(m).attr("value"),"aria-valuetext":g==="input"?m:d.find("option").eq(m).getEncodedText(),title:g==="input"?m:d.find("option").eq(m).getEncodedText()});this.valuebg&&this.valuebg.css("width",b+"%");if(this._labels){var h=this.handle.width()/this.slider.width()*100,
+l=b&&h+(100-h)*b/100,r=b===100?0:Math.min(h+100-l,100);this._labels.each(function(){var b=a(this).is(".ui-slider-label-a");a(this).width((b?l:r)+"%")})}if(!f)f=false,g==="input"?(f=d.val()!==m,d.val(m)):(f=d[0].selectedIndex!==m,d[0].selectedIndex=m),!c&&f&&d.trigger("change")}},enable:function(){this.element.attr("disabled",false);this.slider.removeClass("ui-disabled").attr("aria-disabled",false);return this._setOption("disabled",false)},disable:function(){this.element.attr("disabled",true);this.slider.addClass("ui-disabled").attr("aria-disabled",
+true);return this._setOption("disabled",true)}});a(k).bind("pagecreate create",function(b){a.mobile.slider.prototype.enhanceWithin(b.target,true)})})(jQuery);(function(a){a.widget("mobile.selectmenu",a.mobile.widget,{options:{theme:null,disabled:false,icon:"arrow-d",iconpos:"right",inline:false,corners:true,shadow:true,iconshadow:true,overlayTheme:"a",hidePlaceholderMenuItems:true,closeText:"Close",nativeMenu:true,preventFocusZoom:/iPhone|iPad|iPod/.test(navigator.platform)&&navigator.userAgent.indexOf("AppleWebKit")>
+-1,initSelector:"select:not(:jqmData(role='slider'))",mini:false},_button:function(){return a("<div/>")},_setDisabled:function(a){this.element.attr("disabled",a);this.button.attr("aria-disabled",a);return this._setOption("disabled",a)},_focusButton:function(){var a=this;setTimeout(function(){a.button.focus()},40)},_selectOptions:function(){return this.select.find("option")},_preExtension:function(){var c="";~this.element[0].className.indexOf("ui-btn-left")&&(c=" ui-btn-left");~this.element[0].className.indexOf("ui-btn-right")&&
+(c=" ui-btn-right");this.select=this.element.wrap("<div class='ui-select"+c+"'>");this.selectID=this.select.attr("id");this.label=a("label[for='"+this.selectID+"']").addClass("ui-select");this.isMultiple=this.select[0].multiple;if(!this.options.theme)this.options.theme=a.mobile.getInheritedTheme(this.select,"c")},_create:function(){this._preExtension();this._trigger("beforeCreate");this.button=this._button();var c=this,b=this.options,e=this.button.text(a(this.select[0].options.item(this.select[0].selectedIndex==
+-1?0:this.select[0].selectedIndex)).text()).insertBefore(this.select).buttonMarkup({theme:b.theme,icon:b.icon,iconpos:b.iconpos,inline:b.inline,corners:b.corners,shadow:b.shadow,iconshadow:b.iconshadow,mini:b.mini});b.nativeMenu&&s.opera&&s.opera.version&&this.select.addClass("ui-select-nativeonly");if(this.isMultiple)this.buttonCount=a("<span>").addClass("ui-li-count ui-btn-up-c ui-btn-corner-all").hide().appendTo(e.addClass("ui-li-has-count"));(b.disabled||this.element.attr("disabled"))&&this.disable();
+this.select.change(function(){c.refresh()});this.build()},build:function(){var c=this;this.select.appendTo(c.button).bind("vmousedown",function(){c.button.addClass(a.mobile.activeBtnClass)}).bind("focus",function(){c.button.addClass(a.mobile.focusClass)}).bind("blur",function(){c.button.removeClass(a.mobile.focusClass)}).bind("focus vmouseover",function(){c.button.trigger("vmouseover")}).bind("vmousemove",function(){c.button.removeClass(a.mobile.activeBtnClass)}).bind("change blur vmouseout",function(){c.button.trigger("vmouseout").removeClass(a.mobile.activeBtnClass)}).bind("change blur",
+function(){c.button.removeClass("ui-btn-down-"+c.options.theme)});c.button.bind("vmousedown",function(){c.options.preventFocusZoom&&a.mobile.zoom.disable(true)}).bind("mouseup",function(){c.options.preventFocusZoom&&a.mobile.zoom.enable(true)})},selected:function(){return this._selectOptions().filter(":selected")},selectedIndices:function(){var a=this;return this.selected().map(function(){return a._selectOptions().index(this)}).get()},setButtonText:function(){var c=this,b=this.selected();this.button.find(".ui-btn-text").text(function(){return!c.isMultiple?
+b.text():b.length?b.map(function(){return a(this).text()}).get().join(", "):c.placeholder})},setButtonCount:function(){var a=this.selected();this.isMultiple&&this.buttonCount[a.length>1?"show":"hide"]().text(a.length)},refresh:function(){this.setButtonText();this.setButtonCount()},open:a.noop,close:a.noop,disable:function(){this._setDisabled(true);this.button.addClass("ui-disabled")},enable:function(){this._setDisabled(false);this.button.removeClass("ui-disabled")}});a(k).bind("pagecreate create",
+function(c){a.mobile.selectmenu.prototype.enhanceWithin(c.target,true)})})(jQuery);(function(a){var c=function(b){var c=b.selectID,f=b.label,d=b.select.closest(".ui-page"),g=a("<div>",{"class":"ui-selectmenu-screen ui-screen-hidden"}).appendTo(d),h=b._selectOptions(),j=b.isMultiple=b.select[0].multiple,o=c+"-button",m=c+"-menu",p=a("<div data-"+a.mobile.ns+"role='dialog' data-"+a.mobile.ns+"theme='"+b.options.theme+"' data-"+a.mobile.ns+"overlay-theme='"+b.options.overlayTheme+"'><div data-"+a.mobile.ns+
+"role='header'><div class='ui-title'>"+f.getEncodedText()+"</div></div><div data-"+a.mobile.ns+"role='content'></div></div>"),l=a("<div>",{"class":"ui-selectmenu ui-selectmenu-hidden ui-overlay-shadow ui-corner-all ui-body-"+b.options.overlayTheme+" "+a.mobile.defaultDialogTransition}).insertAfter(g),r=a("<ul>",{"class":"ui-selectmenu-list",id:m,role:"listbox","aria-labelledby":o}).attr("data-"+a.mobile.ns+"theme",b.options.theme).appendTo(l),n=a("<div>",{"class":"ui-header ui-bar-"+b.options.theme}).prependTo(l),
+q=a("<h1>",{"class":"ui-title"}).appendTo(n),t;b.isMultiple&&(t=a("<a>",{text:b.options.closeText,href:"#","class":"ui-btn-left"}).attr("data-"+a.mobile.ns+"iconpos","notext").attr("data-"+a.mobile.ns+"icon","delete").appendTo(n).buttonMarkup());a.extend(b,{select:b.select,selectID:c,buttonId:o,menuId:m,thisPage:d,menuPage:p,label:f,screen:g,selectOptions:h,isMultiple:j,theme:b.options.theme,listbox:l,list:r,header:n,headerTitle:q,headerClose:t,menuPageContent:void 0,menuPageClose:void 0,placeholder:"",
+build:function(){var c=this;c.refresh();c.select.attr("tabindex","-1").focus(function(){a(this).blur();c.button.focus()});c.button.bind("vclick keydown",function(b){if(b.type=="vclick"||b.keyCode&&(b.keyCode===a.mobile.keyCode.ENTER||b.keyCode===a.mobile.keyCode.SPACE))c.open(),b.preventDefault()});c.list.attr("role","listbox").bind("focusin",function(b){a(b.target).attr("tabindex","0").trigger("vmouseover")}).bind("focusout",function(b){a(b.target).attr("tabindex","-1").trigger("vmouseout")}).delegate("li:not(.ui-disabled, .ui-li-divider)",
+"click",function(b){var d=c.select[0].selectedIndex,e=c.list.find("li:not(.ui-li-divider)").index(this),f=c._selectOptions().eq(e)[0];f.selected=c.isMultiple?!f.selected:true;c.isMultiple&&a(this).find(".ui-icon").toggleClass("ui-icon-checkbox-on",f.selected).toggleClass("ui-icon-checkbox-off",!f.selected);(c.isMultiple||d!==e)&&c.select.trigger("change");c.isMultiple||c.close();b.preventDefault()}).keydown(function(c){var d=a(c.target),e=d.closest("li");switch(c.keyCode){case 38:return c=e.prev().not(".ui-selectmenu-placeholder"),
+c.is(".ui-li-divider")&&(c=c.prev()),c.length&&(d.blur().attr("tabindex","-1"),c.addClass("ui-btn-down-"+b.options.theme).find("a").first().focus()),false;case 40:return c=e.next(),c.is(".ui-li-divider")&&(c=c.next()),c.length&&(d.blur().attr("tabindex","-1"),c.addClass("ui-btn-down-"+b.options.theme).find("a").first().focus()),false;case 13:case 32:return d.trigger("click"),false}});c.menuPage.bind("pagehide",function(){c.list.appendTo(c.listbox);c._focusButton();a.mobile._bindPageRemove.call(c.thisPage)});
+c.screen.bind("vclick",function(){c.close()});c.isMultiple&&c.headerClose.click(function(){if(c.menuType=="overlay")return c.close(),false});c.thisPage.addDependents(this.menuPage)},_isRebuildRequired:function(){var a=this.list.find("li");return this._selectOptions().text()!==a.text()},refresh:function(b){var c=this;this._selectOptions();this.selected();var d=this.selectedIndices();(b||this._isRebuildRequired())&&c._buildList();c.setButtonText();c.setButtonCount();c.list.find("li:not(.ui-li-divider)").removeClass(a.mobile.activeBtnClass).attr("aria-selected",
+false).each(function(b){a.inArray(b,d)>-1&&(b=a(this),b.attr("aria-selected",true),c.isMultiple?b.find(".ui-icon").removeClass("ui-icon-checkbox-off").addClass("ui-icon-checkbox-on"):b.is(".ui-selectmenu-placeholder")?b.next().addClass(a.mobile.activeBtnClass):b.addClass(a.mobile.activeBtnClass))})},close:function(){if(!this.options.disabled&&this.isOpen)this.menuType=="page"?s.history.back():(this.screen.addClass("ui-screen-hidden"),this.listbox.addClass("ui-selectmenu-hidden").removeAttr("style").removeClass("in"),
+this.list.appendTo(this.listbox),this._focusButton()),this.isOpen=false},open:function(){function b(){c.list.find("."+a.mobile.activeBtnClass+" a").focus()}if(!this.options.disabled){var c=this,d=a(s),e=c.list.parent(),f=e.outerHeight(),e=e.outerWidth();a(".ui-page-active");var g=d.scrollTop(),j=c.button.offset().top,h=d.height(),d=d.width();c.button.addClass(a.mobile.activeBtnClass);setTimeout(function(){c.button.removeClass(a.mobile.activeBtnClass)},300);if(f>h-80||!a.support.scrollTop){c.menuPage.appendTo(a.mobile.pageContainer).page();
+c.menuPageContent=p.find(".ui-content");c.menuPageClose=p.find(".ui-header a");c.thisPage.unbind("pagehide.remove");if(g==0&&j>h)c.thisPage.one("pagehide",function(){a(this).jqmData("lastScroll",j)});c.menuPage.one("pageshow",function(){b();c.isOpen=true});c.menuType="page";c.menuPageContent.append(c.list);c.menuPage.find("div .ui-title").text(c.label.text());a.mobile.changePage(c.menuPage,{transition:a.mobile.defaultDialogTransition})}else{c.menuType="overlay";c.screen.height(a(k).height()).removeClass("ui-screen-hidden");
+var l=j-g,m=g+h-j,n=f/2,o=parseFloat(c.list.parent().css("max-width")),f=l>f/2&&m>f/2?j+c.button.outerHeight()/2-n:l>m?g+h-f-30:g+30;e<o?g=(d-e)/2:(g=c.button.offset().left+c.button.outerWidth()/2-e/2,g<30?g=30:g+e>d&&(g=d-e-30));c.listbox.append(c.list).removeClass("ui-selectmenu-hidden").css({top:f,left:g}).addClass("in");b();c.isOpen=true}}},_buildList:function(){var b=this.options,c=this.placeholder,d=true,e=this.isMultiple?"checkbox-off":"false";this.list.empty().filter(".ui-listview").listview("destroy");
+var f=this.select.find("option"),g=f.length,j=this.select[0],h="data-"+a.mobile.ns,l=h+"option-index",m=h+"icon";h+="role";for(var n=k.createDocumentFragment(),o,p=0;p<g;p++){var r=f[p],q=a(r),s=r.parentNode,t=q.text(),D=k.createElement("a"),J=[];D.setAttribute("href","#");D.appendChild(k.createTextNode(t));s!==j&&s.nodeName.toLowerCase()==="optgroup"&&(s=s.getAttribute("label"),s!=o&&(o=k.createElement("li"),o.setAttribute(h,"list-divider"),o.setAttribute("role","option"),o.setAttribute("tabindex",
+"-1"),o.appendChild(k.createTextNode(s)),n.appendChild(o),o=s));if(d&&(!r.getAttribute("value")||t.length==0||q.jqmData("placeholder")))if(d=false,b.hidePlaceholderMenuItems&&J.push("ui-selectmenu-placeholder"),!c)c=this.placeholder=t;q=k.createElement("li");r.disabled&&(J.push("ui-disabled"),q.setAttribute("aria-disabled",true));q.setAttribute(l,p);q.setAttribute(m,e);q.className=J.join(" ");q.setAttribute("role","option");D.setAttribute("tabindex","-1");q.appendChild(D);n.appendChild(q)}this.list[0].appendChild(n);
+!this.isMultiple&&!c.length?this.header.hide():this.headerTitle.text(this.placeholder);this.list.listview()},_button:function(){return a("<a>",{href:"#",role:"button",id:this.buttonId,"aria-haspopup":"true","aria-owns":this.menuId})}})};a(k).bind("selectmenubeforecreate",function(b){b=a(b.target).data("selectmenu");b.options.nativeMenu||c(b)})})(jQuery);(function(a){a.widget("mobile.fixedtoolbar",a.mobile.widget,{options:{visibleOnPageShow:true,disablePageZoom:true,transition:"slide",fullscreen:false,
+tapToggle:true,tapToggleBlacklist:"a, input, select, textarea, .ui-header-fixed, .ui-footer-fixed",hideDuringFocus:"input, textarea, select",updatePagePadding:true,trackPersistentToolbars:true,supportBlacklist:function(){var a=s,b=navigator.userAgent,e=navigator.platform,f=b.match(/AppleWebKit\/([0-9]+)/),f=!!f&&f[1],d=b.match(/Fennec\/([0-9]+)/),d=!!d&&d[1],g=b.match(/Opera Mobi\/([0-9]+)/),h=!!g&&g[1];return(e.indexOf("iPhone")>-1||e.indexOf("iPad")>-1||e.indexOf("iPod")>-1)&&f&&f<534||a.operamini&&
+{}.toString.call(a.operamini)==="[object OperaMini]"||g&&h<7458||b.indexOf("Android")>-1&&f&&f<533||d&&d<6||"palmGetResource"in s&&f&&f<534||b.indexOf("MeeGo")>-1&&b.indexOf("NokiaBrowser/8.5.0")>-1?true:false},initSelector:":jqmData(position='fixed')"},_create:function(){var a=this.options,b=this.element,e=b.is(":jqmData(role='header')")?"header":"footer",f=b.closest(".ui-page");a.supportBlacklist()?this.destroy():(b.addClass("ui-"+e+"-fixed"),a.fullscreen?(b.addClass("ui-"+e+"-fullscreen"),f.addClass("ui-page-"+
+e+"-fullscreen")):f.addClass("ui-page-"+e+"-fixed"),this._addTransitionClass(),this._bindPageEvents(),this._bindToggleHandlers())},_addTransitionClass:function(){var a=this.options.transition;a&&a!=="none"&&(a==="slide"&&(a=this.element.is(".ui-header")?"slidedown":"slideup"),this.element.addClass(a))},_bindPageEvents:function(){var c=this,b=c.options;c.element.closest(".ui-page").bind("pagebeforeshow",function(){b.disablePageZoom&&a.mobile.zoom.disable(true);b.visibleOnPageShow||c.hide(true)}).bind("webkitAnimationStart animationstart updatelayout",
+function(){b.updatePagePadding&&c.updatePagePadding()}).bind("pageshow",function(){c.updatePagePadding();b.updatePagePadding&&a(s).bind("throttledresize."+c.widgetName,function(){c.updatePagePadding()})}).bind("pagebeforehide",function(e,f){b.disablePageZoom&&a.mobile.zoom.enable(true);b.updatePagePadding&&a(s).unbind("throttledresize."+c.widgetName);if(b.trackPersistentToolbars){var d=a(".ui-footer-fixed:jqmData(id)",this),g=a(".ui-header-fixed:jqmData(id)",this),h=d.length&&f.nextPage&&a(".ui-footer-fixed:jqmData(id='"+
+d.jqmData("id")+"')",f.nextPage),j=g.length&&f.nextPage&&a(".ui-header-fixed:jqmData(id='"+g.jqmData("id")+"')",f.nextPage),h=h||a();if(h.length||j.length)h.add(j).appendTo(a.mobile.pageContainer),f.nextPage.one("pageshow",function(){h.add(j).appendTo(this)})}})},_visible:true,updatePagePadding:function(){var a=this.element,b=a.is(".ui-header");this.options.fullscreen||a.closest(".ui-page").css("padding-"+(b?"top":"bottom"),a.outerHeight())},_useTransition:function(c){var b=this.element,e=a(s).scrollTop(),
+f=b.height(),d=b.closest(".ui-page").height(),g=a.mobile.getScreenHeight(),b=b.is(":jqmData(role='header')")?"header":"footer";return!c&&(this.options.transition&&this.options.transition!=="none"&&(b==="header"&&!this.options.fullscreen&&e>f||b==="footer"&&!this.options.fullscreen&&e+g<d-f)||this.options.fullscreen)},show:function(a){var b=this.element;this._useTransition(a)?b.removeClass("out ui-fixed-hidden").addClass("in"):b.removeClass("ui-fixed-hidden");this._visible=true},hide:function(a){var b=
+this.element,e="out"+(this.options.transition==="slide"?" reverse":"");this._useTransition(a)?b.addClass(e).removeClass("in").animationComplete(function(){b.addClass("ui-fixed-hidden").removeClass(e)}):b.addClass("ui-fixed-hidden").removeClass(e);this._visible=false},toggle:function(){this[this._visible?"hide":"show"]()},_bindToggleHandlers:function(){var c=this,b=c.options;c.element.closest(".ui-page").bind("vclick",function(e){b.tapToggle&&!a(e.target).closest(b.tapToggleBlacklist).length&&c.toggle()}).bind("focusin focusout",
+function(e){if(screen.width<500&&a(e.target).is(b.hideDuringFocus)&&!a(e.target).closest(".ui-header-fixed, .ui-footer-fixed").length)c[e.type==="focusin"&&c._visible?"hide":"show"]()})},destroy:function(){this.element.removeClass("ui-header-fixed ui-footer-fixed ui-header-fullscreen ui-footer-fullscreen in out fade slidedown slideup ui-fixed-hidden");this.element.closest(".ui-page").removeClass("ui-page-header-fixed ui-page-footer-fixed ui-page-header-fullscreen ui-page-footer-fullscreen")}});a(k).bind("pagecreate create",
+function(c){a(c.target).jqmData("fullscreen")&&a(a.mobile.fixedtoolbar.prototype.options.initSelector,c.target).not(":jqmData(fullscreen)").jqmData("fullscreen",true);a.mobile.fixedtoolbar.prototype.enhanceWithin(c.target)})})(jQuery);(function(a,c){if(/iPhone|iPad|iPod/.test(navigator.platform)&&navigator.userAgent.indexOf("AppleWebKit")>-1){var b=a.mobile.zoom,e,f,d,g,h;a(c).bind("orientationchange.iosorientationfix",b.enable).bind("devicemotion.iosorientationfix",function(a){e=a.originalEvent;
+h=e.accelerationIncludingGravity;f=Math.abs(h.x);d=Math.abs(h.y);g=Math.abs(h.z);!c.orientation&&(f>7||(g>6&&d<8||g<8&&d>6)&&f>5)?b.enabled&&b.disable():b.enabled||b.enable()})}})(jQuery,this);(function(a,c){function b(){var b=a("."+a.mobile.activeBtnClass).first();h.css({top:a.support.scrollTop&&g.scrollTop()+g.height()/2||b.length&&b.offset().top||100})}function e(){var c=h.offset(),d=g.scrollTop(),f=a.mobile.getScreenHeight();if(c.top<d||c.top-d>f)h.addClass("ui-loader-fakefix"),b(),g.unbind("scroll",
+e).bind("scroll",b)}function f(){d.removeClass("ui-mobile-rendering")}var d=a("html");a("head");var g=a(c);a(c.document).trigger("mobileinit");if(a.mobile.gradeA()){if(a.mobile.ajaxBlacklist)a.mobile.ajaxEnabled=false;d.addClass("ui-mobile ui-mobile-rendering");setTimeout(f,5E3);var h=a("<div class='ui-loader'><span class='ui-icon ui-icon-loading'></span><h1></h1></div>");a.extend(a.mobile,{showPageLoadingMsg:function(b,c,f){d.addClass("ui-loading");if(a.mobile.loadingMessage){var k=f||a.mobile.loadingMessageTextVisible;
+b=b||a.mobile.loadingMessageTheme;h.attr("class","ui-loader ui-corner-all ui-body-"+(b||"a")+" ui-loader-"+(k?"verbose":"default")+(f?" ui-loader-textonly":"")).find("h1").text(c||a.mobile.loadingMessage).end().appendTo(a.mobile.pageContainer);e();g.bind("scroll",e)}},hidePageLoadingMsg:function(){d.removeClass("ui-loading");a.mobile.loadingMessage&&h.removeClass("ui-loader-fakefix");a(c).unbind("scroll",b);a(c).unbind("scroll",e)},initializePage:function(){var b=a(":jqmData(role='page'), :jqmData(role='dialog')");
+b.length||(b=a("body").wrapInner("<div data-"+a.mobile.ns+"role='page'></div>").children(0));b.each(function(){var b=a(this);b.jqmData("url")||b.attr("data-"+a.mobile.ns+"url",b.attr("id")||location.pathname+location.search)});a.mobile.firstPage=b.first();a.mobile.pageContainer=b.first().parent().addClass("ui-mobile-viewport");g.trigger("pagecontainercreate");a.mobile.showPageLoadingMsg();f();!a.mobile.hashListeningEnabled||!a.mobile.path.stripHash(location.hash)?a.mobile.changePage(a.mobile.firstPage,
+{transition:"none",reverse:true,changeHash:false,fromHashChange:true}):g.trigger("hashchange",[true])}});a.mobile._registerInternalEvents();a(function(){c.scrollTo(0,1);a.mobile.defaultHomeScroll=!a.support.scrollTop||a(c).scrollTop()===1?0:1;a.fn.controlgroup&&a(k).bind("pagecreate create",function(b){a(":jqmData(role='controlgroup')",b.target).jqmEnhanceable().controlgroup({excludeInvisible:false})});a.mobile.autoInitializePage&&a.mobile.initializePage();g.load(a.mobile.silentScroll)})}})(jQuery,
+this)});
--- a/OrthancExplorer/libs/jquery.mobile.structure-1.1.0.min.css	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-/*! jQuery Mobile v1.1.0 db342b1f315c282692791aa870455901fdb46a55 jquerymobile.com | jquery.org/license */
-.ui-mobile,.ui-mobile body{height:99.9%}.ui-mobile fieldset,.ui-page{padding:0;margin:0}.ui-mobile a img,.ui-mobile fieldset{border-width:0}.ui-mobile-viewport{margin:0;overflow-x:visible;-webkit-text-size-adjust:none;-ms-text-size-adjust:none;-webkit-tap-highlight-color:rgba(0,0,0,0)}body.ui-mobile-viewport,div.ui-mobile-viewport{overflow-x:hidden}.ui-mobile [data-role=page],.ui-mobile [data-role=dialog],.ui-page{top:0;left:0;width:100%;min-height:100%;position:absolute;display:none;border:0}.ui-mobile .ui-page-active{display:block;overflow:visible}.ui-page{outline:0}@media screen and (orientation:portrait){.ui-mobile,.ui-mobile .ui-page{min-height:420px}}@media screen and (orientation:landscape){.ui-mobile,.ui-mobile .ui-page{min-height:300px}}.ui-loading .ui-loader{display:block}.ui-loader{display:none;z-index:9999999;position:fixed;top:50%;box-shadow:0 1px 1px -1px #fff;left:50%;border:0}.ui-loader-default{background:0;opacity:.18;width:46px;height:46px;margin-left:-23px;margin-top:-23px}.ui-loader-verbose{width:200px;opacity:.88;height:auto;margin-left:-110px;margin-top:-43px;padding:10px}.ui-loader-default h1{font-size:0;width:0;height:0;overflow:hidden}.ui-loader-verbose h1{font-size:16px;margin:0;text-align:center}.ui-loader .ui-icon{background-color:#000;display:block;margin:0;width:44px;height:44px;padding:1px;-webkit-border-radius:36px;-moz-border-radius:36px;border-radius:36px}.ui-loader-verbose .ui-icon{margin:0 auto 10px;opacity:.75}.ui-loader-textonly{padding:15px;margin-left:-115px}.ui-loader-textonly .ui-icon{display:none}.ui-loader-fakefix{position:absolute}.ui-mobile-rendering>*{visibility:hidden}.ui-bar,.ui-body{position:relative;padding:.4em 15px;overflow:hidden;display:block;clear:both}.ui-bar{font-size:16px;margin:0}.ui-bar h1,.ui-bar h2,.ui-bar h3,.ui-bar h4,.ui-bar h5,.ui-bar h6{margin:0;padding:0;font-size:16px;display:inline-block}.ui-header,.ui-footer{position:relative;border-left-width:0;border-right-width:0}.ui-header .ui-btn-left,.ui-header .ui-btn-right,.ui-footer .ui-btn-left,.ui-footer .ui-btn-right{position:absolute;top:3px}.ui-header .ui-btn-left,.ui-footer .ui-btn-left{left:5px}.ui-header .ui-btn-right,.ui-footer .ui-btn-right{right:5px}.ui-footer .ui-btn-icon-notext,.ui-header .ui-btn-icon-notext{top:6px}.ui-header .ui-title,.ui-footer .ui-title{min-height:1.1em;text-align:center;font-size:16px;display:block;margin:.6em 30% .8em;padding:0;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;outline:0!important}.ui-footer .ui-title{margin:.6em 15px .8em}.ui-content{border-width:0;overflow:visible;overflow-x:hidden;padding:15px}.ui-icon{width:18px;height:18px}.ui-nojs{position:absolute;left:-9999px}.ui-hide-label label,.ui-hidden-accessible{position:absolute!important;left:-9999px;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}.ui-mobile-viewport-transitioning,.ui-mobile-viewport-transitioning .ui-page{width:100%;height:100%;overflow:hidden}.in{-webkit-animation-timing-function:ease-out;-webkit-animation-duration:350ms;-moz-animation-timing-function:ease-out;-moz-animation-duration:350ms}.out{-webkit-animation-timing-function:ease-in;-webkit-animation-duration:225ms;-moz-animation-timing-function:ease-in;-moz-animation-duration:225}@-webkit-keyframes fadein{from{opacity:0}to{opacity:1}}@-moz-keyframes fadein{from{opacity:0}to{opacity:1}}@-webkit-keyframes fadeout{from{opacity:1}to{opacity:0}}@-moz-keyframes fadeout{from{opacity:1}to{opacity:0}}.fade.out{opacity:0;-webkit-animation-duration:125ms;-webkit-animation-name:fadeout;-moz-animation-duration:125ms;-moz-animation-name:fadeout}.fade.in{opacity:1;-webkit-animation-duration:225ms;-webkit-animation-name:fadein;-moz-animation-duration:225ms;-moz-animation-name:fadein}.pop{-webkit-transform-origin:50% 50%;-moz-transform-origin:50% 50%}.pop.in{-webkit-transform:scale(1);-moz-transform:scale(1);opacity:1;-webkit-animation-name:popin;-moz-animation-name:popin;-webkit-animation-duration:350ms;-moz-animation-duration:350ms}.pop.out{-webkit-animation-name:fadeout;-moz-animation-name:fadeout;opacity:0;-webkit-animation-duration:100ms;-moz-animation-duration:100ms}.pop.in.reverse{-webkit-animation-name:fadein;-moz-animation-name:fadein}.pop.out.reverse{-webkit-transform:scale(.8);-moz-transform:scale(.8);-webkit-animation-name:popout;-moz-animation-name:popout}@-webkit-keyframes popin{from{-webkit-transform:scale(.8);opacity:0}to{-webkit-transform:scale(1);opacity:1}}@-moz-keyframes popin{from{-moz-transform:scale(.8);opacity:0}to{-moz-transform:scale(1);opacity:1}}@-webkit-keyframes popout{from{-webkit-transform:scale(1);opacity:1}to{-webkit-transform:scale(.8);opacity:0}}@-moz-keyframes popout{from{-moz-transform:scale(1);opacity:1}to{-moz-transform:scale(.8);opacity:0}}@-webkit-keyframes slideinfromright{from{-webkit-transform:translateX(100%)}to{-webkit-transform:translateX(0)}}@-moz-keyframes slideinfromright{from{-moz-transform:translateX(100%)}to{-moz-transform:translateX(0)}}@-webkit-keyframes slideinfromleft{from{-webkit-transform:translateX(-100%)}to{-webkit-transform:translateX(0)}}@-moz-keyframes slideinfromleft{from{-moz-transform:translateX(-100%)}to{-moz-transform:translateX(0)}}@-webkit-keyframes slideouttoleft{from{-webkit-transform:translateX(0)}to{-webkit-transform:translateX(-100%)}}@-moz-keyframes slideouttoleft{from{-moz-transform:translateX(0)}to{-moz-transform:translateX(-100%)}}@-webkit-keyframes slideouttoright{from{-webkit-transform:translateX(0)}to{-webkit-transform:translateX(100%)}}@-moz-keyframes slideouttoright{from{-moz-transform:translateX(0)}to{-moz-transform:translateX(100%)}}.slide.out,.slide.in{-webkit-animation-timing-function:ease-out;-webkit-animation-duration:350ms;-moz-animation-timing-function:ease-out;-moz-animation-duration:350ms}.slide.out{-webkit-transform:translateX(-100%);-webkit-animation-name:slideouttoleft;-moz-transform:translateX(-100%);-moz-animation-name:slideouttoleft}.slide.in{-webkit-transform:translateX(0);-webkit-animation-name:slideinfromright;-moz-transform:translateX(0);-moz-animation-name:slideinfromright}.slide.out.reverse{-webkit-transform:translateX(100%);-webkit-animation-name:slideouttoright;-moz-transform:translateX(100%);-moz-animation-name:slideouttoright}.slide.in.reverse{-webkit-transform:translateX(0);-webkit-animation-name:slideinfromleft;-moz-transform:translateX(0);-moz-animation-name:slideinfromleft}.slidefade.out{-webkit-transform:translateX(-100%);-webkit-animation-name:slideouttoleft;-moz-transform:translateX(-100%);-moz-animation-name:slideouttoleft;-webkit-animation-duration:225ms;-moz-animation-duration:225ms}.slidefade.in{-webkit-transform:translateX(0);-webkit-animation-name:fadein;-moz-transform:translateX(0);-moz-animation-name:fadein;-webkit-animation-duration:200ms;-moz-animation-duration:200ms}.slidefade.out.reverse{-webkit-transform:translateX(100%);-webkit-animation-name:slideouttoright;-moz-transform:translateX(100%);-moz-animation-name:slideouttoright;-webkit-animation-duration:200ms;-moz-animation-duration:200ms}.slidefade.in.reverse{-webkit-transform:translateX(0);-webkit-animation-name:fadein;-moz-transform:translateX(0);-moz-animation-name:fadein;-webkit-animation-duration:200ms;-moz-animation-duration:200ms}.slidedown.out{-webkit-animation-name:fadeout;-moz-animation-name:fadeout;-webkit-animation-duration:100ms;-moz-animation-duration:100ms}.slidedown.in{-webkit-transform:translateY(0);-webkit-animation-name:slideinfromtop;-moz-transform:translateY(0);-moz-animation-name:slideinfromtop;-webkit-animation-duration:250ms;-moz-animation-duration:250ms}.slidedown.in.reverse{-webkit-animation-name:fadein;-moz-animation-name:fadein;-webkit-animation-duration:150ms;-moz-animation-duration:150ms}.slidedown.out.reverse{-webkit-transform:translateY(-100%);-moz-transform:translateY(-100%);-webkit-animation-name:slideouttotop;-moz-animation-name:slideouttotop;-webkit-animation-duration:200ms;-moz-animation-duration:200ms}@-webkit-keyframes slideinfromtop{from{-webkit-transform:translateY(-100%)}to{-webkit-transform:translateY(0)}}@-moz-keyframes slideinfromtop{from{-moz-transform:translateY(-100%)}to{-moz-transform:translateY(0)}}@-webkit-keyframes slideouttotop{from{-webkit-transform:translateY(0)}to{-webkit-transform:translateY(-100%)}}@-moz-keyframes slideouttotop{from{-moz-transform:translateY(0)}to{-moz-transform:translateY(-100%)}}.slideup.out{-webkit-animation-name:fadeout;-moz-animation-name:fadeout;-webkit-animation-duration:100ms;-moz-animation-duration:100ms}.slideup.in{-webkit-transform:translateY(0);-webkit-animation-name:slideinfrombottom;-moz-transform:translateY(0);-moz-animation-name:slideinfrombottom;-webkit-animation-duration:250ms;-moz-animation-duration:250ms}.slideup.in.reverse{-webkit-animation-name:fadein;-moz-animation-name:fadein;-webkit-animation-duration:150ms;-moz-animation-duration:150ms}.slideup.out.reverse{-webkit-transform:translateY(100%);-moz-transform:translateY(100%);-webkit-animation-name:slideouttobottom;-moz-animation-name:slideouttobottom;-webkit-animation-duration:200ms;-moz-animation-duration:200ms}@-webkit-keyframes slideinfrombottom{from{-webkit-transform:translateY(100%)}to{-webkit-transform:translateY(0)}}@-moz-keyframes slideinfrombottom{from{-moz-transform:translateY(100%)}to{-moz-transform:translateY(0)}}@-webkit-keyframes slideouttobottom{from{-webkit-transform:translateY(0)}to{-webkit-transform:translateY(100%)}}@-moz-keyframes slideouttobottom{from{-moz-transform:translateY(0)}to{-moz-transform:translateY(100%)}}.viewport-flip{-webkit-perspective:1000;-moz-perspective:1000;position:absolute}.flip{-webkit-backface-visibility:hidden;-webkit-transform:translateX(0);-moz-backface-visibility:hidden;-moz-transform:translateX(0)}.flip.out{-webkit-transform:rotateY(-90deg) scale(.9);-webkit-animation-name:flipouttoleft;-webkit-animation-duration:175ms;-moz-transform:rotateY(-90deg) scale(.9);-moz-animation-name:flipouttoleft;-moz-animation-duration:175ms}.flip.in{-webkit-animation-name:flipintoright;-webkit-animation-duration:225ms;-moz-animation-name:flipintoright;-moz-animation-duration:225ms}.flip.out.reverse{-webkit-transform:rotateY(90deg) scale(.9);-webkit-animation-name:flipouttoright;-moz-transform:rotateY(90deg) scale(.9);-moz-animation-name:flipouttoright}.flip.in.reverse{-webkit-animation-name:flipintoleft;-moz-animation-name:flipintoleft}@-webkit-keyframes flipouttoleft{from{-webkit-transform:rotateY(0)}to{-webkit-transform:rotateY(-90deg) scale(.9)}}@-moz-keyframes flipouttoleft{from{-moz-transform:rotateY(0)}to{-moz-transform:rotateY(-90deg) scale(.9)}}@-webkit-keyframes flipouttoright{from{-webkit-transform:rotateY(0)}to{-webkit-transform:rotateY(90deg) scale(.9)}}@-moz-keyframes flipouttoright{from{-moz-transform:rotateY(0)}to{-moz-transform:rotateY(90deg) scale(.9)}}@-webkit-keyframes flipintoleft{from{-webkit-transform:rotateY(-90deg) scale(.9)}to{-webkit-transform:rotateY(0)}}@-moz-keyframes flipintoleft{from{-moz-transform:rotateY(-90deg) scale(.9)}to{-moz-transform:rotateY(0)}}@-webkit-keyframes flipintoright{from{-webkit-transform:rotateY(90deg) scale(.9)}to{-webkit-transform:rotateY(0)}}@-moz-keyframes flipintoright{from{-moz-transform:rotateY(90deg) scale(.9)}to{-moz-transform:rotateY(0)}}.viewport-turn{-webkit-perspective:1000;-moz-perspective:1000;position:absolute}.turn{-webkit-backface-visibility:hidden;-webkit-transform:translateX(0);-webkit-transform-origin:0 0;-moz-backface-visibility:hidden;-moz-transform:translateX(0);-moz-transform-origin:0 0}.turn.out{-webkit-transform:rotateY(-90deg) scale(.9);-webkit-animation-name:flipouttoleft;-moz-transform:rotateY(-90deg) scale(.9);-moz-animation-name:flipouttoleft;-webkit-animation-duration:125ms;-moz-animation-duration:125ms}.turn.in{-webkit-animation-name:flipintoright;-moz-animation-name:flipintoright;-webkit-animation-duration:250ms;-moz-animation-duration:250ms}.turn.out.reverse{-webkit-transform:rotateY(90deg) scale(.9);-webkit-animation-name:flipouttoright;-moz-transform:rotateY(90deg) scale(.9);-moz-animation-name:flipouttoright}.turn.in.reverse{-webkit-animation-name:flipintoleft;-moz-animation-name:flipintoleft}@-webkit-keyframes flipouttoleft{from{-webkit-transform:rotateY(0)}to{-webkit-transform:rotateY(-90deg) scale(.9)}}@-moz-keyframes flipouttoleft{from{-moz-transform:rotateY(0)}to{-moz-transform:rotateY(-90deg) scale(.9)}}@-webkit-keyframes flipouttoright{from{-webkit-transform:rotateY(0)}to{-webkit-transform:rotateY(90deg) scale(.9)}}@-moz-keyframes flipouttoright{from{-moz-transform:rotateY(0)}to{-moz-transform:rotateY(90deg) scale(.9)}}@-webkit-keyframes flipintoleft{from{-webkit-transform:rotateY(-90deg) scale(.9)}to{-webkit-transform:rotateY(0)}}@-moz-keyframes flipintoleft{from{-moz-transform:rotateY(-90deg) scale(.9)}to{-moz-transform:rotateY(0)}}@-webkit-keyframes flipintoright{from{-webkit-transform:rotateY(90deg) scale(.9)}to{-webkit-transform:rotateY(0)}}@-moz-keyframes flipintoright{from{-moz-transform:rotateY(90deg) scale(.9)}to{-moz-transform:rotateY(0)}}.flow{-webkit-transform-origin:50% 30%;-moz-transform-origin:50% 30%;-webkit-box-shadow:0 0 20px rgba(0,0,0,.4);-moz-box-shadow:0 0 20px rgba(0,0,0,.4)}.ui-dialog.flow{-webkit-transform-origin:none;-moz-transform-origin:none;-webkit-box-shadow:none;-moz-box-shadow:none}.flow.out{-webkit-transform:translateX(-100%) scale(.7);-webkit-animation-name:flowouttoleft;-webkit-animation-timing-function:ease;-webkit-animation-duration:350ms;-moz-transform:translateX(-100%) scale(.7);-moz-animation-name:flowouttoleft;-moz-animation-timing-function:ease;-moz-animation-duration:350ms}.flow.in{-webkit-transform:translateX(0) scale(1);-webkit-animation-name:flowinfromright;-webkit-animation-timing-function:ease;-webkit-animation-duration:350ms;-moz-transform:translateX(0) scale(1);-moz-animation-name:flowinfromright;-moz-animation-timing-function:ease;-moz-animation-duration:350ms}.flow.out.reverse{-webkit-transform:translateX(100%);-webkit-animation-name:flowouttoright;-moz-transform:translateX(100%);-moz-animation-name:flowouttoright}.flow.in.reverse{-webkit-animation-name:flowinfromleft;-moz-animation-name:flowinfromleft}@-webkit-keyframes flowouttoleft{0%{-webkit-transform:translateX(0) scale(1)}60%,70%{-webkit-transform:translateX(0) scale(.7)}100%{-webkit-transform:translateX(-100%) scale(.7)}}@-moz-keyframes flowouttoleft{0%{-moz-transform:translateX(0) scale(1)}60%,70%{-moz-transform:translateX(0) scale(.7)}100%{-moz-transform:translateX(-100%) scale(.7)}}@-webkit-keyframes flowouttoright{0%{-webkit-transform:translateX(0) scale(1)}60%,70%{-webkit-transform:translateX(0) scale(.7)}100%{-webkit-transform:translateX(100%) scale(.7)}}@-moz-keyframes flowouttoright{0%{-moz-transform:translateX(0) scale(1)}60%,70%{-moz-transform:translateX(0) scale(.7)}100%{-moz-transform:translateX(100%) scale(.7)}}@-webkit-keyframes flowinfromleft{0%{-webkit-transform:translateX(-100%) scale(.7)}30%,40%{-webkit-transform:translateX(0) scale(.7)}100%{-webkit-transform:translateX(0) scale(1)}}@-moz-keyframes flowinfromleft{0%{-moz-transform:translateX(-100%) scale(.7)}30%,40%{-moz-transform:translateX(0) scale(.7)}100%{-moz-transform:translateX(0) scale(1)}}@-webkit-keyframes flowinfromright{0%{-webkit-transform:translateX(100%) scale(.7)}30%,40%{-webkit-transform:translateX(0) scale(.7)}100%{-webkit-transform:translateX(0) scale(1)}}@-moz-keyframes flowinfromright{0%{-moz-transform:translateX(100%) scale(.7)}30%,40%{-moz-transform:translateX(0) scale(.7)}100%{-moz-transform:translateX(0) scale(1)}}.ui-grid-a,.ui-grid-b,.ui-grid-c,.ui-grid-d{overflow:hidden}.ui-block-a,.ui-block-b,.ui-block-c,.ui-block-d,.ui-block-e{margin:0;padding:0;border:0;float:left;min-height:1px}.ui-grid-solo .ui-block-a{width:100%;float:none}.ui-grid-a .ui-block-a,.ui-grid-a .ui-block-b{width:50%}.ui-grid-a .ui-block-a{clear:left}.ui-grid-b .ui-block-a,.ui-grid-b .ui-block-b,.ui-grid-b .ui-block-c{width:33.333%}.ui-grid-b .ui-block-a{clear:left}.ui-grid-c .ui-block-a,.ui-grid-c .ui-block-b,.ui-grid-c .ui-block-c,.ui-grid-c .ui-block-d{width:25%}.ui-grid-c .ui-block-a{clear:left}.ui-grid-d .ui-block-a,.ui-grid-d .ui-block-b,.ui-grid-d .ui-block-c,.ui-grid-d .ui-block-d,.ui-grid-d .ui-block-e{width:20%}.ui-grid-d .ui-block-a{clear:left}.ui-header-fixed,.ui-footer-fixed{left:0;right:0;width:100%;position:fixed;z-index:1000}.ui-header-fixed{top:0}.ui-footer-fixed{bottom:0}.ui-header-fullscreen,.ui-footer-fullscreen{opacity:.9}.ui-page-header-fixed{padding-top:2.5em}.ui-page-footer-fixed{padding-bottom:3em}.ui-page-header-fullscreen .ui-content,.ui-page-footer-fullscreen .ui-content{padding:0}.ui-fixed-hidden{position:absolute}.ui-page-header-fullscreen .ui-fixed-hidden,.ui-page-footer-fullscreen .ui-fixed-hidden{left:-99999em}.ui-header-fixed .ui-btn,.ui-footer-fixed .ui-btn{z-index:10}.ui-navbar{overflow:hidden}.ui-navbar ul,.ui-navbar-expanded ul{list-style:none;padding:0;margin:0;position:relative;display:block;border:0}.ui-navbar-collapsed ul{float:left;width:75%;margin-right:-2px}.ui-navbar-collapsed .ui-navbar-toggle{float:left;width:25%}.ui-navbar li.ui-navbar-truncate{position:absolute;left:-9999px;top:-9999px}.ui-navbar li .ui-btn,.ui-navbar .ui-navbar-toggle .ui-btn{display:block;font-size:12px;text-align:center;margin:0;border-right-width:0;max-width:100%}.ui-navbar li .ui-btn{margin-right:-1px}.ui-navbar li .ui-btn:last-child{margin-right:0}.ui-header .ui-navbar li .ui-btn,.ui-header .ui-navbar .ui-navbar-toggle .ui-btn,.ui-footer .ui-navbar li .ui-btn,.ui-footer .ui-navbar .ui-navbar-toggle .ui-btn{border-top-width:0;border-bottom-width:0}.ui-navbar .ui-btn-inner{padding-left:2px;padding-right:2px}.ui-navbar-noicons li .ui-btn .ui-btn-inner,.ui-navbar-noicons .ui-navbar-toggle .ui-btn-inner{padding-top:.8em;padding-bottom:.9em}.ui-navbar-expanded .ui-btn{margin:0;font-size:14px}.ui-navbar-expanded .ui-btn-inner{padding-left:5px;padding-right:5px}.ui-navbar-expanded .ui-btn-icon-top .ui-btn-inner{padding:45px 5px 15px;text-align:center}.ui-navbar-expanded .ui-btn-icon-top .ui-icon{top:15px}.ui-navbar-expanded .ui-btn-icon-bottom .ui-btn-inner{padding:15px 5px 45px;text-align:center}.ui-navbar-expanded .ui-btn-icon-bottom .ui-icon{bottom:15px}.ui-navbar-expanded li .ui-btn .ui-btn-inner{min-height:2.5em}.ui-navbar-expanded .ui-navbar-noicons .ui-btn .ui-btn-inner{padding-top:1.8em;padding-bottom:1.9em}.ui-btn{display:block;text-align:center;cursor:pointer;position:relative;margin:.5em 5px;padding:0}.ui-mini{margin:.25em 5px}.ui-btn-inner{padding:.6em 20px;min-width:.75em;display:block;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;position:relative;zoom:1}.ui-btn input,.ui-btn button{z-index:2}.ui-btn-left,.ui-btn-right,.ui-btn-inline{display:inline-block}.ui-btn-block{display:block}.ui-header .ui-btn,.ui-footer .ui-btn{display:inline-block;margin:0}.ui-header .ui-btn-inner,.ui-footer .ui-btn-inner,.ui-mini .ui-btn-inner{font-size:12.5px;padding:.55em 11px .5em}.ui-header .ui-fullsize .ui-btn-inner,.ui-footer .ui-fullsize .ui-btn-inner{font-size:16px;padding:.6em 25px}.ui-btn-icon-notext{width:24px;height:24px}.ui-btn-icon-notext .ui-btn-inner{padding:0;height:100%}.ui-btn-icon-notext .ui-btn-inner .ui-icon{margin:2px 1px 2px 3px}.ui-btn-text{position:relative;z-index:1;width:100%}.ui-btn-icon-notext .ui-btn-text{position:absolute;left:-9999px}.ui-btn-icon-left .ui-btn-inner{padding-left:40px}.ui-btn-icon-right .ui-btn-inner{padding-right:40px}.ui-btn-icon-top .ui-btn-inner{padding-top:40px}.ui-btn-icon-bottom .ui-btn-inner{padding-bottom:40px}.ui-header .ui-btn-icon-left .ui-btn-inner,.ui-footer .ui-btn-icon-left .ui-btn-inner,.ui-mini .ui-btn-icon-left .ui-btn-inner{padding-left:30px}.ui-header .ui-btn-icon-right .ui-btn-inner,.ui-footer .ui-btn-icon-right .ui-btn-inner,.ui-mini .ui-btn-icon-right .ui-btn-inner{padding-right:30px}.ui-header .ui-btn-icon-top .ui-btn-inner,.ui-footer .ui-btn-icon-top .ui-btn-inner,.ui-mini .ui-btn-icon-top .ui-btn-inner{padding:30px 3px .5em 3px}.ui-header .ui-btn-icon-bottom .ui-btn-inner,.ui-footer .ui-btn-icon-bottom .ui-btn-inner,.ui-mini .ui-btn-icon-bottom .ui-btn-inner{padding:.55em 3px 30px 3px}.ui-btn-icon-notext .ui-icon{display:block;z-index:0}.ui-btn-icon-left .ui-btn-inner .ui-icon,.ui-btn-icon-right .ui-btn-inner .ui-icon{position:absolute;top:50%;margin-top:-9px}.ui-btn-icon-top .ui-btn-inner .ui-icon,.ui-btn-icon-bottom .ui-btn-inner .ui-icon{position:absolute;left:50%;margin-left:-9px}.ui-btn-icon-left .ui-icon{left:10px}.ui-btn-icon-right .ui-icon{right:10px}.ui-btn-icon-top .ui-icon{top:10px}.ui-btn-icon-bottom .ui-icon{top:auto;bottom:10px}.ui-header .ui-btn-icon-left .ui-icon,.ui-footer .ui-btn-icon-left .ui-icon,.ui-mini.ui-btn-icon-left .ui-icon,.ui-mini .ui-btn-icon-left .ui-icon{left:5px}.ui-header .ui-btn-icon-right .ui-icon,.ui-footer .ui-btn-icon-right .ui-icon,.ui-mini.ui-btn-icon-right .ui-icon,.ui-mini .ui-btn-icon-right .ui-icon{right:5px}.ui-header .ui-btn-icon-top .ui-icon,.ui-footer .ui-btn-icon-top .ui-icon,.ui-mini.ui-btn-icon-top .ui-icon,.ui-mini .ui-btn-icon-top .ui-icon{top:5px}.ui-header .ui-btn-icon-bottom .ui-icon,.ui-footer .ui-btn-icon-bottom .ui-icon,.ui-mini.ui-btn-icon-bottom .ui-icon,.ui-mini .ui-btn-icon-bottom .ui-icon{bottom:5px}.ui-btn-hidden{position:absolute;top:0;left:0;width:100%;height:100%;-webkit-appearance:button;opacity:.1;cursor:pointer;background:#fff;background:rgba(255,255,255,0);filter:Alpha(Opacity=.0001);font-size:1px;border:0;text-indent:-9999px}.ui-collapsible{margin:.5em 0}.ui-collapsible-heading{font-size:16px;display:block;margin:0 -8px;padding:0;border-width:0 0 1px 0;position:relative}.ui-collapsible-heading a{text-align:left;margin:0}.ui-collapsible-heading .ui-btn-inner,.ui-collapsible-heading .ui-btn-icon-left .ui-btn-inner{padding-left:40px}.ui-collapsible-heading .ui-btn-icon-right .ui-btn-inner{padding-left:12px;padding-right:40px}.ui-collapsible-heading .ui-btn-icon-top .ui-btn-inner,.ui-collapsible-heading .ui-btn-icon-bottom .ui-btn-inner{padding-right:40px;text-align:center}.ui-collapsible-heading a span.ui-btn{position:absolute;left:6px;top:50%;margin:-12px 0 0 0;width:20px;height:20px;padding:1px 0 1px 2px;text-indent:-9999px}.ui-collapsible-heading a span.ui-btn .ui-btn-inner{padding:10px 0}.ui-collapsible-heading a span.ui-btn .ui-icon{left:0;margin-top:-10px}.ui-collapsible-heading-status{position:absolute;top:-9999px;left:0}.ui-collapsible-content{display:block;margin:0 -8px;padding:10px 16px;border-top:0;background-image:none;font-weight:normal}.ui-collapsible-content-collapsed{display:none}.ui-collapsible-set{margin:.5em 0}.ui-collapsible-set .ui-collapsible{margin:-1px 0 0}.ui-controlgroup,fieldset.ui-controlgroup{padding:0;margin:0 0 .5em;zoom:1}.ui-bar .ui-controlgroup{margin:0 .3em}.ui-controlgroup-label{font-size:16px;line-height:1.4;font-weight:normal;margin:0 0 .4em}.ui-controlgroup-controls{display:block;width:100%}.ui-controlgroup li{list-style:none}.ui-controlgroup-vertical .ui-btn,.ui-controlgroup-vertical .ui-checkbox,.ui-controlgroup-vertical .ui-radio{margin:0;border-bottom-width:0}.ui-controlgroup-controls label.ui-select{position:absolute;left:-9999px}.ui-controlgroup-vertical .ui-controlgroup-last{border-bottom-width:1px}.ui-controlgroup-horizontal{padding:0}.ui-controlgroup-horizontal .ui-btn-inner{text-align:center}.ui-controlgroup-horizontal .ui-btn,.ui-controlgroup-horizontal .ui-select{display:inline-block;margin:0 -6px 0 0}.ui-controlgroup-horizontal .ui-checkbox,.ui-controlgroup-horizontal .ui-radio{float:left;clear:none;margin:0 -1px 0 0}.ui-controlgroup-horizontal .ui-checkbox .ui-btn,.ui-controlgroup-horizontal .ui-radio .ui-btn,.ui-controlgroup-horizontal .ui-checkbox:last-child,.ui-controlgroup-horizontal .ui-radio:last-child{margin-right:0}.ui-controlgroup-horizontal .ui-controlgroup-last{margin-right:0}.ui-controlgroup .ui-checkbox label,.ui-controlgroup .ui-radio label{font-size:16px}@media all and (min-width:450px){.ui-field-contain .ui-controlgroup-label{vertical-align:top;display:inline-block;width:20%;margin:0 2% 0 0}.ui-field-contain .ui-controlgroup-controls{width:60%;display:inline-block}.ui-field-contain .ui-controlgroup .ui-select{width:100%}.ui-field-contain .ui-controlgroup-horizontal .ui-select{width:auto}}.ui-dialog{background:none!important}.ui-dialog-contain{width:92.5%;max-width:500px;margin:10% auto 15px auto;padding:0}.ui-dialog .ui-header{margin-top:15%;border:0;overflow:hidden}.ui-dialog .ui-header,.ui-dialog .ui-content,.ui-dialog .ui-footer{display:block;position:relative;width:auto}.ui-dialog .ui-header,.ui-dialog .ui-footer{z-index:10;padding:0}.ui-dialog .ui-footer{padding:0 15px}.ui-dialog .ui-content{padding:15px}.ui-dialog{margin-top:-15px}.ui-checkbox,.ui-radio{position:relative;clear:both;margin:.2em 0 .5em;z-index:1}.ui-checkbox .ui-btn,.ui-radio .ui-btn{margin:0;text-align:left;z-index:2}.ui-checkbox .ui-btn-inner,.ui-radio .ui-btn-inner{white-space:normal}.ui-checkbox .ui-btn-icon-left .ui-btn-inner,.ui-radio .ui-btn-icon-left .ui-btn-inner{padding-left:45px}.ui-checkbox .ui-mini.ui-btn-icon-left .ui-btn-inner,.ui-radio .ui-mini.ui-btn-icon-left .ui-btn-inner{padding-left:36px}.ui-checkbox .ui-btn-icon-right .ui-btn-inner,.ui-radio .ui-btn-icon-right .ui-btn-inner{padding-right:45px}.ui-checkbox .ui-mini.ui-btn-icon-right .ui-btn-inner,.ui-radio .ui-mini.ui-btn-icon-right .ui-btn-inner{padding-right:36px}.ui-checkbox .ui-btn-icon-top .ui-btn-inner,.ui-radio .ui-btn-icon-top .ui-btn-inner{padding-right:0;padding-left:0;text-align:center}.ui-checkbox .ui-btn-icon-bottom .ui-btn-inner,.ui-radio .ui-btn-icon-bottom .ui-btn-inner{padding-right:0;padding-left:0;text-align:center}.ui-checkbox .ui-icon,.ui-radio .ui-icon{top:1.1em}.ui-checkbox .ui-btn-icon-left .ui-icon,.ui-radio .ui-btn-icon-left .ui-icon{left:15px}.ui-checkbox .ui-mini.ui-btn-icon-left .ui-icon,.ui-radio .ui-mini.ui-btn-icon-left .ui-icon{left:9px}.ui-checkbox .ui-btn-icon-right .ui-icon,.ui-radio .ui-btn-icon-right .ui-icon{right:15px}.ui-checkbox .ui-mini.ui-btn-icon-right .ui-icon,.ui-radio .ui-mini.ui-btn-icon-right .ui-icon{right:9px}.ui-checkbox .ui-btn-icon-top .ui-icon,.ui-radio .ui-btn-icon-top .ui-icon{top:10px}.ui-checkbox .ui-btn-icon-bottom .ui-icon,.ui-radio .ui-btn-icon-bottom .ui-icon{top:auto;bottom:10px}.ui-checkbox .ui-btn-icon-right .ui-icon,.ui-radio .ui-btn-icon-right .ui-icon{right:15px}.ui-checkbox .ui-mini.ui-btn-icon-right .ui-icon,.ui-radio .ui-mini.ui-btn-icon-right .ui-icon{right:9px}.ui-checkbox input,.ui-radio input{position:absolute;left:20px;top:50%;width:10px;height:10px;margin:-5px 0 0 0;outline:0!important;z-index:1}.ui-field-contain,fieldset.ui-field-contain{padding:.8em 0;margin:0;border-width:0 0 1px 0;overflow:visible}.ui-field-contain:first-child{border-top-width:0}.ui-header .ui-field-contain-left,.ui-header .ui-field-contain-right{position:absolute;top:0;width:25%}.ui-header .ui-field-contain-left{left:1em}.ui-header .ui-field-contain-right{right:1em}@media all and (min-width:450px){.ui-field-contain,.ui-mobile fieldset.ui-field-contain{border-width:0;padding:0;margin:1em 0}}.ui-select{display:block;position:relative}.ui-select select{position:absolute;left:-9999px;top:-9999px}.ui-select .ui-btn{overflow:hidden;opacity:1;margin:0}.ui-select .ui-btn select{cursor:pointer;-webkit-appearance:button;left:0;top:0;width:100%;min-height:1.5em;min-height:100%;height:3em;max-height:100%;opacity:0;-ms-filter:"alpha(opacity=0)";filter:alpha(opacity=0);z-index:2}.ui-select .ui-disabled{opacity:.3}@-moz-document url-prefix(){.ui-select .ui-btn select{opacity:.0001}}.ui-select .ui-btn select.ui-select-nativeonly{opacity:1;text-indent:0}.ui-select .ui-btn-icon-right .ui-btn-inner{padding-right:45px}.ui-select .ui-btn-icon-right .ui-icon{right:15px}.ui-select .ui-mini.ui-btn-icon-right .ui-icon{right:7px}label.ui-select{font-size:16px;line-height:1.4;font-weight:normal;margin:0 0 .3em;display:block}.ui-select .ui-btn-text,.ui-selectmenu .ui-btn-text{display:block;min-height:1em;overflow:hidden!important}.ui-select .ui-btn-text{text-overflow:ellipsis}.ui-selectmenu{position:absolute;padding:0;z-index:1100!important;width:80%;max-width:350px;padding:6px}.ui-selectmenu .ui-listview{margin:0}.ui-selectmenu .ui-btn.ui-li-divider{cursor:default}.ui-selectmenu-hidden{top:-9999px;left:-9999px}.ui-selectmenu-screen{position:absolute;top:0;left:0;width:100%;height:100%;z-index:99}.ui-screen-hidden,.ui-selectmenu-list .ui-li .ui-icon{display:none}.ui-selectmenu-list .ui-li .ui-icon{display:block}.ui-li.ui-selectmenu-placeholder{display:none}.ui-selectmenu .ui-header .ui-title{margin:.6em 46px .8em}@media all and (min-width:450px){.ui-field-contain label.ui-select{vertical-align:top;display:inline-block;width:20%;margin:0 2% 0 0}.ui-field-contain .ui-select{width:60%;display:inline-block}}.ui-selectmenu .ui-header h1:after{content:'.';visibility:hidden}label.ui-input-text{font-size:16px;line-height:1.4;display:block;font-weight:normal;margin:0 0 .3em}input.ui-input-text,textarea.ui-input-text{background-image:none;padding:.4em;line-height:1.4;font-size:16px;display:block;width:97%;outline:0}.ui-header input.ui-input-text,.ui-footer input.ui-input-text{margin-left:1.25%;padding:.4em 1%;width:95.5%}input.ui-input-text{-webkit-appearance:none}textarea.ui-input-text{height:50px;-webkit-transition:height 200ms linear;-moz-transition:height 200ms linear;-o-transition:height 200ms linear;transition:height 200ms linear}.ui-input-search{padding:0 30px;background-image:none;position:relative}.ui-icon-searchfield:after{position:absolute;left:7px;top:50%;margin-top:-9px;content:"";width:18px;height:18px;opacity:.5}.ui-input-search input.ui-input-text{border:0;width:98%;padding:.4em 0;margin:0;display:block;background:transparent none;outline:0!important}.ui-input-search .ui-input-clear{position:absolute;right:0;top:50%;margin-top:-13px}.ui-mini .ui-input-clear{right:-3px}.ui-input-search .ui-input-clear-hidden{display:none}input.ui-mini,.ui-mini input,textarea.ui-mini{font-size:14px}textarea.ui-mini{height:45px}@media all and (min-width:450px){.ui-field-contain label.ui-input-text{vertical-align:top;display:inline-block;width:20%;margin:0 2% 0 0}.ui-field-contain input.ui-input-text,.ui-field-contain textarea.ui-input-text,.ui-field-contain .ui-input-search{width:60%;display:inline-block}.ui-field-contain .ui-input-search{width:50%}.ui-hide-label input.ui-input-text,.ui-hide-label textarea.ui-input-text,.ui-hide-label .ui-input-search{padding:.4em;width:97%}.ui-input-search input.ui-input-text{width:98%}}.ui-listview{margin:0;counter-reset:listnumbering}.ui-content .ui-listview{margin:-15px}.ui-content .ui-listview-inset{margin:1em 0}.ui-listview,.ui-li{list-style:none;padding:0}.ui-li,.ui-li.ui-field-contain{display:block;margin:0;position:relative;overflow:visible;text-align:left;border-width:0;border-top-width:1px}.ui-li .ui-btn-text a.ui-link-inherit{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.ui-li-divider,.ui-li-static{padding:.5em 15px;font-size:14px;font-weight:bold}.ui-li-divider{counter-reset:listnumbering}ol.ui-listview .ui-link-inherit:before,ol.ui-listview .ui-li-static:before,.ui-li-dec{font-size:.8em;display:inline-block;padding-right:.3em;font-weight:normal;counter-increment:listnumbering;content:counter(listnumbering) ". "}ol.ui-listview .ui-li-jsnumbering:before{content:""!important}.ui-listview-inset .ui-li{border-right-width:1px;border-left-width:1px}.ui-li:last-child,.ui-li.ui-field-contain:last-child{border-bottom-width:1px}.ui-li>.ui-btn-inner{display:block;position:relative;padding:0}.ui-li .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li{padding:.7em 15px .7em 15px;display:block}.ui-li-has-thumb .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-thumb{min-height:60px;padding-left:100px}.ui-li-has-icon .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-icon{min-height:20px;padding-left:40px}.ui-li-has-count .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-count{padding-right:45px}.ui-li-has-arrow .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-arrow{padding-right:30px}.ui-li-has-arrow.ui-li-has-count .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-arrow.ui-li-has-count{padding-right:75px}.ui-li-has-count .ui-btn-text{padding-right:15px}.ui-li-heading{font-size:16px;font-weight:bold;display:block;margin:.6em 0;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.ui-li-desc{font-size:12px;font-weight:normal;display:block;margin:-.5em 0 .6em;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.ui-li-thumb,.ui-listview .ui-li-icon{position:absolute;left:1px;top:0;max-height:80px;max-width:80px}.ui-listview .ui-li-icon{max-height:40px;max-width:40px;left:10px;top:.9em}.ui-li-thumb,.ui-listview .ui-li-icon,.ui-li-content{float:left;margin-right:10px}.ui-li-aside{float:right;width:50%;text-align:right;margin:.3em 0}@media all and (min-width:480px){.ui-li-aside{width:45%}}.ui-li-divider{cursor:default}.ui-li-has-alt .ui-btn-inner a.ui-link-inherit,.ui-li-static.ui-li-has-alt{padding-right:95px}.ui-li-has-count .ui-li-count{position:absolute;font-size:11px;font-weight:bold;padding:.2em .5em;top:50%;margin-top:-.9em;right:48px}.ui-li-divider .ui-li-count,.ui-li-static .ui-li-count{right:10px}.ui-li-has-alt .ui-li-count{right:55px}.ui-li-link-alt{position:absolute;width:40px;height:100%;border-width:0;border-left-width:1px;top:0;right:0;margin:0;padding:0;z-index:2}.ui-li-link-alt .ui-btn{overflow:hidden;position:absolute;right:8px;top:50%;margin:-11px 0 0 0;border-bottom-width:1px;z-index:-1}.ui-li-link-alt .ui-btn-inner{padding:0;height:100%;position:absolute;width:100%;top:0;left:0}.ui-li-link-alt .ui-btn .ui-icon{right:50%;margin-right:-9px}.ui-listview * .ui-btn-inner>.ui-btn>.ui-btn-inner{border-top:0}.ui-listview-filter{border-width:0;overflow:hidden;margin:-15px -15px 15px -15px}.ui-listview-filter .ui-input-search{margin:5px;width:auto;display:block}.ui-listview-filter-inset{margin:-15px -5px -15px -5px;background:transparent}.ui-li.ui-screen-hidden{display:none}@media only screen and (min-device-width:768px) and (max-device-width:1024px){.ui-li .ui-btn-text{overflow:visible}}label.ui-slider{font-size:16px;line-height:1.4;font-weight:normal;margin:0 0 .3em;display:block}input.ui-slider-input,.ui-field-contain input.ui-slider-input{display:inline-block;width:50px}select.ui-slider-switch{display:none}div.ui-slider{position:relative;display:inline-block;overflow:visible;height:15px;padding:0;margin:0 2% 0 20px;top:4px;width:65%}div.ui-slider-mini{height:12px;margin-left:10px}div.ui-slider-bg{border:0;height:100%;padding-right:8px}.ui-controlgroup a.ui-slider-handle,a.ui-slider-handle{position:absolute;z-index:1;top:50%;width:28px;height:28px;margin-top:-15px;margin-left:-15px;outline:0}a.ui-slider-handle .ui-btn-inner{padding:0;height:100%}div.ui-slider-mini a.ui-slider-handle{height:14px;width:14px;margin:-8px 0 0 -7px}div.ui-slider-mini a.ui-slider-handle .ui-btn-inner{height:30px;width:30px;padding:0;margin:-9px 0 0 -9px}@media all and (min-width:450px){.ui-field-contain label.ui-slider{vertical-align:top;display:inline-block;width:20%;margin:0 2% 0 0}.ui-field-contain div.ui-slider{width:43%}.ui-field-contain div.ui-slider-switch{width:5.5em}}div.ui-slider-switch{height:32px;margin-left:0;width:5.8em}a.ui-slider-handle-snapping{-webkit-transition:left 70ms linear;-moz-transition:left 70ms linear}div.ui-slider-switch .ui-slider-handle{margin-top:1px}.ui-slider-inneroffset{margin:0 16px;position:relative;z-index:1}div.ui-slider-switch.ui-slider-mini{width:5em;height:29px}div.ui-slider-switch.ui-slider-mini .ui-slider-inneroffset{margin:0 15px 0 14px}div.ui-slider-switch.ui-slider-mini .ui-slider-handle{width:25px;height:25px;margin:1px 0 0 -13px}div.ui-slider-switch.ui-slider-mini a.ui-slider-handle .ui-btn-inner{height:30px;width:30px;padding:0;margin:0}span.ui-slider-label{position:absolute;text-align:center;width:100%;overflow:hidden;font-size:16px;top:0;line-height:2;min-height:100%;border-width:0;white-space:nowrap}.ui-slider-mini span.ui-slider-label{font-size:14px}span.ui-slider-label-a{z-index:1;left:0;text-indent:-1.5em}span.ui-slider-label-b{z-index:0;right:0;text-indent:1.5em}.ui-slider-inline{width:120px;display:inline-block}
\ No newline at end of file
--- a/OrthancExplorer/libs/jquery.mobile.theme-1.1.0.min.css	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-/*! jQuery Mobile v1.1.0 db342b1f315c282692791aa870455901fdb46a55 jquerymobile.com | jquery.org/license */
-.ui-bar-a{border:1px solid #333;background:#111;color:#fff;font-weight:bold;text-shadow:0 -1px 1px #000;background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#111));background-image:-webkit-linear-gradient(#3c3c3c,#111);background-image:-moz-linear-gradient(#3c3c3c,#111);background-image:-ms-linear-gradient(#3c3c3c,#111);background-image:-o-linear-gradient(#3c3c3c,#111);background-image:linear-gradient(#3c3c3c,#111)}.ui-bar-a,.ui-bar-a input,.ui-bar-a select,.ui-bar-a textarea,.ui-bar-a button{font-family:Helvetica,Arial,sans-serif}.ui-bar-a .ui-link-inherit{color:#fff}.ui-bar-a .ui-link{color:#7cc4e7;font-weight:bold}.ui-bar-a .ui-link:hover{color:#2489ce}.ui-bar-a .ui-link:active{color:#2489ce}.ui-bar-a .ui-link:visited{color:#2489ce}.ui-body-a,.ui-overlay-a{border:1px solid #444;background:#222;color:#fff;text-shadow:0 1px 1px #111;font-weight:normal;background-image:-webkit-gradient(linear,left top,left bottom,from(#444),to(#222));background-image:-webkit-linear-gradient(#444,#222);background-image:-moz-linear-gradient(#444,#222);background-image:-ms-linear-gradient(#444,#222);background-image:-o-linear-gradient(#444,#222);background-image:linear-gradient(#444,#222)}.ui-overlay-a{background-image:none;border-width:0}.ui-body-a,.ui-body-a input,.ui-body-a select,.ui-body-a textarea,.ui-body-a button{font-family:Helvetica,Arial,sans-serif}.ui-body-a .ui-link-inherit{color:#fff}.ui-body-a .ui-link{color:#2489ce;font-weight:bold}.ui-body-a .ui-link:hover{color:#2489ce}.ui-body-a .ui-link:active{color:#2489ce}.ui-body-a .ui-link:visited{color:#2489ce}.ui-btn-up-a{border:1px solid #111;background:#333;font-weight:bold;color:#fff;text-shadow:0 1px 1px #111;background-image:-webkit-gradient(linear,left top,left bottom,from(#444),to(#2d2d2d));background-image:-webkit-linear-gradient(#444,#2d2d2d);background-image:-moz-linear-gradient(#444,#2d2d2d);background-image:-ms-linear-gradient(#444,#2d2d2d);background-image:-o-linear-gradient(#444,#2d2d2d);background-image:linear-gradient(#444,#2d2d2d)}.ui-btn-up-a a.ui-link-inherit{color:#fff}.ui-btn-hover-a{border:1px solid #000;background:#444;font-weight:bold;color:#fff;text-shadow:0 1px 1px #111;background-image:-webkit-gradient(linear,left top,left bottom,from(#555),to(#383838));background-image:-webkit-linear-gradient(#555,#383838);background-image:-moz-linear-gradient(#555,#383838);background-image:-ms-linear-gradient(#555,#383838);background-image:-o-linear-gradient(#555,#383838);background-image:linear-gradient(#555,#383838)}.ui-btn-hover-a a.ui-link-inherit{color:#fff}.ui-btn-down-a{border:1px solid #000;background:#222;font-weight:bold;color:#fff;text-shadow:0 1px 1px #111;background-image:-webkit-gradient(linear,left top,left bottom,from(#202020),to(#2c2c2c));background-image:-webkit-linear-gradient(#202020,#2c2c2c);background-image:-moz-linear-gradient(#202020,#2c2c2c);background-image:-ms-linear-gradient(#202020,#2c2c2c);background-image:-o-linear-gradient(#202020,#2c2c2c);background-image:linear-gradient(#202020,#2c2c2c)}.ui-btn-down-a a.ui-link-inherit{color:#fff}.ui-btn-up-a,.ui-btn-hover-a,.ui-btn-down-a{font-family:Helvetica,Arial,sans-serif;text-decoration:none}.ui-bar-b{border:1px solid #456f9a;background:#5e87b0;color:#fff;font-weight:bold;text-shadow:0 1px 1px #3e6790;background-image:-webkit-gradient(linear,left top,left bottom,from(#6facd5),to(#497bae));background-image:-webkit-linear-gradient(#6facd5,#497bae);background-image:-moz-linear-gradient(#6facd5,#497bae);background-image:-ms-linear-gradient(#6facd5,#497bae);background-image:-o-linear-gradient(#6facd5,#497bae);background-image:linear-gradient(#6facd5,#497bae)}.ui-bar-b,.ui-bar-b input,.ui-bar-b select,.ui-bar-b textarea,.ui-bar-b button{font-family:Helvetica,Arial,sans-serif}.ui-bar-b .ui-link-inherit{color:#fff}.ui-bar-b .ui-link{color:#ddf0f8;font-weight:bold}.ui-bar-b .ui-link:hover{color:#ddf0f8}.ui-bar-b .ui-link:active{color:#ddf0f8}.ui-bar-b .ui-link:visited{color:#ddf0f8}.ui-body-b,.ui-overlay-b{border:1px solid #999;background:#f3f3f3;color:#222;text-shadow:0 1px 0 #fff;font-weight:normal;background-image:-webkit-gradient(linear,left top,left bottom,from(#ddd),to(#ccc));background-image:-webkit-linear-gradient(#ddd,#ccc);background-image:-moz-linear-gradient(#ddd,#ccc);background-image:-ms-linear-gradient(#ddd,#ccc);background-image:-o-linear-gradient(#ddd,#ccc);background-image:linear-gradient(#ddd,#ccc)}.ui-overlay-b{background-image:none;border-width:0}.ui-body-b,.ui-body-b input,.ui-body-b select,.ui-body-b textarea,.ui-body-b button{font-family:Helvetica,Arial,sans-serif}.ui-body-b .ui-link-inherit{color:#333}.ui-body-b .ui-link{color:#2489ce;font-weight:bold}.ui-body-b .ui-link:hover{color:#2489ce}.ui-body-b .ui-link:active{color:#2489ce}.ui-body-b .ui-link:visited{color:#2489ce}.ui-btn-up-b{border:1px solid #044062;background:#396b9e;font-weight:bold;color:#fff;text-shadow:0 1px 1px #194b7e;background-image:-webkit-gradient(linear,left top,left bottom,from(#5f9cc5),to(#396b9e));background-image:-webkit-linear-gradient(#5f9cc5,#396b9e);background-image:-moz-linear-gradient(#5f9cc5,#396b9e);background-image:-ms-linear-gradient(#5f9cc5,#396b9e);background-image:-o-linear-gradient(#5f9cc5,#396b9e);background-image:linear-gradient(#5f9cc5,#396b9e)}.ui-btn-up-b a.ui-link-inherit{color:#fff}.ui-btn-hover-b{border:1px solid #00415e;background:#4b88b6;font-weight:bold;color:#fff;text-shadow:0 1px 1px #194b7e;background-image:-webkit-gradient(linear,left top,left bottom,from(#6facd5),to(#4272a4));background-image:-webkit-linear-gradient(#6facd5,#4272a4);background-image:-moz-linear-gradient(#6facd5,#4272a4);background-image:-ms-linear-gradient(#6facd5,#4272a4);background-image:-o-linear-gradient(#6facd5,#4272a4);background-image:linear-gradient(#6facd5,#4272a4)}.ui-btn-hover-b a.ui-link-inherit{color:#fff}.ui-btn-down-b{border:1px solid #225377;background:#4e89c5;font-weight:bold;color:#fff;text-shadow:0 1px 1px #194b7e;background-image:-webkit-gradient(linear,left top,left bottom,from(#295b8e),to(#3e79b5));background-image:-webkit-linear-gradient(#295b8e,#3e79b5);background-image:-moz-linear-gradient(#295b8e,#3e79b5);background-image:-ms-linear-gradient(#295b8e,#3e79b5);background-image:-o-linear-gradient(#295b8e,#3e79b5);background-image:linear-gradient(#295b8e,#3e79b5)}.ui-btn-down-b a.ui-link-inherit{color:#fff}.ui-btn-up-b,.ui-btn-hover-b,.ui-btn-down-b{font-family:Helvetica,Arial,sans-serif;text-decoration:none}.ui-bar-c{border:1px solid #b3b3b3;background:#eee;color:#3e3e3e;font-weight:bold;text-shadow:0 1px 1px #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#f0f0f0),to(#ddd));background-image:-webkit-linear-gradient(#f0f0f0,#ddd);background-image:-moz-linear-gradient(#f0f0f0,#ddd);background-image:-ms-linear-gradient(#f0f0f0,#ddd);background-image:-o-linear-gradient(#f0f0f0,#ddd);background-image:linear-gradient(#f0f0f0,#ddd)}.ui-bar-c .ui-link-inherit{color:#3e3e3e}.ui-bar-c .ui-link{color:#7cc4e7;font-weight:bold}.ui-bar-c .ui-link:hover{color:#2489ce}.ui-bar-c .ui-link:active{color:#2489ce}.ui-bar-c .ui-link:visited{color:#2489ce}.ui-bar-c,.ui-bar-c input,.ui-bar-c select,.ui-bar-c textarea,.ui-bar-c button{font-family:Helvetica,Arial,sans-serif}.ui-body-c,.ui-overlay-c{border:1px solid #aaa;color:#333;text-shadow:0 1px 0 #fff;background:#f9f9f9;background-image:-webkit-gradient(linear,left top,left bottom,from(#f9f9f9),to(#eee));background-image:-webkit-linear-gradient(#f9f9f9,#eee);background-image:-moz-linear-gradient(#f9f9f9,#eee);background-image:-ms-linear-gradient(#f9f9f9,#eee);background-image:-o-linear-gradient(#f9f9f9,#eee);background-image:linear-gradient(#f9f9f9,#eee)}.ui-overlay-c{background-image:none;border-width:0}.ui-body-c,.ui-body-c input,.ui-body-c select,.ui-body-c textarea,.ui-body-c button{font-family:Helvetica,Arial,sans-serif}.ui-body-c .ui-link-inherit{color:#333}.ui-body-c .ui-link{color:#2489ce;font-weight:bold}.ui-body-c .ui-link:hover{color:#2489ce}.ui-body-c .ui-link:active{color:#2489ce}.ui-body-c .ui-link:visited{color:#2489ce}.ui-btn-up-c{border:1px solid #ccc;background:#eee;font-weight:bold;color:#222;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f1f1f1));background-image:-webkit-linear-gradient(#fff,#f1f1f1);background-image:-moz-linear-gradient(#fff,#f1f1f1);background-image:-ms-linear-gradient(#fff,#f1f1f1);background-image:-o-linear-gradient(#fff,#f1f1f1);background-image:linear-gradient(#fff,#f1f1f1)}.ui-btn-up-c a.ui-link-inherit{color:#2f3e46}.ui-btn-hover-c{border:1px solid #bbb;background:#dfdfdf;font-weight:bold;color:#222;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#f6f6f6),to(#e0e0e0));background-image:-webkit-linear-gradient(#f9f9f9,#e0e0e0);background-image:-moz-linear-gradient(#f6f6f6,#e0e0e0);background-image:-ms-linear-gradient(#f6f6f6,#e0e0e0);background-image:-o-linear-gradient(#f6f6f6,#e0e0e0);background-image:linear-gradient(#f6f6f6,#e0e0e0)}.ui-btn-hover-c a.ui-link-inherit{color:#2f3e46}.ui-btn-down-c{border:1px solid #bbb;background:#d6d6d6;font-weight:bold;color:#222;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#d0d0d0),to(#dfdfdf));background-image:-webkit-linear-gradient(#d0d0d0,#dfdfdf);background-image:-moz-linear-gradient(#d0d0d0,#dfdfdf);background-image:-ms-linear-gradient(#d0d0d0,#dfdfdf);background-image:-o-linear-gradient(#d0d0d0,#dfdfdf);background-image:linear-gradient(#d0d0d0,#dfdfdf)}.ui-btn-down-c a.ui-link-inherit{color:#2f3e46}.ui-btn-up-c,.ui-btn-hover-c,.ui-btn-down-c{font-family:Helvetica,Arial,sans-serif;text-decoration:none}.ui-bar-d{border:1px solid #bbb;background:#bbb;color:#333;text-shadow:0 1px 0 #eee;background-image:-webkit-gradient(linear,left top,left bottom,from(#ddd),to(#bbb));background-image:-webkit-linear-gradient(#ddd,#bbb);background-image:-moz-linear-gradient(#ddd,#bbb);background-image:-ms-linear-gradient(#ddd,#bbb);background-image:-o-linear-gradient(#ddd,#bbb);background-image:linear-gradient(#ddd,#bbb)}.ui-bar-d,.ui-bar-d input,.ui-bar-d select,.ui-bar-d textarea,.ui-bar-d button{font-family:Helvetica,Arial,sans-serif}.ui-bar-d .ui-link-inherit{color:#333}.ui-bar-d .ui-link{color:#2489ce;font-weight:bold}.ui-bar-d .ui-link:hover{color:#2489ce}.ui-bar-d .ui-link:active{color:#2489ce}.ui-bar-d .ui-link:visited{color:#2489ce}.ui-body-d,.ui-overlay-d{border:1px solid #bbb;color:#333;text-shadow:0 1px 0 #fff;background:#fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#fff));background-image:-webkit-linear-gradient(#fff,#fff);background-image:-moz-linear-gradient(#fff,#fff);background-image:-ms-linear-gradient(#fff,#fff);background-image:-o-linear-gradient(#fff,#fff);background-image:linear-gradient(#fff,#fff)}.ui-overlay-d{background-image:none;border-width:0}.ui-body-d,.ui-body-d input,.ui-body-d select,.ui-body-d textarea,.ui-body-d button{font-family:Helvetica,Arial,sans-serif}.ui-body-d .ui-link-inherit{color:#333}.ui-body-d .ui-link{color:#2489ce;font-weight:bold}.ui-body-d .ui-link:hover{color:#2489ce}.ui-body-d .ui-link:active{color:#2489ce}.ui-body-d .ui-link:visited{color:#2489ce}.ui-btn-up-d{border:1px solid #bbb;background:#fff;font-weight:bold;color:#333;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fafafa),to(#f6f6f6));background-image:-webkit-linear-gradient(#fafafa,#f6f6f6);background-image:-moz-linear-gradient(#fafafa,#f6f6f6);background-image:-ms-linear-gradient(#fafafa,#f6f6f6);background-image:-o-linear-gradient(#fafafa,#f6f6f6);background-image:linear-gradient(#fafafa,#f6f6f6)}.ui-btn-up-d a.ui-link-inherit{color:#333}.ui-btn-hover-d{border:1px solid #aaa;background:#eee;font-weight:bold;color:#333;cursor:pointer;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#eee),to(#fff));background-image:-webkit-linear-gradient(#eee,#fff);background-image:-moz-linear-gradient(#eee,#fff);background-image:-ms-linear-gradient(#eee,#fff);background-image:-o-linear-gradient(#eee,#fff);background-image:linear-gradient(#eee,#fff)}.ui-btn-hover-d a.ui-link-inherit{color:#333}.ui-btn-down-d{border:1px solid #aaa;background:#eee;font-weight:bold;color:#333;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#e5e5e5),to(#f2f2f2));background-image:-webkit-linear-gradient(#e5e5e5,#f2f2f2);background-image:-moz-linear-gradient(#e5e5e5,#f2f2f2);background-image:-ms-linear-gradient(#e5e5e5,#f2f2f2);background-image:-o-linear-gradient(#e5e5e5,#f2f2f2);background-image:linear-gradient(#e5e5e5,#f2f2f2)}.ui-btn-down-d a.ui-link-inherit{color:#333}.ui-btn-up-d,.ui-btn-hover-d,.ui-btn-down-d{font-family:Helvetica,Arial,sans-serif;text-decoration:none}.ui-bar-e{border:1px solid #f7c942;background:#fadb4e;color:#333;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fceda7),to(#fbef7e));background-image:-webkit-linear-gradient(#fceda7,#fbef7e);background-image:-moz-linear-gradient(#fceda7,#fbef7e);background-image:-ms-linear-gradient(#fceda7,#fbef7e);background-image:-o-linear-gradient(#fceda7,#fbef7e);background-image:linear-gradient(#fceda7,#fbef7e)}.ui-bar-e,.ui-bar-e input,.ui-bar-e select,.ui-bar-e textarea,.ui-bar-e button{font-family:Helvetica,Arial,sans-serif}.ui-bar-e .ui-link-inherit{color:#333}.ui-bar-e .ui-link{color:#2489ce;font-weight:bold}.ui-bar-e .ui-link:hover{color:#2489ce}.ui-bar-e .ui-link:active{color:#2489ce}.ui-bar-e .ui-link:visited{color:#2489ce}.ui-body-e,.ui-overlay-e{border:1px solid #f7c942;color:#222;text-shadow:0 1px 0 #fff;background:#fff9df;background-image:-webkit-gradient(linear,left top,left bottom,from(#fffadf),to(#fff3a5));background-image:-webkit-linear-gradient(#fffadf,#fff3a5);background-image:-moz-linear-gradient(#fffadf,#fff3a5);background-image:-ms-linear-gradient(#fffadf,#fff3a5);background-image:-o-linear-gradient(#fffadf,#fff3a5);background-image:linear-gradient(#fffadf,#fff3a5)}.ui-overlay-e{background-image:none;border-width:0}.ui-body-e,.ui-body-e input,.ui-body-e select,.ui-body-e textarea,.ui-body-e button{font-family:Helvetica,Arial,sans-serif}.ui-body-e .ui-link-inherit{color:#333}.ui-body-e .ui-link{color:#2489ce;font-weight:bold}.ui-body-e .ui-link:hover{color:#2489ce}.ui-body-e .ui-link:active{color:#2489ce}.ui-body-e .ui-link:visited{color:#2489ce}.ui-btn-up-e{border:1px solid #f4c63f;background:#fadb4e;font-weight:bold;color:#222;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#ffefaa),to(#ffe155));background-image:-webkit-linear-gradient(#ffefaa,#ffe155);background-image:-moz-linear-gradient(#ffefaa,#ffe155);background-image:-ms-linear-gradient(#ffefaa,#ffe155);background-image:-o-linear-gradient(#ffefaa,#ffe155);background-image:linear-gradient(#ffefaa,#ffe155)}.ui-btn-up-e a.ui-link-inherit{color:#222}.ui-btn-hover-e{border:1px solid #f2c43d;background:#fbe26f;font-weight:bold;color:#111;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#fff5ba),to(#fbdd52));background-image:-webkit-linear-gradient(#fff5ba,#fbdd52);background-image:-moz-linear-gradient(#fff5ba,#fbdd52);background-image:-ms-linear-gradient(#fff5ba,#fbdd52);background-image:-o-linear-gradient(#fff5ba,#fbdd52);background-image:linear-gradient(#fff5ba,#fbdd52)}.ui-btn-hover-e a.ui-link-inherit{color:#333}.ui-btn-down-e{border:1px solid #f2c43d;background:#fceda7;font-weight:bold;color:#111;text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left top,left bottom,from(#f8d94c),to(#fadb4e));background-image:-webkit-linear-gradient(#f8d94c,#fadb4e);background-image:-moz-linear-gradient(#f8d94c,#fadb4e);background-image:-ms-linear-gradient(#f8d94c,#fadb4e);background-image:-o-linear-gradient(#f8d94c,#fadb4e);background-image:linear-gradient(#f8d94c,#fadb4e)}.ui-btn-down-e a.ui-link-inherit{color:#333}.ui-btn-up-e,.ui-btn-hover-e,.ui-btn-down-e{font-family:Helvetica,Arial,sans-serif;text-decoration:none}a.ui-link-inherit{text-decoration:none!important}.ui-btn-active{border:1px solid #2373a5;background:#5393c5;font-weight:bold;color:#fff;cursor:pointer;text-shadow:0 1px 1px #3373a5;text-decoration:none;background-image:-webkit-gradient(linear,left top,left bottom,from(#5393c5),to(#6facd5));background-image:-webkit-linear-gradient(#5393c5,#6facd5);background-image:-moz-linear-gradient(#5393c5,#6facd5);background-image:-ms-linear-gradient(#5393c5,#6facd5);background-image:-o-linear-gradient(#5393c5,#6facd5);background-image:linear-gradient(#5393c5,#6facd5);font-family:Helvetica,Arial,sans-serif}.ui-btn-active a.ui-link-inherit{color:#fff}.ui-btn-inner{border-top:1px solid #fff;border-color:rgba(255,255,255,.3)}.ui-corner-tl{-moz-border-radius-topleft:.6em;-webkit-border-top-left-radius:.6em;border-top-left-radius:.6em}.ui-corner-tr{-moz-border-radius-topright:.6em;-webkit-border-top-right-radius:.6em;border-top-right-radius:.6em}.ui-corner-bl{-moz-border-radius-bottomleft:.6em;-webkit-border-bottom-left-radius:.6em;border-bottom-left-radius:.6em}.ui-corner-br{-moz-border-radius-bottomright:.6em;-webkit-border-bottom-right-radius:.6em;border-bottom-right-radius:.6em}.ui-corner-top{-moz-border-radius-topleft:.6em;-webkit-border-top-left-radius:.6em;border-top-left-radius:.6em;-moz-border-radius-topright:.6em;-webkit-border-top-right-radius:.6em;border-top-right-radius:.6em}.ui-corner-bottom{-moz-border-radius-bottomleft:.6em;-webkit-border-bottom-left-radius:.6em;border-bottom-left-radius:.6em;-moz-border-radius-bottomright:.6em;-webkit-border-bottom-right-radius:.6em;border-bottom-right-radius:.6em}.ui-corner-right{-moz-border-radius-topright:.6em;-webkit-border-top-right-radius:.6em;border-top-right-radius:.6em;-moz-border-radius-bottomright:.6em;-webkit-border-bottom-right-radius:.6em;border-bottom-right-radius:.6em}.ui-corner-left{-moz-border-radius-topleft:.6em;-webkit-border-top-left-radius:.6em;border-top-left-radius:.6em;-moz-border-radius-bottomleft:.6em;-webkit-border-bottom-left-radius:.6em;border-bottom-left-radius:.6em}.ui-corner-all{-moz-border-radius:.6em;-webkit-border-radius:.6em;border-radius:.6em}.ui-corner-none{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.ui-br{border-bottom:#828282;border-bottom:rgba(130,130,130,.3);border-bottom-width:1px;border-bottom-style:solid}.ui-disabled{opacity:.3}.ui-disabled,.ui-disabled a{cursor:default!important;pointer-events:none}.ui-disabled .ui-btn-text{-ms-filter:"alpha(opacity=30)";filter:alpha(opacity=30);zoom:1}.ui-icon,.ui-icon-searchfield:after{background:#666;background:rgba(0,0,0,.4);background-image:url(images/icons-18-white.png);background-repeat:no-repeat;-moz-border-radius:9px;-webkit-border-radius:9px;border-radius:9px}.ui-icon-alt{background:#fff;background:rgba(255,255,255,.3);background-image:url(images/icons-18-black.png);background-repeat:no-repeat}@media only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min--moz-device-pixel-ratio:1.5),only screen and (min-resolution:240dpi){.ui-icon-plus,.ui-icon-minus,.ui-icon-delete,.ui-icon-arrow-r,.ui-icon-arrow-l,.ui-icon-arrow-u,.ui-icon-arrow-d,.ui-icon-check,.ui-icon-gear,.ui-icon-refresh,.ui-icon-forward,.ui-icon-back,.ui-icon-grid,.ui-icon-star,.ui-icon-alert,.ui-icon-info,.ui-icon-home,.ui-icon-search,.ui-icon-searchfield:after,.ui-icon-checkbox-off,.ui-icon-checkbox-on,.ui-icon-radio-off,.ui-icon-radio-on{background-image:url(images/icons-36-white.png);-moz-background-size:776px 18px;-o-background-size:776px 18px;-webkit-background-size:776px 18px;background-size:776px 18px}.ui-icon-alt{background-image:url(images/icons-36-black.png)}}.ui-icon-plus{background-position:-0 50%}.ui-icon-minus{background-position:-36px 50%}.ui-icon-delete{background-position:-72px 50%}.ui-icon-arrow-r{background-position:-108px 50%}.ui-icon-arrow-l{background-position:-144px 50%}.ui-icon-arrow-u{background-position:-180px 50%}.ui-icon-arrow-d{background-position:-216px 50%}.ui-icon-check{background-position:-252px 50%}.ui-icon-gear{background-position:-288px 50%}.ui-icon-refresh{background-position:-324px 50%}.ui-icon-forward{background-position:-360px 50%}.ui-icon-back{background-position:-396px 50%}.ui-icon-grid{background-position:-432px 50%}.ui-icon-star{background-position:-468px 50%}.ui-icon-alert{background-position:-504px 50%}.ui-icon-info{background-position:-540px 50%}.ui-icon-home{background-position:-576px 50%}.ui-icon-search,.ui-icon-searchfield:after{background-position:-612px 50%}.ui-icon-checkbox-off{background-position:-684px 50%}.ui-icon-checkbox-on{background-position:-648px 50%}.ui-icon-radio-off{background-position:-756px 50%}.ui-icon-radio-on{background-position:-720px 50%}.ui-checkbox .ui-icon{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}.ui-icon-checkbox-off,.ui-icon-radio-off{background-color:transparent}.ui-checkbox-on .ui-icon,.ui-radio-on .ui-icon{background-color:#4596ce}.ui-icon-loading{background:url(images/ajax-loader.gif);background-size:46px 46px}.ui-btn-corner-tl{-moz-border-radius-topleft:1em;-webkit-border-top-left-radius:1em;border-top-left-radius:1em}.ui-btn-corner-tr{-moz-border-radius-topright:1em;-webkit-border-top-right-radius:1em;border-top-right-radius:1em}.ui-btn-corner-bl{-moz-border-radius-bottomleft:1em;-webkit-border-bottom-left-radius:1em;border-bottom-left-radius:1em}.ui-btn-corner-br{-moz-border-radius-bottomright:1em;-webkit-border-bottom-right-radius:1em;border-bottom-right-radius:1em}.ui-btn-corner-top{-moz-border-radius-topleft:1em;-webkit-border-top-left-radius:1em;border-top-left-radius:1em;-moz-border-radius-topright:1em;-webkit-border-top-right-radius:1em;border-top-right-radius:1em}.ui-btn-corner-bottom{-moz-border-radius-bottomleft:1em;-webkit-border-bottom-left-radius:1em;border-bottom-left-radius:1em;-moz-border-radius-bottomright:1em;-webkit-border-bottom-right-radius:1em;border-bottom-right-radius:1em}.ui-btn-corner-right{-moz-border-radius-topright:1em;-webkit-border-top-right-radius:1em;border-top-right-radius:1em;-moz-border-radius-bottomright:1em;-webkit-border-bottom-right-radius:1em;border-bottom-right-radius:1em}.ui-btn-corner-left{-moz-border-radius-topleft:1em;-webkit-border-top-left-radius:1em;border-top-left-radius:1em;-moz-border-radius-bottomleft:1em;-webkit-border-bottom-left-radius:1em;border-bottom-left-radius:1em}.ui-btn-corner-all{-moz-border-radius:1em;-webkit-border-radius:1em;border-radius:1em}.ui-corner-tl,.ui-corner-tr,.ui-corner-bl,.ui-corner-br,.ui-corner-top,.ui-corner-bottom,.ui-corner-right,.ui-corner-left,.ui-corner-all,.ui-btn-corner-tl,.ui-btn-corner-tr,.ui-btn-corner-bl,.ui-btn-corner-br,.ui-btn-corner-top,.ui-btn-corner-bottom,.ui-btn-corner-right,.ui-btn-corner-left,.ui-btn-corner-all{-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.ui-overlay{background:#666;opacity:.5;filter:Alpha(Opacity=50);position:absolute;width:100%;height:100%}.ui-overlay-shadow{-moz-box-shadow:0 0 12px rgba(0,0,0,.6);-webkit-box-shadow:0 0 12px rgba(0,0,0,.6);box-shadow:0 0 12px rgba(0,0,0,.6)}.ui-shadow{-moz-box-shadow:0 1px 4px rgba(0,0,0,.3);-webkit-box-shadow:0 1px 4px rgba(0,0,0,.3);box-shadow:0 1px 4px rgba(0,0,0,.3)}.ui-bar-a .ui-shadow,.ui-bar-b .ui-shadow,.ui-bar-c .ui-shadow{-moz-box-shadow:0 1px 0 rgba(255,255,255,.3);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.3);box-shadow:0 1px 0 rgba(255,255,255,.3)}.ui-shadow-inset{-moz-box-shadow:inset 0 1px 4px rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 4px rgba(0,0,0,.2);box-shadow:inset 0 1px 4px rgba(0,0,0,.2)}.ui-icon-shadow{-moz-box-shadow:0 1px 0 rgba(255,255,255,.4);-webkit-box-shadow:0 1px 0 rgba(255,255,255,.4);box-shadow:0 1px 0 rgba(255,255,255,.4)}.ui-btn:focus{outline:0}.ui-focus,.ui-btn:focus{-moz-box-shadow:0 0 12px #387bbe;-webkit-box-shadow:0 0 12px #387bbe;box-shadow:0 0 12px #387bbe}.ui-mobile-nosupport-boxshadow *{-moz-box-shadow:none!important;-webkit-box-shadow:none!important;box-shadow:none!important}.ui-mobile-nosupport-boxshadow .ui-focus,.ui-mobile-nosupport-boxshadow .ui-btn:focus{outline-width:1px;outline-style:dotted}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancExplorer/query-retrieve.js	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,294 @@
+function JavascriptDateToDicom(date)
+{
+  var s = date.toISOString();
+  return s.substring(0, 4) + s.substring(5, 7) + s.substring(8, 10);
+}
+
+function GenerateDicomDate(days)
+{
+  var today = new Date();
+  var other = new Date(today);
+  other.setDate(today.getDate() + days);
+  return JavascriptDateToDicom(other);
+}
+
+
+$('#query-retrieve').live('pagebeforeshow', function() {
+  $.ajax({
+    url: '../modalities',
+    dataType: 'json',
+    async: false,
+    cache: false,
+    success: function(modalities) {
+      var target = $('#qr-server');
+      $('option', target).remove();
+
+      for (var i = 0; i < modalities.length; i++) {
+        var option = $('<option>').text(modalities[i]);
+        target.append(option);
+      }
+
+      target.selectmenu('refresh');
+    }
+  });
+
+  var target = $('#qr-date');
+  $('option', target).remove();
+  target.append($('<option>').attr('value', '*').text('Any date'));
+  target.append($('<option>').attr('value', GenerateDicomDate(0)).text('Today'));
+  target.append($('<option>').attr('value', GenerateDicomDate(-1)).text('Yesterday'));
+  target.append($('<option>').attr('value', GenerateDicomDate(-7) + '-').text('Last 7 days'));
+  target.append($('<option>').attr('value', GenerateDicomDate(-31) + '-').text('Last 31 days'));
+  target.append($('<option>').attr('value', GenerateDicomDate(-31 * 3) + '-').text('Last 3 months'));
+  target.append($('<option>').attr('value', GenerateDicomDate(-365) + '-').text('Last year'));
+  target.selectmenu('refresh');
+});
+
+
+$('#qr-echo').live('click', function() {
+  var server = $('#qr-server').val();
+  var message = 'Error: The C-Echo has failed!';
+
+  $.ajax({
+    url: '../modalities/' + server + '/echo',
+    type: 'POST', 
+    cache: false,
+    async: false,
+    success: function() {
+      message = 'The C-Echo has succeeded!';
+    }
+  });
+
+  $('<div>').simpledialog2({
+    mode: 'button',
+    headerText: 'Echo result',
+    headerClose: true,
+    buttonPrompt: message,
+    animate: false,
+    buttons : {
+      'OK': { click: function () { } }
+    }
+  });
+
+  return false;
+});
+
+
+$('#qr-submit').live('click', function() {
+  var query = {
+    'Level' : 'Study',
+    'Query' : {
+      'AccessionNumber' : '*',
+      'PatientBirthDate' : '*',
+      'PatientID' : '*',
+      'PatientName' : '*',
+      'PatientSex' : '*',
+      'SpecificCharacterSet' : 'ISO_IR 192',  // UTF-8
+      'StudyDate' : $('#qr-date').val(),
+      'StudyDescription' : '*'
+    }
+  };
+
+  var field = $('#qr-fields input:checked').val();
+  query['Query'][field] = $('#qr-value').val().toUpperCase();
+
+  var modalities = '';
+  $('#qr-modalities input:checked').each(function() {
+    var s = $(this).attr('name');
+    if (modalities == '*')
+      modalities = s;
+    else
+      modalities += '\\' + s;
+  });
+
+  if (modalities.length > 0) {
+    query['Query']['ModalitiesInStudy'] = modalities;
+  }
+
+
+  var server = $('#qr-server').val();
+  $.ajax({
+    url: '../modalities/' + server + '/query',
+    type: 'POST', 
+    data: JSON.stringify(query),
+    dataType: 'json',
+    async: false,
+    error: function() {
+      alert('Error during query (C-Find)');
+    },
+    success: function(result) {
+      window.location.assign('explorer.html#query-retrieve-2?server=' + server + '&uuid=' + result['ID']);
+    }
+  });
+
+  return false;
+});
+
+
+
+function Retrieve(url)
+{
+  $.ajax({
+    url: '../system',
+    dataType: 'json',
+    async: false,
+    success: function(system) {
+      $('<div>').simpledialog2({
+        mode: 'button',
+        headerText: 'Target',
+        headerClose: true,
+        buttonPrompt: 'Enter Application Entity Title (AET):',
+        buttonInput: true,
+        buttonInputDefault: system['DicomAet'],
+        buttons : {
+          'OK': {
+            click: function () { 
+              var aet = $.mobile.sdLastInput;
+              if (aet.length == 0)
+                aet = system['DicomAet'];
+
+              $.ajax({
+                url: url,
+                type: 'POST',
+                async: true,  // Necessary to block UI
+                dataType: 'text',
+                data: aet,
+                beforeSend: function() {
+                  $.blockUI({ message: $('#info-retrieve') });
+                },
+                complete: function(s) {
+                  $.unblockUI();
+                },
+                error: function() {
+                  alert('Error during retrieve');
+                }
+              });
+            }
+          }
+        }
+      });
+    }
+  });
+}
+
+
+
+
+$('#query-retrieve-2').live('pagebeforeshow', function() {
+  if ($.mobile.pageData) {
+    var uri = '../queries/' + $.mobile.pageData.uuid + '/answers';
+
+    $.ajax({
+      url: uri,
+      dataType: 'json',
+      async: false,
+      success: function(answers) {
+        var target = $('#query-retrieve-2 ul');
+        $('li', target).remove();
+
+        for (var i = 0; i < answers.length; i++) {
+          $.ajax({
+            url: uri + '/' + answers[i] + '/content?simplify',
+            dataType: 'json',
+            async: false,
+            success: function(study) {
+              var series = '#query-retrieve-3?server=' + $.mobile.pageData.server + '&uuid=' + study['StudyInstanceUID'];
+              var info = $('<a>').attr('href', series).html(
+                ('<h3>{0} - {1}</h3>' + 
+                 '<p>Accession number: <b>{2}</b></p>' +
+                 '<p>Birth date: <b>{3}</b></p>' +
+                 '<p>Patient sex: <b>{4}</b></p>' +
+                 '<p>Study description: <b>{5}</b></p>' +
+                 '<p>Study date: <b>{6}</b></p>').format(
+                   study['PatientID'],
+                   study['PatientName'],
+                   study['AccessionNumber'],
+                   FormatDicomDate(study['PatientBirthDate']),
+                   study['PatientSex'],
+                   study['StudyDescription'],
+                   FormatDicomDate(study['StudyDate'])));
+
+              var studyUri = uri + '/' + answers[i] + '/retrieve';
+              var retrieve = $('<a>').text('Retrieve').click(function() {
+                Retrieve(studyUri);
+              });
+
+              target.append($('<li>').append(info).append(retrieve));
+            }
+          });
+        }
+
+        target.listview('refresh');
+      }
+    });
+  }
+});
+
+
+$('#query-retrieve-3').live('pagebeforeshow', function() {
+  if ($.mobile.pageData) {
+    var query = {
+      'Level' : 'Series',
+      'Query' : {
+        'Modality' : '*',
+        'ProtocolName' : '*',
+        'SeriesDescription' : '*',
+        'SeriesInstanceUID' : '*',
+        'StudyInstanceUID' : $.mobile.pageData.uuid
+      }
+    };
+
+    $.ajax({
+      url: '../modalities/' + $.mobile.pageData.server + '/query',
+      type: 'POST', 
+      data: JSON.stringify(query),
+      dataType: 'json',
+      async: false,
+      error: function() {
+        alert('Error during query (C-Find)');
+      },
+      success: function(answer) {
+        var uri = '../queries/' + answer['ID'] + '/answers';
+
+        $.ajax({
+          url: uri,
+          dataType: 'json',
+          async: false,
+          success: function(answers) {
+            
+            var target = $('#query-retrieve-3 ul');
+            $('li', target).remove();
+
+            for (var i = 0; i < answers.length; i++) {
+              $.ajax({
+                url: uri + '/' + answers[i] + '/content?simplify',
+                dataType: 'json',
+                async: false,
+                success: function(series) {
+                  var info = $('<a>').html(
+                    ('<h3>{0}</h3>'  + 
+                     '<p>Modality: <b>{1}</b></p>' +
+                     '<p>Protocol name: <b>{2}</b></p>'
+                    ).format(
+                      series['SeriesDescription'],
+                      series['Modality'],
+                      series['ProtocolName']
+                    ));
+
+                  var seriesUri = uri + '/' + answers[i] + '/retrieve';
+                  var retrieve = $('<a>').text('Retrieve').click(function() {
+                    Retrieve(seriesUri);
+                  });
+
+                  target.append($('<li>').append(info).append(retrieve));
+                }
+              });
+            }
+
+            target.listview('refresh');
+          }
+        });
+      }
+    });
+  }
+});
--- a/OrthancServer/DatabaseWrapper.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DatabaseWrapper.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -34,10 +34,10 @@
 #include "DatabaseWrapper.h"
 
 #include "../Core/DicomFormat/DicomArray.h"
+#include "../Core/Logging.h"
 #include "../Core/Uuid.h"
 #include "EmbeddedResources.h"
 
-#include <glog/logging.h>
 #include <stdio.h>
 #include <boost/lexical_cast.hpp>
 
@@ -49,10 +49,10 @@
     class SignalFileDeleted : public SQLite::IScalarFunction
     {
     private:
-      IServerIndexListener& listener_;
+      IDatabaseListener& listener_;
 
     public:
-      SignalFileDeleted(IServerIndexListener& listener) :
+      SignalFileDeleted(IDatabaseListener& listener) :
         listener_(listener)
       {
       }
@@ -96,10 +96,10 @@
     class SignalResourceDeleted : public SQLite::IScalarFunction
     {
     private:
-      IServerIndexListener& listener_;
+      IDatabaseListener& listener_;
 
     public:
-      SignalResourceDeleted(IServerIndexListener& listener) :
+      SignalResourceDeleted(IDatabaseListener& listener) :
         listener_(listener)
       {
       }
@@ -742,14 +742,27 @@
     }
   }
 
-  static void UpgradeDatabase(SQLite::Connection& db,
-                              EmbeddedResources::FileResourceId script)
+  void DatabaseWrapper::GetAllPublicIds(std::list<std::string>& target,
+                                        ResourceType resourceType,
+                                        size_t since,
+                                        size_t limit)
   {
-    std::string upgrade;
-    EmbeddedResources::GetFileResource(upgrade, script);
-    db.BeginTransaction();
-    db.Execute(upgrade);
-    db.CommitTransaction();    
+    if (limit == 0)
+    {
+      target.clear();
+      return;
+    }
+
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Resources WHERE resourceType=? LIMIT ? OFFSET ?");
+    s.BindInt(0, resourceType);
+    s.BindInt64(1, limit);
+    s.BindInt64(2, since);
+
+    target.clear();
+    while (s.Step())
+    {
+      target.push_back(s.ColumnString(0));
+    }
   }
 
 
@@ -784,52 +797,26 @@
     }
 
     // Check the version of the database
-    std::string version;
-    if (!LookupGlobalProperty(version, GlobalProperty_DatabaseSchemaVersion))
+    std::string tmp;
+    if (!LookupGlobalProperty(tmp, GlobalProperty_DatabaseSchemaVersion))
     {
-      version = "Unknown";
+      tmp = "Unknown";
     }
 
     bool ok = false;
     try
     {
-      LOG(INFO) << "Version of the Orthanc database: " << version;
-      unsigned int v = boost::lexical_cast<unsigned int>(version);
-
-      /**
-       * History of the database versions:
-       *  - Orthanc before Orthanc 0.3.0 (inclusive) had no version
-       *  - Version 2: only Orthanc 0.3.1
-       *  - Version 3: from Orthanc 0.3.2 to Orthanc 0.7.2 (inclusive)
-       *  - Version 4: from Orthanc 0.7.3 to Orthanc 0.8.4 (inclusive)
-       *  - Version 5: from Orthanc 0.8.5 (inclusive)
-       **/
-
-      // This version of Orthanc is only compatible with versions 3, 4 and 5 of the DB schema
-      ok = (v == 3 || v == 4 || v == 5);
-
-      if (v == 3)
-      {
-        LOG(WARNING) << "Upgrading database version from 3 to 4";
-        UpgradeDatabase(db_, EmbeddedResources::UPGRADE_DATABASE_3_TO_4);
-        v = 4;
-      }
-
-      if (v == 4)
-      {
-        LOG(WARNING) << "Upgrading database version from 4 to 5";
-        UpgradeDatabase(db_, EmbeddedResources::UPGRADE_DATABASE_4_TO_5);
-        v = 5;
-      }
+      LOG(INFO) << "Version of the Orthanc database: " << tmp;
+      version_ = boost::lexical_cast<unsigned int>(tmp);
+      ok = true;
     }
     catch (boost::bad_lexical_cast&)
     {
-      ok = false;
     }
 
     if (!ok)
     {
-      LOG(ERROR) << "Incompatible version of the Orthanc database: " << version;
+      LOG(ERROR) << "Incompatible version of the Orthanc database: " << tmp;
       throw OrthancException(ErrorCode_IncompatibleDatabaseVersion);
     }
 
@@ -837,7 +824,51 @@
     db_.Register(signalRemainingAncestor_);
   }
 
-  void DatabaseWrapper::SetListener(IServerIndexListener& listener)
+
+  static void ExecuteUpgradeScript(SQLite::Connection& db,
+                                   EmbeddedResources::FileResourceId script)
+  {
+    std::string upgrade;
+    EmbeddedResources::GetFileResource(upgrade, script);
+    db.BeginTransaction();
+    db.Execute(upgrade);
+    db.CommitTransaction();    
+  }
+
+
+  void DatabaseWrapper::Upgrade(unsigned int targetVersion,
+                                IStorageArea& storageArea)
+  {
+    if (targetVersion != 5)
+    {
+      throw OrthancException(ErrorCode_IncompatibleDatabaseVersion);
+    }
+
+    // This version of Orthanc is only compatible with versions 3, 4 and 5 of the DB schema
+    if (version_ != 3 &&
+        version_ != 4 &&
+        version_ != 5)
+    {
+      throw OrthancException(ErrorCode_IncompatibleDatabaseVersion);
+    }
+
+    if (version_ == 3)
+    {
+      LOG(WARNING) << "Upgrading database version from 3 to 4";
+      ExecuteUpgradeScript(db_, EmbeddedResources::UPGRADE_DATABASE_3_TO_4);
+      version_ = 4;
+    }
+
+    if (version_ == 4)
+    {
+      LOG(WARNING) << "Upgrading database version from 4 to 5";
+      ExecuteUpgradeScript(db_, EmbeddedResources::UPGRADE_DATABASE_4_TO_5);
+      version_ = 5;
+    }
+  }
+
+
+  void DatabaseWrapper::SetListener(IDatabaseListener& listener)
   {
     listener_ = &listener;
     db_.Register(new Internals::SignalFileDeleted(listener));
--- a/OrthancServer/DatabaseWrapper.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DatabaseWrapper.h	Wed Sep 30 13:23:31 2015 +0200
@@ -52,9 +52,10 @@
   class DatabaseWrapper : public IDatabaseWrapper
   {
   private:
-    IServerIndexListener* listener_;
+    IDatabaseListener* listener_;
     SQLite::Connection db_;
     Internals::SignalRemainingAncestor* signalRemainingAncestor_;
+    unsigned int version_;
 
     void Open();
 
@@ -75,7 +76,7 @@
 
     DatabaseWrapper();
 
-    virtual void SetListener(IServerIndexListener& listener);
+    virtual void SetListener(IDatabaseListener& listener);
 
     virtual void SetGlobalProperty(GlobalProperty property,
                                    const std::string& value);
@@ -170,6 +171,11 @@
     virtual void GetAllPublicIds(std::list<std::string>& target,
                                  ResourceType resourceType);
 
+    virtual void GetAllPublicIds(std::list<std::string>& target,
+                                 ResourceType resourceType,
+                                 size_t since,
+                                 size_t limit);
+
     virtual bool SelectPatientToRecycle(int64_t& internalId);
 
     virtual bool SelectPatientToRecycle(int64_t& internalId,
@@ -217,6 +223,13 @@
     virtual void GetAllMetadata(std::map<MetadataType, std::string>& target,
                                 int64_t id);
 
+    virtual unsigned int GetDatabaseVersion()
+    {
+      return version_;
+    }
+
+    virtual void Upgrade(unsigned int targetVersion,
+                         IStorageArea& storageArea);
 
 
 
--- a/OrthancServer/DicomDirWriter.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DicomDirWriter.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -103,6 +103,7 @@
 #include "FromDcmtkBridge.h"
 #include "ToDcmtkBridge.h"
 
+#include "../Core/Logging.h"
 #include "../Core/OrthancException.h"
 #include "../Core/Uuid.h"
 
@@ -118,7 +119,6 @@
 #include "dcmtk/dcmdata/dcvrtm.h"     /* for class DcmTime */
 
 #include <memory>
-#include <glog/logging.h>
 
 namespace Orthanc
 {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/DicomFindQuery.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,389 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+
+#include "PrecompiledHeadersServer.h"
+#include "DicomFindQuery.h"
+
+#include "FromDcmtkBridge.h"
+
+#include <boost/regex.hpp> 
+
+
+namespace Orthanc
+{
+  class DicomFindQuery::ValueConstraint : public DicomFindQuery::IConstraint
+  {
+  private:
+    bool          isCaseSensitive_;
+    std::string   expected_;
+
+  public:
+    ValueConstraint(const std::string& value,
+                    bool caseSensitive) :
+      isCaseSensitive_(caseSensitive),
+      expected_(value)
+    {
+    }
+
+    const std::string& GetValue() const
+    {
+      return expected_;
+    }
+
+    virtual bool IsExactConstraint() const
+    {
+      return isCaseSensitive_;
+    }
+
+    virtual bool Apply(const std::string& value) const
+    {
+      if (isCaseSensitive_)
+      {
+        return expected_ == value;
+      }
+      else
+      {
+        std::string v, c;
+        Toolbox::ToLowerCase(v, value);
+        Toolbox::ToLowerCase(c, expected_);
+        return v == c;
+      }
+    }
+  };
+
+
+  class DicomFindQuery::ListConstraint : public DicomFindQuery::IConstraint
+  {
+  private:
+    std::set<std::string>  values_;
+
+  public:
+    ListConstraint(const std::string& values)
+    {
+      std::vector<std::string> items;
+      Toolbox::TokenizeString(items, values, '\\');
+
+      for (size_t i = 0; i < items.size(); i++)
+      {
+        std::string lower;
+        Toolbox::ToLowerCase(lower, items[i]);
+        values_.insert(lower);
+      }
+    }
+
+    virtual bool Apply(const std::string& value) const
+    {
+      std::string tmp;
+      Toolbox::ToLowerCase(tmp, value);
+      return values_.find(tmp) != values_.end();
+    }
+  };
+
+
+  class DicomFindQuery::RangeConstraint : public DicomFindQuery::IConstraint
+  {
+  private:
+    std::string lower_;
+    std::string upper_;
+
+  public:
+    RangeConstraint(const std::string& range)
+    {
+      size_t separator = range.find('-');
+      Toolbox::ToLowerCase(lower_, range.substr(0, separator));
+      Toolbox::ToLowerCase(upper_, range.substr(separator + 1));
+    }
+
+    virtual bool Apply(const std::string& value) const
+    {
+      std::string v;
+      Toolbox::ToLowerCase(v, value);
+
+      if (lower_.size() == 0 && 
+          upper_.size() == 0)
+      {
+        return false;
+      }
+
+      if (lower_.size() == 0)
+      {
+        return v <= upper_;
+      }
+
+      if (upper_.size() == 0)
+      {
+        return v >= lower_;
+      }
+    
+      return (v >= lower_ && v <= upper_);
+    }
+  };
+
+
+  class DicomFindQuery::WildcardConstraint : public DicomFindQuery::IConstraint
+  {
+  private:
+    boost::regex pattern_;
+
+  public:
+    WildcardConstraint(const std::string& wildcard,
+                       bool caseSensitive)
+    {
+      std::string re = Toolbox::WildcardToRegularExpression(wildcard);
+
+      if (caseSensitive)
+      {
+        pattern_ = boost::regex(re);
+      }
+      else
+      {
+        pattern_ = boost::regex(re, boost::regex::icase /* case insensitive search */);
+      }
+    }
+
+    virtual bool Apply(const std::string& value) const
+    {
+      return boost::regex_match(value, pattern_);
+    }
+  };
+
+
+  void DicomFindQuery::PrepareMainDicomTags(ResourceType level)
+  {
+    std::set<DicomTag> tags;
+    DicomMap::GetMainDicomTags(tags, level);
+
+    for (std::set<DicomTag>::const_iterator
+           it = tags.begin(); it != tags.end(); ++it)
+    {
+      mainDicomTags_[*it] = level;
+    }
+  }
+
+
+  DicomFindQuery::DicomFindQuery() : 
+    level_(ResourceType_Patient),
+    filterJson_(false)
+  {
+    PrepareMainDicomTags(ResourceType_Patient);
+    PrepareMainDicomTags(ResourceType_Study);
+    PrepareMainDicomTags(ResourceType_Series);
+    PrepareMainDicomTags(ResourceType_Instance);
+  }
+
+
+  DicomFindQuery::~DicomFindQuery()
+  {
+    for (Constraints::iterator it = constraints_.begin();
+         it != constraints_.end(); ++it)
+    {
+      delete it->second;
+    }
+  }
+
+
+
+
+  void DicomFindQuery::AssignConstraint(const DicomTag& tag,
+                                        IConstraint* constraint)
+  {
+    Constraints::iterator it = constraints_.find(tag);
+
+    if (it != constraints_.end())
+    {
+      constraints_.erase(it);
+    }
+
+    constraints_[tag] = constraint;
+
+    MainDicomTags::const_iterator tmp = mainDicomTags_.find(tag);
+    if (tmp == mainDicomTags_.end())
+    {
+      // The query depends upon a DICOM tag that is not a main tag
+      // from the point of view of Orthanc, we need to decode the
+      // JSON file on the disk.
+      filterJson_ = true;
+    }
+    else
+    {
+      filteredLevels_.insert(tmp->second);
+    }
+  }
+
+
+  void DicomFindQuery::SetConstraint(const DicomTag& tag,
+                                     const std::string& constraint,
+                                     bool caseSensitivePN)
+  {
+    ValueRepresentation vr = FromDcmtkBridge::GetValueRepresentation(tag);
+
+    bool sensitive = true;
+    if (vr == ValueRepresentation_PatientName)
+    {
+      sensitive = caseSensitivePN;
+    }
+
+    // http://www.itk.org/Wiki/DICOM_QueryRetrieve_Explained
+    // http://dicomiseasy.blogspot.be/2012/01/dicom-queryretrieve-part-i.html  
+
+    if ((vr == ValueRepresentation_Date ||
+         vr == ValueRepresentation_DateTime ||
+         vr == ValueRepresentation_Time) &&
+        constraint.find('-') != std::string::npos)
+    {
+      /**
+       * Range matching is only defined for TM, DA and DT value
+       * representations. This code fixes issues 35 and 37.
+       *
+       * Reference: "Range matching is not defined for types of
+       * Attributes other than dates and times", DICOM PS 3.4,
+       * C.2.2.2.5 ("Range Matching").
+       **/
+      AssignConstraint(tag, new RangeConstraint(constraint));
+    }
+    else if (constraint.find('\\') != std::string::npos)
+    {
+      AssignConstraint(tag, new ListConstraint(constraint));
+    }
+    else if (constraint.find('*') != std::string::npos ||
+             constraint.find('?') != std::string::npos)
+    {
+      AssignConstraint(tag, new WildcardConstraint(constraint, sensitive));
+    }
+    else
+    {
+      /**
+       * Case-insensitive match for PN value representation (Patient
+       * Name). Case-senstive match for all the other value
+       * representations.
+       *
+       * Reference: DICOM PS 3.4
+       *   - C.2.2.2.1 ("Single Value Matching") 
+       *   - C.2.2.2.4 ("Wild Card Matching")
+       * http://medical.nema.org/Dicom/2011/11_04pu.pdf
+       *
+       * "Except for Attributes with a PN Value Representation, only
+       * entities with values which match exactly the value specified in the
+       * request shall match. This matching is case-sensitive, i.e.,
+       * sensitive to the exact encoding of the key attribute value in
+       * character sets where a letter may have multiple encodings (e.g.,
+       * based on its case, its position in a word, or whether it is
+       * accented)
+       * 
+       * For Attributes with a PN Value Representation (e.g., Patient Name
+       * (0010,0010)), an application may perform literal matching that is
+       * either case-sensitive, or that is insensitive to some or all
+       * aspects of case, position, accent, or other character encoding
+       * variants."
+       *
+       * (0008,0018) UI SOPInstanceUID     => Case-sensitive
+       * (0008,0050) SH AccessionNumber    => Case-sensitive
+       * (0010,0020) LO PatientID          => Case-sensitive
+       * (0020,000D) UI StudyInstanceUID   => Case-sensitive
+       * (0020,000E) UI SeriesInstanceUID  => Case-sensitive
+      **/
+
+      AssignConstraint(tag, new ValueConstraint(constraint, sensitive));
+    }
+  }
+
+
+  bool DicomFindQuery::RestrictIdentifier(std::string& value,
+                                          DicomTag identifier) const
+  {
+    Constraints::const_iterator it = constraints_.find(identifier);
+    if (it == constraints_.end() ||
+        !it->second->IsExactConstraint())
+    {
+      return false;
+    }
+    else
+    {
+      value = dynamic_cast<ValueConstraint*>(it->second)->GetValue();
+      return true;
+    }
+  }
+
+  bool DicomFindQuery::HasMainDicomTagsFilter(ResourceType level) const
+  {
+    return filteredLevels_.find(level) != filteredLevels_.end();
+  }
+
+  bool DicomFindQuery::FilterMainDicomTags(const std::string& resourceId,
+                                           ResourceType level,
+                                           const DicomMap& mainTags) const
+  {
+    std::set<DicomTag> tags;
+    mainTags.GetTags(tags);
+
+    for (std::set<DicomTag>::const_iterator
+           it = tags.begin(); it != tags.end(); ++it)
+    {
+      Constraints::const_iterator constraint = constraints_.find(*it);
+      if (constraint != constraints_.end() &&
+          !constraint->second->Apply(mainTags.GetValue(*it).AsString()))
+      {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  bool DicomFindQuery::HasInstanceFilter() const
+  {
+    return filterJson_;
+  }
+
+  bool DicomFindQuery::FilterInstance(const std::string& instanceId,
+                                      const Json::Value& content) const
+  {
+    for (Constraints::const_iterator it = constraints_.begin();
+         it != constraints_.end(); ++it)
+    {
+      std::string tag = it->first.Format();
+      std::string value;
+      if (content.isMember(tag))
+      {
+        value = content.get(tag, Json::arrayValue).get("Value", "").asString();
+      }
+
+      if (!it->second->Apply(value))
+      {
+        return false;
+      }
+    }
+
+    return true;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/DicomFindQuery.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,111 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ResourceFinder.h"
+
+namespace Orthanc
+{
+  class DicomFindQuery : public ResourceFinder::IQuery
+  {
+  private:
+    class  IConstraint : public boost::noncopyable
+    {
+    public:
+      virtual ~IConstraint()
+      {
+      }
+
+      virtual bool IsExactConstraint() const
+      {
+        return false;
+      }
+
+      virtual bool Apply(const std::string& value) const = 0;
+    };
+
+
+    class ValueConstraint;
+    class RangeConstraint;
+    class ListConstraint;
+    class WildcardConstraint;
+
+    typedef std::map<DicomTag, IConstraint*>  Constraints;
+    typedef std::map<DicomTag, ResourceType>  MainDicomTags;
+
+    MainDicomTags           mainDicomTags_;
+    ResourceType            level_;
+    bool                    filterJson_;
+    Constraints             constraints_;
+    std::set<ResourceType>  filteredLevels_;
+
+    void AssignConstraint(const DicomTag& tag,
+                          IConstraint* constraint);
+
+    void PrepareMainDicomTags(ResourceType level);
+
+
+  public:
+    DicomFindQuery();
+
+    virtual ~DicomFindQuery();
+
+    void SetLevel(ResourceType level)
+    {
+      level_ = level;
+    }
+
+    virtual ResourceType GetLevel() const
+    {
+      return level_;
+    }
+
+    void SetConstraint(const DicomTag& tag,
+                       const std::string& constraint,
+                       bool caseSensitivePN);
+
+    virtual bool RestrictIdentifier(std::string& value,
+                                    DicomTag identifier) const;
+
+    virtual bool HasMainDicomTagsFilter(ResourceType level) const;
+
+    virtual bool FilterMainDicomTags(const std::string& resourceId,
+                                     ResourceType level,
+                                     const DicomMap& mainTags) const;
+
+    virtual bool HasInstanceFilter() const;
+
+    virtual bool FilterInstance(const std::string& instanceId,
+                                const Json::Value& content) const;
+  };
+}
--- a/OrthancServer/DicomInstanceToStore.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DicomInstanceToStore.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -30,12 +30,13 @@
  **/
 
 
+#include "PrecompiledHeadersServer.h"
 #include "DicomInstanceToStore.h"
 
 #include "FromDcmtkBridge.h"
+#include "../Core/Logging.h"
 
 #include <dcmtk/dcmdata/dcfilefo.h>
-#include <glog/logging.h>
 
 
 namespace Orthanc
@@ -171,4 +172,99 @@
 
     return json_.GetConstContent();
   }
+
+
+
+  void DicomInstanceToStore::GetOriginInformation(Json::Value& result) const
+  {
+    result = Json::objectValue;
+    result["RequestOrigin"] = EnumerationToString(origin_);
+
+    switch (origin_)
+    {
+      case RequestOrigin_Unknown:
+      {
+        // None of the methods "SetDicomProtocolOrigin()", "SetHttpOrigin()",
+        // "SetLuaOrigin()" or "SetPluginsOrigin()" was called!
+        throw OrthancException(ErrorCode_BadSequenceOfCalls);
+      }
+
+      case RequestOrigin_DicomProtocol:
+      {
+        result["RemoteIp"] = remoteIp_;
+        result["RemoteAet"] = dicomRemoteAet_;
+        result["CalledAet"] = dicomCalledAet_;
+        break;
+      }
+
+      case RequestOrigin_Http:
+      {
+        result["RemoteIp"] = remoteIp_;
+        result["Username"] = httpUsername_;
+        break;
+      }
+
+      case RequestOrigin_Lua:
+      case RequestOrigin_Plugins:
+      {
+        // No additional information available for these kinds of requests
+        break;
+      }
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+    }
+  }
+
+
+  void DicomInstanceToStore::SetDicomProtocolOrigin(const char* remoteIp,
+                                                    const char* remoteAet,
+                                                    const char* calledAet)
+  {
+    origin_ = RequestOrigin_DicomProtocol;
+    remoteIp_ = remoteIp;
+    dicomRemoteAet_ = remoteAet;
+    dicomCalledAet_ = calledAet;
+  }
+
+  void DicomInstanceToStore::SetRestOrigin(const RestApiCall& call)
+  {
+    origin_ = call.GetRequestOrigin();
+
+    if (origin_ == RequestOrigin_Http)
+    {
+      remoteIp_ = call.GetRemoteIp();
+      httpUsername_ = call.GetUsername();
+    }
+  }
+
+  void DicomInstanceToStore::SetHttpOrigin(const char* remoteIp,
+                                           const char* username)
+  {
+    origin_ = RequestOrigin_Http;
+    remoteIp_ = remoteIp;
+    httpUsername_ = username;
+  }
+
+  void DicomInstanceToStore::SetLuaOrigin()
+  {
+    origin_ = RequestOrigin_Lua;
+  }
+
+  void DicomInstanceToStore::SetPluginsOrigin()
+  {
+    origin_ = RequestOrigin_Plugins;
+  }
+
+  const char* DicomInstanceToStore::GetRemoteAet() const
+  {
+    if (origin_ == RequestOrigin_DicomProtocol)
+    {
+      return dicomRemoteAet_.c_str();
+    }
+    else
+    {
+      return "";
+    }
+  }
 }
--- a/OrthancServer/DicomInstanceToStore.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DicomInstanceToStore.h	Wed Sep 30 13:23:31 2015 +0200
@@ -35,6 +35,7 @@
 #include "ParsedDicomFile.h"
 #include "ServerIndex.h"
 #include "../Core/OrthancException.h"
+#include "../Core/RestApi/RestApiCall.h"
 
 namespace Orthanc
 {
@@ -143,13 +144,35 @@
     SmartContainer<DicomMap>  summary_;
     SmartContainer<Json::Value>  json_;
 
-    std::string remoteAet_;
-    std::string calledAet_;
+    RequestOrigin origin_;
+    std::string remoteIp_;
+    std::string dicomRemoteAet_;
+    std::string dicomCalledAet_;
+    std::string httpUsername_;
     ServerIndex::MetadataMap metadata_;
 
     void ComputeMissingInformation();
 
   public:
+    DicomInstanceToStore() : origin_(RequestOrigin_Unknown)
+    {
+    }
+
+    void SetDicomProtocolOrigin(const char* remoteIp,
+                                const char* remoteAet,
+                                const char* calledAet);
+
+    void SetRestOrigin(const RestApiCall& call);
+
+    void SetHttpOrigin(const char* remoteIp,
+                       const char* username);
+
+    void SetLuaOrigin();
+
+    void SetPluginsOrigin();
+
+    const char* GetRemoteAet() const; 
+
     void SetBuffer(const std::string& dicom)
     {
       buffer_.SetConstReference(dicom);
@@ -170,26 +193,6 @@
       json_.SetConstReference(json);
     }
 
-    const std::string& GetRemoteAet() const
-    {
-      return remoteAet_;
-    }
-
-    void SetRemoteAet(const std::string& aet)
-    {
-      remoteAet_ = aet;
-    }
-
-    const std::string& GetCalledAet() const
-    {
-      return calledAet_;
-    }
-
-    void SetCalledAet(const std::string& aet)
-    {
-      calledAet_ = aet;
-    }
-
     void AddMetadata(ResourceType level,
                      MetadataType metadata,
                      const std::string& value);
@@ -211,5 +214,7 @@
     const DicomMap& GetSummary();
     
     const Json::Value& GetJson();
+
+    void GetOriginInformation(Json::Value& result) const;
   };
 }
--- a/OrthancServer/DicomModification.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DicomModification.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,11 +33,11 @@
 #include "PrecompiledHeadersServer.h"
 #include "DicomModification.h"
 
+#include "../Core/Logging.h"
 #include "../Core/OrthancException.h"
 #include "FromDcmtkBridge.h"
 
 #include <memory>   // For std::auto_ptr
-#include <glog/logging.h>
 
 
 static const std::string ORTHANC_DEIDENTIFICATION_METHOD = "Orthanc " ORTHANC_VERSION " - PS 3.15-2008 Table E.1-1";
--- a/OrthancServer/DicomProtocol/DicomFindAnswers.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DicomProtocol/DicomFindAnswers.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -53,14 +53,15 @@
     }
   }
 
-  void DicomFindAnswers::ToJson(Json::Value& target) const
+  void DicomFindAnswers::ToJson(Json::Value& target,
+                                bool simplify) const
   {
     target = Json::arrayValue;
 
     for (size_t i = 0; i < GetSize(); i++)
     {
       Json::Value answer(Json::objectValue);
-      FromDcmtkBridge::ToJson(answer, GetAnswer(i));
+      FromDcmtkBridge::ToJson(answer, GetAnswer(i), simplify);
       target.append(answer);
     }
   }
--- a/OrthancServer/DicomProtocol/DicomFindAnswers.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DicomProtocol/DicomFindAnswers.h	Wed Sep 30 13:23:31 2015 +0200
@@ -69,6 +69,7 @@
       return *items_.at(index);
     }
 
-    void ToJson(Json::Value& target) const;
+    void ToJson(Json::Value& target,
+                bool simplify) const;
   };
 }
--- a/OrthancServer/DicomProtocol/DicomServer.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DicomProtocol/DicomServer.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,6 +33,7 @@
 #include "../PrecompiledHeadersServer.h"
 #include "DicomServer.h"
 
+#include "../../Core/Logging.h"
 #include "../../Core/OrthancException.h"
 #include "../../Core/Toolbox.h"
 #include "../../Core/Uuid.h"
@@ -41,9 +42,6 @@
 #include "EmbeddedResources.h"
 
 #include <boost/thread.hpp>
-#include <boost/filesystem.hpp>
-#include <dcmtk/dcmdata/dcdict.h>
-#include <glog/logging.h>
 
 #if defined(__linux)
 #include <cstdlib>
@@ -60,99 +58,6 @@
   };
 
 
-#if DCMTK_USE_EMBEDDED_DICTIONARIES == 1
-  static void LoadEmbeddedDictionary(DcmDataDictionary& dictionary,
-                                     EmbeddedResources::FileResourceId resource)
-  {
-    Toolbox::TemporaryFile tmp;
-
-    FILE* fp = fopen(tmp.GetPath().c_str(), "wb");
-    fwrite(EmbeddedResources::GetFileResourceBuffer(resource), 
-           EmbeddedResources::GetFileResourceSize(resource), 1, fp);
-    fclose(fp);
-
-    if (!dictionary.loadDictionary(tmp.GetPath().c_str()))
-    {
-      throw OrthancException(ErrorCode_InternalError);
-    }
-  }
-                             
-#else
-  static void LoadExternalDictionary(DcmDataDictionary& dictionary,
-                                     const std::string& directory,
-                                     const std::string& filename)
-  {
-    boost::filesystem::path p = directory;
-    p = p / filename;
-
-    LOG(WARNING) << "Loading the external DICOM dictionary " << p;
-
-    if (!dictionary.loadDictionary(p.string().c_str()))
-    {
-      throw OrthancException(ErrorCode_InternalError);
-    }
-  }
-                            
-#endif
-
-
-  void DicomServer::InitializeDictionary()
-  {
-    /* Disable "gethostbyaddr" (which results in memory leaks) and use raw IP addresses */
-    dcmDisableGethostbyaddr.set(OFTrue);
-
-    dcmDataDict.clear();
-    DcmDataDictionary& d = dcmDataDict.wrlock();
-
-#if DCMTK_USE_EMBEDDED_DICTIONARIES == 1
-    LOG(WARNING) << "Loading the embedded dictionaries";
-    /**
-     * Do not load DICONDE dictionary, it breaks the other tags. The
-     * command "strace storescu 2>&1 |grep dic" shows that DICONDE
-     * dictionary is not loaded by storescu.
-     **/
-    //LoadEmbeddedDictionary(d, EmbeddedResources::DICTIONARY_DICONDE);
-
-    LoadEmbeddedDictionary(d, EmbeddedResources::DICTIONARY_DICOM);
-    LoadEmbeddedDictionary(d, EmbeddedResources::DICTIONARY_PRIVATE);
-
-#elif defined(__linux) || defined(__FreeBSD_kernel__)
-    std::string path = DCMTK_DICTIONARY_DIR;
-
-    const char* env = std::getenv(DCM_DICT_ENVIRONMENT_VARIABLE);
-    if (env != NULL)
-    {
-      path = std::string(env);
-    }
-
-    LoadExternalDictionary(d, path, "dicom.dic");
-    LoadExternalDictionary(d, path, "private.dic");
-
-#else
-#error Support your platform here
-#endif
-
-    dcmDataDict.unlock();
-
-    /* make sure data dictionary is loaded */
-    if (!dcmDataDict.isDictionaryLoaded())
-    {
-      LOG(ERROR) << "No DICOM dictionary loaded, check environment variable: " << DCM_DICT_ENVIRONMENT_VARIABLE;
-      throw OrthancException(ErrorCode_InternalError);
-    }
-
-    {
-      // Test the dictionary with a simple DICOM tag
-      DcmTag key(0x0010, 0x1030); // This is PatientWeight
-      if (key.getEVR() != EVR_DS)
-      {
-        LOG(ERROR) << "The DICOM dictionary has not been correctly read";
-        throw OrthancException(ErrorCode_InternalError);
-      }
-    }
-  }
-
-
   void DicomServer::ServerThread(DicomServer* server)
   {
     /* initialize network, i.e. create an instance of T_ASC_Network*. */
@@ -162,7 +67,7 @@
     if (cond.bad())
     {
       LOG(ERROR) << "cannot create network: " << cond.text();
-      throw OrthancException("Cannot create network");
+      throw OrthancException(ErrorCode_DicomPortInUse);
     }
 
     LOG(INFO) << "DICOM server started";
@@ -223,7 +128,11 @@
 
   DicomServer::~DicomServer()
   {
-    Stop();
+    if (continue_)
+    {
+      LOG(ERROR) << "INTERNAL ERROR: DicomServer::Stop() should be invoked manually to avoid mess in the destruction order!";
+      Stop();
+    }
   }
 
   void DicomServer::SetPortNumber(uint16_t port)
@@ -275,12 +184,12 @@
   {
     if (aet.size() == 0)
     {
-      throw OrthancException("Too short AET");
+      throw OrthancException(ErrorCode_BadApplicationEntityTitle);
     }
 
     if (aet.size() > 16)
     {
-      throw OrthancException("AET must be shorter than 16 characters");
+      throw OrthancException(ErrorCode_BadApplicationEntityTitle);
     }
 
     for (size_t i = 0; i < aet.size(); i++)
@@ -323,7 +232,7 @@
     }
     else
     {
-      throw OrthancException("No C-FIND request handler factory");
+      throw OrthancException(ErrorCode_NoCFindHandler);
     }
   }
 
@@ -346,7 +255,7 @@
     }
     else
     {
-      throw OrthancException("No C-MOVE request handler factory");
+      throw OrthancException(ErrorCode_NoCMoveHandler);
     }
   }
 
@@ -369,7 +278,7 @@
     }
     else
     {
-      throw OrthancException("No C-STORE request handler factory");
+      throw OrthancException(ErrorCode_NoCStoreHandler);
     }
   }
 
@@ -392,7 +301,7 @@
     }
     else
     {
-      throw OrthancException("No application entity filter");
+      throw OrthancException(ErrorCode_NoApplicationEntityFilter);
     }
   }
 
@@ -409,16 +318,23 @@
     }
   }
 
+
   void DicomServer::Stop()
   {
-    continue_ = false;
+    if (continue_)
+    {
+      continue_ = false;
 
-    if (pimpl_->thread_.joinable())
-    {
-      pimpl_->thread_.join();
+      if (pimpl_->thread_.joinable())
+      {
+        pimpl_->thread_.join();
+      }
+
+      bagOfDispatchers_.Finalize();
     }
   }
 
+
   bool DicomServer::IsMyAETitle(const std::string& aet) const
   {
     if (!HasCalledApplicationEntityTitleCheck())
--- a/OrthancServer/DicomProtocol/DicomServer.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DicomProtocol/DicomServer.h	Wed Sep 30 13:23:31 2015 +0200
@@ -66,8 +66,6 @@
     static void ServerThread(DicomServer* server);
 
   public:
-    static void InitializeDictionary();
-
     DicomServer();
 
     ~DicomServer();
--- a/OrthancServer/DicomProtocol/DicomUserConnection.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DicomProtocol/DicomUserConnection.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -81,9 +81,11 @@
 #include "../PrecompiledHeadersServer.h"
 #include "DicomUserConnection.h"
 
+#include "../../Core/DicomFormat/DicomArray.h"
+#include "../../Core/Logging.h"
 #include "../../Core/OrthancException.h"
+#include "../FromDcmtkBridge.h"
 #include "../ToDcmtkBridge.h"
-#include "../FromDcmtkBridge.h"
 
 #include <dcmtk/dcmdata/dcistrmb.h>
 #include <dcmtk/dcmdata/dcistrmf.h>
@@ -92,8 +94,6 @@
 #include <dcmtk/dcmnet/diutil.h>
 
 #include <set>
-#include <glog/logging.h>
-
 
 
 #ifdef _WIN32
@@ -299,7 +299,7 @@
     DIC_UI sopInstance;
     if (!DU_findSOPClassAndInstanceInDataSet(dcmff.getDataset(), sopClass, sopInstance))
     {
-      throw OrthancException("DicomUserConnection: Unable to find the SOP class and instance");
+      throw OrthancException(ErrorCode_NoSopClassOrInstance);
     }
 
     // Figure out which of the accepted presentation contexts should be used
@@ -309,8 +309,7 @@
       const char *modalityName = dcmSOPClassUIDToModality(sopClass);
       if (!modalityName) modalityName = dcmFindNameOfUID(sopClass);
       if (!modalityName) modalityName = "unknown SOP class";
-      throw OrthancException("DicomUserConnection: No presentation context for modality " + 
-                             std::string(modalityName));
+      throw OrthancException(ErrorCode_NoPresentationContext);
     }
 
     // Prepare the transmission of data
@@ -337,6 +336,16 @@
   }
 
 
+  namespace
+  {
+    struct FindPayload
+    {
+      DicomFindAnswers* answers;
+      std::string       level;
+    };
+  }
+
+
   static void FindCallback(
     /* in */
     void *callbackData,
@@ -346,73 +355,144 @@
     DcmDataset *responseIdentifiers /* pending response identifiers */
     )
   {
-    DicomFindAnswers& answers = *reinterpret_cast<DicomFindAnswers*>(callbackData);
+    FindPayload& payload = *reinterpret_cast<FindPayload*>(callbackData);
 
     if (responseIdentifiers != NULL)
     {
       DicomMap m;
       FromDcmtkBridge::Convert(m, *responseIdentifiers);
-      answers.Add(m);
+
+      if (!m.HasTag(DICOM_TAG_QUERY_RETRIEVE_LEVEL))
+      {
+        m.SetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL, payload.level);
+      }
+
+      payload.answers->Add(m);
+    }
+  }
+
+
+  static void CheckFindQuery(ResourceType level,
+                             const DicomMap& fields)
+  {
+    std::set<DicomTag> allowedTags;
+
+    // WARNING: Do not add "break" or reorder items in this switch-case!
+    switch (level)
+    {
+      case ResourceType_Instance:
+        DicomTag::AddTagsForModule(allowedTags, DicomModule_Instance);
+
+      case ResourceType_Series:
+        DicomTag::AddTagsForModule(allowedTags, DicomModule_Series);
+
+      case ResourceType_Study:
+        DicomTag::AddTagsForModule(allowedTags, DicomModule_Study);
+
+      case ResourceType_Patient:
+        DicomTag::AddTagsForModule(allowedTags, DicomModule_Patient);
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+    }
+
+    if (level == ResourceType_Study)
+    {
+      allowedTags.insert(DICOM_TAG_MODALITIES_IN_STUDY);
+    }
+
+    allowedTags.insert(DICOM_TAG_SPECIFIC_CHARACTER_SET);
+
+    DicomArray query(fields);
+    for (size_t i = 0; i < query.GetSize(); i++)
+    {
+      const DicomTag& tag = query.GetElement(i).GetTag();
+      if (allowedTags.find(tag) == allowedTags.end())
+      {
+        LOG(ERROR) << "Tag not allowed for this C-Find level: " << tag;
+        throw OrthancException(ErrorCode_BadRequest);
+      }
     }
   }
 
+
+  static DcmDataset* ConvertQueryFields(const DicomMap& fields,
+                                        ModalityManufacturer manufacturer)
+  {
+    switch (manufacturer)
+    {
+      case ModalityManufacturer_SyngoVia:
+      {
+        std::auto_ptr<DicomMap> fix(fields.Clone());
+
+        // This issue for Syngo.Via and its solution was reported by
+        // Emsy Chan by private mail on June 17th, 2015.
+        std::set<DicomTag> tags;
+        fix->GetTags(tags);
+
+        for (std::set<DicomTag>::const_iterator it = tags.begin(); it != tags.end(); ++it)
+        {
+          if (FromDcmtkBridge::GetValueRepresentation(*it) == ValueRepresentation_Date)
+          {
+            // Replace a "*" query by an empty query ("") for "date"
+            // value representations. Necessary to search over dates
+            // in Syngo.Via.
+            const DicomValue* value = fix->TestAndGetValue(*it);
+
+            if (value != NULL && 
+                value->AsString() == "*")
+            {
+              fix->SetValue(*it, "");
+            }
+          }
+        }
+
+        return ToDcmtkBridge::Convert(*fix);
+      }
+
+      default:
+        return ToDcmtkBridge::Convert(fields);
+    }
+  }
+
+
   void DicomUserConnection::Find(DicomFindAnswers& result,
-                                 FindRootModel model,
+                                 ResourceType level,
                                  const DicomMap& fields)
   {
+    CheckFindQuery(level, fields);
+
     CheckIsOpen();
 
+    FindPayload payload;
+    payload.answers = &result;
+
+    std::auto_ptr<DcmDataset> dataset(ConvertQueryFields(fields, manufacturer_));
+
     const char* sopClass;
-    std::auto_ptr<DcmDataset> dataset(ToDcmtkBridge::Convert(fields));
-    switch (model)
+    switch (level)
     {
-      case FindRootModel_Patient:
+      case ResourceType_Patient:
+        payload.level = "PATIENT";
         DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "PATIENT");
         sopClass = UID_FINDPatientRootQueryRetrieveInformationModel;
-      
-        // Accession number
-        if (!fields.HasTag(0x0008, 0x0050))
-          DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), "");
-
-        // Patient ID
-        if (!fields.HasTag(0x0010, 0x0020))
-          DU_putStringDOElement(dataset.get(), DcmTagKey(0x0010, 0x0020), "");
-
         break;
 
-      case FindRootModel_Study:
+      case ResourceType_Study:
+        payload.level = "STUDY";
         DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "STUDY");
         sopClass = UID_FINDStudyRootQueryRetrieveInformationModel;
-
-        // Accession number
-        if (!fields.HasTag(0x0008, 0x0050))
-          DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), "");
-
-        // Study instance UID
-        if (!fields.HasTag(0x0020, 0x000d))
-          DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), "");
-
         break;
 
-      case FindRootModel_Series:
+      case ResourceType_Series:
+        payload.level = "SERIES";
         DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "SERIES");
         sopClass = UID_FINDStudyRootQueryRetrieveInformationModel;
-
-        // Accession number
-        if (!fields.HasTag(0x0008, 0x0050))
-          DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), "");
-
-        // Study instance UID
-        if (!fields.HasTag(0x0020, 0x000d))
-          DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), "");
-
-        // Series instance UID
-        if (!fields.HasTag(0x0020, 0x000e))
-          DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000e), "");
-
         break;
 
-      case FindRootModel_Instance:
+      case ResourceType_Instance:
+        payload.level = "INSTANCE";
         if (manufacturer_ == ModalityManufacturer_ClearCanvas ||
             manufacturer_ == ModalityManufacturer_Dcm4Chee)
         {
@@ -427,7 +507,27 @@
         }
 
         sopClass = UID_FINDStudyRootQueryRetrieveInformationModel;
+        break;
 
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    // Add the expected tags for this query level.
+    // WARNING: Do not reorder or add "break" in this switch-case!
+    switch (level)
+    {
+      case ResourceType_Instance:
+        // SOP Instance UID
+        if (!fields.HasTag(0x0008, 0x0018))
+          DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0018), "");
+
+      case ResourceType_Series:
+        // Series instance UID
+        if (!fields.HasTag(0x0020, 0x000e))
+          DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000e), "");
+
+      case ResourceType_Study:
         // Accession number
         if (!fields.HasTag(0x0008, 0x0050))
           DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), "");
@@ -436,13 +536,10 @@
         if (!fields.HasTag(0x0020, 0x000d))
           DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), "");
 
-        // Series instance UID
-        if (!fields.HasTag(0x0020, 0x000e))
-          DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000e), "");
-
-        // SOP Instance UID
-        if (!fields.HasTag(0x0008, 0x0018))
-          DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0018), "");
+      case ResourceType_Patient:
+        // Patient ID
+        if (!fields.HasTag(0x0010, 0x0020))
+          DU_putStringDOElement(dataset.get(), DcmTagKey(0x0010, 0x0020), "");
 
         break;
 
@@ -454,7 +551,7 @@
     int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass);
     if (presID == 0)
     {
-      throw OrthancException("DicomUserConnection: The C-FIND command is not supported by the remote AET");
+      throw OrthancException(ErrorCode_DicomFindUnavailable);
     }
 
     T_DIMSE_C_FindRQ request;
@@ -467,7 +564,7 @@
     T_DIMSE_C_FindRSP response;
     DcmDataset* statusDetail = NULL;
     OFCondition cond = DIMSE_findUser(pimpl_->assoc_, presID, &request, dataset.get(),
-                                      FindCallback, &result,
+                                      FindCallback, &payload,
                                       /*opt_blockMode*/ DIMSE_BLOCKING, 
                                       /*opt_dimse_timeout*/ pimpl_->dimseTimeout_,
                                       &response, &statusDetail);
@@ -481,72 +578,53 @@
   }
 
 
-  void DicomUserConnection::FindPatient(DicomFindAnswers& result,
-                                        const DicomMap& fields)
-  {
-    // Only keep the filters from "fields" that are related to the patient
-    DicomMap s;
-    fields.ExtractPatientInformation(s);
-    Find(result, FindRootModel_Patient, s);
-  }
-
-  void DicomUserConnection::FindStudy(DicomFindAnswers& result,
-                                      const DicomMap& fields)
-  {
-    // Only keep the filters from "fields" that are related to the study
-    DicomMap s;
-    fields.ExtractStudyInformation(s);
-
-    s.CopyTagIfExists(fields, DICOM_TAG_PATIENT_ID);
-    s.CopyTagIfExists(fields, DICOM_TAG_ACCESSION_NUMBER);
-    s.CopyTagIfExists(fields, DICOM_TAG_MODALITIES_IN_STUDY);
-
-    Find(result, FindRootModel_Study, s);
-  }
-
-  void DicomUserConnection::FindSeries(DicomFindAnswers& result,
-                                       const DicomMap& fields)
-  {
-    // Only keep the filters from "fields" that are related to the series
-    DicomMap s;
-    fields.ExtractSeriesInformation(s);
-
-    s.CopyTagIfExists(fields, DICOM_TAG_PATIENT_ID);
-    s.CopyTagIfExists(fields, DICOM_TAG_ACCESSION_NUMBER);
-    s.CopyTagIfExists(fields, DICOM_TAG_STUDY_INSTANCE_UID);
-
-    Find(result, FindRootModel_Series, s);
-  }
-
-  void DicomUserConnection::FindInstance(DicomFindAnswers& result,
+  void DicomUserConnection::MoveInternal(const std::string& targetAet,
+                                         ResourceType level,
                                          const DicomMap& fields)
   {
-    // Only keep the filters from "fields" that are related to the instance
-    DicomMap s;
-    fields.ExtractInstanceInformation(s);
-
-    s.CopyTagIfExists(fields, DICOM_TAG_PATIENT_ID);
-    s.CopyTagIfExists(fields, DICOM_TAG_ACCESSION_NUMBER);
-    s.CopyTagIfExists(fields, DICOM_TAG_STUDY_INSTANCE_UID);
-    s.CopyTagIfExists(fields, DICOM_TAG_SERIES_INSTANCE_UID);
-
-    Find(result, FindRootModel_Instance, s);
-  }
-
-
-  void DicomUserConnection::Move(const std::string& targetAet,
-                                 const DicomMap& fields)
-  {
     CheckIsOpen();
 
+    std::auto_ptr<DcmDataset> dataset(ConvertQueryFields(fields, manufacturer_));
+
     const char* sopClass = UID_MOVEStudyRootQueryRetrieveInformationModel;
-    std::auto_ptr<DcmDataset> dataset(ToDcmtkBridge::Convert(fields));
+    switch (level)
+    {
+      case ResourceType_Patient:
+        DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "PATIENT");
+        break;
+
+      case ResourceType_Study:
+        DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "STUDY");
+        break;
+
+      case ResourceType_Series:
+        DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "SERIES");
+        break;
+
+      case ResourceType_Instance:
+        if (manufacturer_ == ModalityManufacturer_ClearCanvas ||
+            manufacturer_ == ModalityManufacturer_Dcm4Chee)
+        {
+          // This is a particular case for ClearCanvas, thanks to Peter Somlo <peter.somlo@gmail.com>.
+          // https://groups.google.com/d/msg/orthanc-users/j-6C3MAVwiw/iolB9hclom8J
+          // http://www.clearcanvas.ca/Home/Community/OldForums/tabid/526/aff/11/aft/14670/afv/topic/Default.aspx
+          DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "IMAGE");
+        }
+        else
+        {
+          DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "INSTANCE");
+        }
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
 
     // Figure out which of the accepted presentation contexts should be used
     int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass);
     if (presID == 0)
     {
-      throw OrthancException("DicomUserConnection: The C-MOVE command is not supported by the remote AET");
+      throw OrthancException(ErrorCode_DicomMoveUnavailable);
     }
 
     T_DIMSE_C_MoveRQ request;
@@ -640,7 +718,7 @@
   }
 
 
-  void DicomUserConnection::Connect(const RemoteModalityParameters& parameters)
+  void DicomUserConnection::SetRemoteModality(const RemoteModalityParameters& parameters)
   {
     SetRemoteApplicationEntityTitle(parameters.GetApplicationEntityTitle());
     SetRemoteHost(parameters.GetHost());
@@ -697,7 +775,7 @@
     {
       if (host.size() > HOST_NAME_MAX - 10)
       {
-        throw OrthancException("Remote host name is too long");
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
       }
 
       Close();
@@ -758,7 +836,7 @@
 
     if (ASC_countAcceptedPresentationContexts(pimpl_->params_) == 0)
     {
-      throw OrthancException("DicomUserConnection: No Acceptable Presentation Contexts");
+      throw OrthancException(ErrorCode_NoPresentationContext);
     }
   }
 
@@ -830,33 +908,86 @@
   }
 
 
-  void DicomUserConnection::MoveSeries(const std::string& targetAet,
-                                       const DicomMap& findResult)
+  static void TestAndCopyTag(DicomMap& result,
+                             const DicomMap& source,
+                             const DicomTag& tag)
+  {
+    if (!source.HasTag(tag))
+    {
+      throw OrthancException(ErrorCode_BadRequest);
+    }
+    else
+    {
+      result.SetValue(tag, source.GetValue(tag));
+    }
+  }
+
+
+  void DicomUserConnection::Move(const std::string& targetAet,
+                                 const DicomMap& findResult)
   {
-    DicomMap simplified;
-    simplified.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, findResult.GetValue(DICOM_TAG_STUDY_INSTANCE_UID));
-    simplified.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, findResult.GetValue(DICOM_TAG_SERIES_INSTANCE_UID));
-    Move(targetAet, simplified);
+    if (!findResult.HasTag(DICOM_TAG_QUERY_RETRIEVE_LEVEL))
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+
+    const std::string tmp = findResult.GetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL).AsString();
+    ResourceType level = StringToResourceType(tmp.c_str());
+
+    DicomMap move;
+    switch (level)
+    {
+      case ResourceType_Patient:
+        TestAndCopyTag(move, findResult, DICOM_TAG_PATIENT_ID);
+        break;
+
+      case ResourceType_Study:
+        TestAndCopyTag(move, findResult, DICOM_TAG_STUDY_INSTANCE_UID);
+        break;
+
+      case ResourceType_Series:
+        TestAndCopyTag(move, findResult, DICOM_TAG_STUDY_INSTANCE_UID);
+        TestAndCopyTag(move, findResult, DICOM_TAG_SERIES_INSTANCE_UID);
+        break;
+
+      case ResourceType_Instance:
+        TestAndCopyTag(move, findResult, DICOM_TAG_STUDY_INSTANCE_UID);
+        TestAndCopyTag(move, findResult, DICOM_TAG_SERIES_INSTANCE_UID);
+        TestAndCopyTag(move, findResult, DICOM_TAG_SOP_INSTANCE_UID);
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+    }
+
+    MoveInternal(targetAet, level, move);
+  }
+
+
+  void DicomUserConnection::MovePatient(const std::string& targetAet,
+                                        const std::string& patientId)
+  {
+    DicomMap query;
+    query.SetValue(DICOM_TAG_PATIENT_ID, patientId);
+    MoveInternal(targetAet, ResourceType_Patient, query);
+  }
+
+  void DicomUserConnection::MoveStudy(const std::string& targetAet,
+                                      const std::string& studyUid)
+  {
+    DicomMap query;
+    query.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid);
+    MoveInternal(targetAet, ResourceType_Study, query);
   }
 
   void DicomUserConnection::MoveSeries(const std::string& targetAet,
                                        const std::string& studyUid,
                                        const std::string& seriesUid)
   {
-    DicomMap map;
-    map.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid);
-    map.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, seriesUid);
-    Move(targetAet, map);
-  }
-
-  void DicomUserConnection::MoveInstance(const std::string& targetAet,
-                                         const DicomMap& findResult)
-  {
-    DicomMap simplified;
-    simplified.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, findResult.GetValue(DICOM_TAG_STUDY_INSTANCE_UID));
-    simplified.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, findResult.GetValue(DICOM_TAG_SERIES_INSTANCE_UID));
-    simplified.SetValue(DICOM_TAG_SOP_INSTANCE_UID, findResult.GetValue(DICOM_TAG_SOP_INSTANCE_UID));
-    Move(targetAet, simplified);
+    DicomMap query;
+    query.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid);
+    query.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, seriesUid);
+    MoveInternal(targetAet, ResourceType_Series, query);
   }
 
   void DicomUserConnection::MoveInstance(const std::string& targetAet,
@@ -864,11 +995,11 @@
                                          const std::string& seriesUid,
                                          const std::string& instanceUid)
   {
-    DicomMap map;
-    map.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid);
-    map.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, seriesUid);
-    map.SetValue(DICOM_TAG_SOP_INSTANCE_UID, instanceUid);
-    Move(targetAet, map);
+    DicomMap query;
+    query.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid);
+    query.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, seriesUid);
+    query.SetValue(DICOM_TAG_SOP_INSTANCE_UID, instanceUid);
+    MoveInternal(targetAet, ResourceType_Instance, query);
   }
 
 
--- a/OrthancServer/DicomProtocol/DicomUserConnection.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DicomProtocol/DicomUserConnection.h	Wed Sep 30 13:23:31 2015 +0200
@@ -46,14 +46,6 @@
   class DicomUserConnection : public boost::noncopyable
   {
   private:
-    enum FindRootModel
-    {
-      FindRootModel_Patient,
-      FindRootModel_Study,
-      FindRootModel_Series,
-      FindRootModel_Instance
-    };
-
     struct PImpl;
     boost::shared_ptr<PImpl> pimpl_;
 
@@ -72,12 +64,9 @@
 
     void SetupPresentationContexts(const std::string& preferredTransferSyntax);
 
-    void Find(DicomFindAnswers& result,
-              FindRootModel model,
-              const DicomMap& fields);
-
-    void Move(const std::string& targetAet,
-              const DicomMap& fields);
+    void MoveInternal(const std::string& targetAet,
+                      ResourceType level,
+                      const DicomMap& fields);
 
     void ResetStorageSOPClasses();
 
@@ -88,7 +77,7 @@
 
     ~DicomUserConnection();
 
-    void Connect(const RemoteModalityParameters& parameters);
+    void SetRemoteModality(const RemoteModalityParameters& parameters);
 
     void SetLocalApplicationEntityTitle(const std::string& aet);
 
@@ -150,29 +139,24 @@
 
     void StoreFile(const std::string& path);
 
-    void FindPatient(DicomFindAnswers& result,
-                     const DicomMap& fields);
-
-    void FindStudy(DicomFindAnswers& result,
-                   const DicomMap& fields);
+    void Find(DicomFindAnswers& result,
+              ResourceType level,
+              const DicomMap& fields);
 
-    void FindSeries(DicomFindAnswers& result,
-                    const DicomMap& fields);
+    void Move(const std::string& targetAet,
+              const DicomMap& findResult);
 
-    void FindInstance(DicomFindAnswers& result,
-                      const DicomMap& fields);
+    void MovePatient(const std::string& targetAet,
+                     const std::string& patientId);
 
-    void MoveSeries(const std::string& targetAet,
-                    const DicomMap& findResult);
+    void MoveStudy(const std::string& targetAet,
+                   const std::string& studyUid);
 
     void MoveSeries(const std::string& targetAet,
                     const std::string& studyUid,
                     const std::string& seriesUid);
 
     void MoveInstance(const std::string& targetAet,
-                      const DicomMap& findResult);
-
-    void MoveInstance(const std::string& targetAet,
                       const std::string& studyUid,
                       const std::string& seriesUid,
                       const std::string& instanceUid);
--- a/OrthancServer/DicomProtocol/IFindRequestHandler.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DicomProtocol/IFindRequestHandler.h	Wed Sep 30 13:23:31 2015 +0200
@@ -55,6 +55,7 @@
      **/
     virtual bool Handle(DicomFindAnswers& answers,
                         const DicomMap& input,
-                        const std::string& callingAETitle) = 0;
+                        const std::string& remoteIp,
+                        const std::string& remoteAet) = 0;
   };
 }
--- a/OrthancServer/DicomProtocol/IMoveRequestHandler.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DicomProtocol/IMoveRequestHandler.h	Wed Sep 30 13:23:31 2015 +0200
@@ -68,7 +68,9 @@
     }
 
     virtual IMoveRequestIterator* Handle(const std::string& target,
-                                         const DicomMap& input) = 0;
+                                         const DicomMap& input,
+                                         const std::string& remoteIp,
+                                         const std::string& remoteAet) = 0;
   };
 
 }
--- a/OrthancServer/DicomProtocol/IStoreRequestHandler.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DicomProtocol/IStoreRequestHandler.h	Wed Sep 30 13:23:31 2015 +0200
@@ -50,6 +50,7 @@
     virtual void Handle(const std::string& dicomFile,
                         const DicomMap& dicomSummary,
                         const Json::Value& dicomJson,
+                        const std::string& remoteIp,
                         const std::string& remoteAet,
                         const std::string& calledAet) = 0;
   };
--- a/OrthancServer/DicomProtocol/RemoteModalityParameters.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DicomProtocol/RemoteModalityParameters.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -48,15 +48,17 @@
   {
   }
 
-  void RemoteModalityParameters::SetPort(int port)
+  RemoteModalityParameters::RemoteModalityParameters(const std::string& aet,
+                                                     const std::string& host,
+                                                     uint16_t port,
+                                                     ModalityManufacturer manufacturer)
   {
-    if (port <= 0 || port >= 65535)
-    {
-      throw OrthancException(ErrorCode_ParameterOutOfRange);
-    }
+    SetApplicationEntityTitle(aet);
+    SetHost(host);
+    SetPort(port);
+    SetManufacturer(manufacturer);
+  }
 
-    port_ = port;
-  }
 
   void RemoteModalityParameters::FromJson(const Json::Value& modality)
   {
@@ -72,13 +74,20 @@
     const Json::Value& portValue = modality.get(2u, "");
     try
     {
-      SetPort(portValue.asInt());
+      int tmp = portValue.asInt();
+
+      if (tmp <= 0 || tmp >= 65535)
+      {
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+
+      SetPort(static_cast<uint16_t>(tmp));
     }
     catch (std::runtime_error /* error inside JsonCpp */)
     {
       try
       {
-        SetPort(boost::lexical_cast<int>(portValue.asString()));
+        SetPort(boost::lexical_cast<uint16_t>(portValue.asString()));
       }
       catch (boost::bad_lexical_cast)
       {
--- a/OrthancServer/DicomProtocol/RemoteModalityParameters.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DicomProtocol/RemoteModalityParameters.h	Wed Sep 30 13:23:31 2015 +0200
@@ -34,6 +34,7 @@
 
 #include "../ServerEnumerations.h"
 
+#include <stdint.h>
 #include <string>
 #include <json/json.h>
 
@@ -41,17 +42,20 @@
 {
   class RemoteModalityParameters
   {
-    // TODO Use the flyweight pattern for this class
-
   private:
     std::string aet_;
     std::string host_;
-    int port_;
+    uint16_t port_;
     ModalityManufacturer manufacturer_;
 
   public:
     RemoteModalityParameters();
 
+    RemoteModalityParameters(const std::string& aet,
+                             const std::string& host,
+                             uint16_t port,
+                             ModalityManufacturer manufacturer);
+
     const std::string& GetApplicationEntityTitle() const
     {
       return aet_;
@@ -72,12 +76,15 @@
       host_ = host;
     }
     
-    int GetPort() const
+    uint16_t GetPort() const
     {
       return port_;
     }
 
-    void SetPort(int port);
+    void SetPort(uint16_t port)
+    {
+      port_ = port;
+    }
 
     ModalityManufacturer GetManufacturer() const
     {
--- a/OrthancServer/DicomProtocol/ReusableDicomUserConnection.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DicomProtocol/ReusableDicomUserConnection.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,10 +33,9 @@
 #include "../PrecompiledHeadersServer.h"
 #include "ReusableDicomUserConnection.h"
 
+#include "../../Core/Logging.h"
 #include "../../Core/OrthancException.h"
 
-#include <glog/logging.h>
-
 namespace Orthanc
 {
   static boost::posix_time::ptime Now()
@@ -44,16 +43,15 @@
     return boost::posix_time::microsec_clock::local_time();
   }
 
-  void ReusableDicomUserConnection::Open(const std::string& remoteAet,
-                                         const std::string& address,
-                                         int port,
-                                         ModalityManufacturer manufacturer)
+  void ReusableDicomUserConnection::Open(const std::string& localAet,
+                                         const RemoteModalityParameters& remote)
   {
     if (connection_ != NULL &&
-        connection_->GetRemoteApplicationEntityTitle() == remoteAet &&
-        connection_->GetRemoteHost() == address &&
-        connection_->GetRemotePort() == port &&
-        connection_->GetRemoteManufacturer() == manufacturer)
+        connection_->GetLocalApplicationEntityTitle() == localAet &&
+        connection_->GetRemoteApplicationEntityTitle() == remote.GetApplicationEntityTitle() &&
+        connection_->GetRemoteHost() == remote.GetHost() &&
+        connection_->GetRemotePort() == remote.GetPort() &&
+        connection_->GetRemoteManufacturer() == remote.GetManufacturer())
     {
       // The current connection can be reused
       LOG(INFO) << "Reusing the previous SCU connection";
@@ -63,11 +61,8 @@
     Close();
 
     connection_ = new DicomUserConnection();
-    connection_->SetLocalApplicationEntityTitle(localAet_);
-    connection_->SetRemoteApplicationEntityTitle(remoteAet);
-    connection_->SetRemoteHost(address);
-    connection_->SetRemotePort(port);
-    connection_->SetRemoteManufacturer(manufacturer);
+    connection_->SetLocalApplicationEntityTitle(localAet);
+    connection_->SetRemoteModality(remote);
     connection_->Open();
   }
     
@@ -103,24 +98,13 @@
     }
   }
     
-  ReusableDicomUserConnection::Locker::Locker(ReusableDicomUserConnection& that,
-                                              const std::string& aet,
-                                              const std::string& address,
-                                              int port,
-                                              ModalityManufacturer manufacturer) :
-    ::Orthanc::Locker(that)
-  {
-    that.Open(aet, address, port, manufacturer);
-    connection_ = that.connection_;
-  }
-
 
   ReusableDicomUserConnection::Locker::Locker(ReusableDicomUserConnection& that,
+                                              const std::string& localAet,
                                               const RemoteModalityParameters& remote) :
     ::Orthanc::Locker(that)
   {
-    that.Open(remote.GetApplicationEntityTitle(), remote.GetHost(), 
-              remote.GetPort(), remote.GetManufacturer());
+    that.Open(localAet, remote);
     connection_ = that.connection_;    
   }
 
@@ -137,8 +121,7 @@
 
   ReusableDicomUserConnection::ReusableDicomUserConnection() : 
     connection_(NULL), 
-    timeBeforeClose_(boost::posix_time::seconds(5)),  // By default, close connection after 5 seconds
-    localAet_("ORTHANC")
+    timeBeforeClose_(boost::posix_time::seconds(5))  // By default, close connection after 5 seconds
   {
     lastUse_ = Now();
     continue_ = true;
@@ -147,9 +130,11 @@
 
   ReusableDicomUserConnection::~ReusableDicomUserConnection()
   {
-    continue_ = false;
-    closeThread_.join();
-    Close();
+    if (continue_)
+    {
+      LOG(ERROR) << "INTERNAL ERROR: ReusableDicomUserConnection::Finalize() should be invoked manually to avoid mess in the destruction order!";
+      Finalize();
+    }
   }
 
   void ReusableDicomUserConnection::SetMillisecondsBeforeClose(uint64_t ms)
@@ -164,13 +149,6 @@
     timeBeforeClose_ = boost::posix_time::milliseconds(ms);
   }
 
-  void ReusableDicomUserConnection::SetLocalApplicationEntityTitle(const std::string& aet)
-  {
-    boost::mutex::scoped_lock lock(mutex_);
-    Close();
-    localAet_ = aet;
-  }
-
   void ReusableDicomUserConnection::Lock()
   {
     mutex_.lock();
@@ -189,5 +167,21 @@
     lastUse_ = Now();
     mutex_.unlock();
   }
+
+  
+  void ReusableDicomUserConnection::Finalize()
+  {
+    if (continue_)
+    {
+      continue_ = false;
+
+      if (closeThread_.joinable())
+      {
+        closeThread_.join();
+      }
+
+      Close();
+    }
+  }
 }
 
--- a/OrthancServer/DicomProtocol/ReusableDicomUserConnection.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/DicomProtocol/ReusableDicomUserConnection.h	Wed Sep 30 13:23:31 2015 +0200
@@ -49,12 +49,9 @@
     boost::posix_time::time_duration timeBeforeClose_;
     boost::posix_time::ptime lastUse_;
     boost::thread closeThread_;
-    std::string localAet_;
 
-    void Open(const std::string& remoteAet,
-              const std::string& address,
-              int port,
-              ModalityManufacturer manufacturer);
+    void Open(const std::string& localAet,
+              const RemoteModalityParameters& remote);
     
     void Close();
 
@@ -73,14 +70,9 @@
 
     public:
       Locker(ReusableDicomUserConnection& that,
+             const std::string& localAet,
              const RemoteModalityParameters& remote);
 
-      Locker(ReusableDicomUserConnection& that,
-             const std::string& aet,
-             const std::string& address,
-             int port,
-             ModalityManufacturer manufacturer);
-
       DicomUserConnection& GetConnection();
     };
 
@@ -88,16 +80,9 @@
 
     virtual ~ReusableDicomUserConnection();
 
-    uint64_t GetMillisecondsBeforeClose() const
-    {
-      return static_cast<uint64_t>(timeBeforeClose_.total_milliseconds());
-    }
-
     void SetMillisecondsBeforeClose(uint64_t ms);
 
-    const std::string& GetLocalApplicationEntityTitle() const;
-
-    void SetLocalApplicationEntityTitle(const std::string& aet);
+    void Finalize();
   };
 }
 
--- a/OrthancServer/ExportedResource.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/ExportedResource.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -30,6 +30,7 @@
  **/
 
 
+#include "PrecompiledHeadersServer.h"
 #include "ExportedResource.h"
 
 #include "../Core/OrthancException.h"
--- a/OrthancServer/FromDcmtkBridge.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/FromDcmtkBridge.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -42,9 +42,10 @@
 #include "FromDcmtkBridge.h"
 #include "ToDcmtkBridge.h"
 #include "OrthancInitialization.h"
+#include "../Core/Logging.h"
 #include "../Core/Toolbox.h"
 #include "../Core/OrthancException.h"
-#include "../Core/ImageFormats/PngWriter.h"
+#include "../Core/Images/PngWriter.h"
 #include "../Core/Uuid.h"
 #include "../Core/DicomFormat/DicomString.h"
 #include "../Core/DicomFormat/DicomNullValue.h"
@@ -54,6 +55,7 @@
 #include <limits>
 
 #include <boost/lexical_cast.hpp>
+#include <boost/filesystem.hpp>
 
 #include <dcmtk/dcmdata/dcchrstr.h>
 #include <dcmtk/dcmdata/dcdicent.h>
@@ -90,9 +92,9 @@
 #include <dcmtk/dcmdata/dcpxitem.h>
 #include <dcmtk/dcmdata/dcvrat.h>
 
+#include <dcmtk/dcmnet/dul.h>
 
 #include <boost/math/special_functions/round.hpp>
-#include <glog/logging.h>
 #include <dcmtk/dcmdata/dcostrmb.h>
 
 
@@ -119,10 +121,173 @@
   }
 
 
+#if DCMTK_USE_EMBEDDED_DICTIONARIES == 1
+  static void LoadEmbeddedDictionary(DcmDataDictionary& dictionary,
+                                     EmbeddedResources::FileResourceId resource)
+  {
+    Toolbox::TemporaryFile tmp;
+
+    FILE* fp = fopen(tmp.GetPath().c_str(), "wb");
+    fwrite(EmbeddedResources::GetFileResourceBuffer(resource), 
+           EmbeddedResources::GetFileResourceSize(resource), 1, fp);
+    fclose(fp);
+
+    if (!dictionary.loadDictionary(tmp.GetPath().c_str()))
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+  }
+                             
+#else
+  static void LoadExternalDictionary(DcmDataDictionary& dictionary,
+                                     const std::string& directory,
+                                     const std::string& filename)
+  {
+    boost::filesystem::path p = directory;
+    p = p / filename;
+
+    LOG(WARNING) << "Loading the external DICOM dictionary " << p;
+
+    if (!dictionary.loadDictionary(p.string().c_str()))
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+  }
+                            
+#endif
+
+
+  namespace
+  {
+    class DictionaryLocker
+    {
+    private:
+      DcmDataDictionary& dictionary_;
+
+    public:
+      DictionaryLocker() : dictionary_(dcmDataDict.wrlock())
+      {
+      }
+
+      ~DictionaryLocker()
+      {
+        dcmDataDict.unlock();
+      }
+
+      DcmDataDictionary& operator*()
+      {
+        return dictionary_;
+      }
+
+      DcmDataDictionary* operator->()
+      {
+        return &dictionary_;
+      }
+    };
+  }
+
+
+  void FromDcmtkBridge::InitializeDictionary()
+  {
+    /* Disable "gethostbyaddr" (which results in memory leaks) and use raw IP addresses */
+    dcmDisableGethostbyaddr.set(OFTrue);
+
+    {
+      DictionaryLocker locker;
+
+      locker->clear();
+
+#if DCMTK_USE_EMBEDDED_DICTIONARIES == 1
+      LOG(WARNING) << "Loading the embedded dictionaries";
+      /**
+       * Do not load DICONDE dictionary, it breaks the other tags. The
+       * command "strace storescu 2>&1 |grep dic" shows that DICONDE
+       * dictionary is not loaded by storescu.
+       **/
+      //LoadEmbeddedDictionary(*locker, EmbeddedResources::DICTIONARY_DICONDE);
+
+      LoadEmbeddedDictionary(*locker, EmbeddedResources::DICTIONARY_DICOM);
+      LoadEmbeddedDictionary(*locker, EmbeddedResources::DICTIONARY_PRIVATE);
+
+#elif defined(__linux) || defined(__FreeBSD_kernel__)
+      std::string path = DCMTK_DICTIONARY_DIR;
+
+      const char* env = std::getenv(DCM_DICT_ENVIRONMENT_VARIABLE);
+      if (env != NULL)
+      {
+        path = std::string(env);
+      }
+
+      LoadExternalDictionary(*locker, path, "dicom.dic");
+      LoadExternalDictionary(*locker, path, "private.dic");
+
+#else
+#error Support your platform here
+#endif
+    }
+
+    /* make sure data dictionary is loaded */
+    if (!dcmDataDict.isDictionaryLoaded())
+    {
+      LOG(ERROR) << "No DICOM dictionary loaded, check environment variable: " << DCM_DICT_ENVIRONMENT_VARIABLE;
+      throw OrthancException(ErrorCode_InternalError);
+    }
+
+    {
+      // Test the dictionary with a simple DICOM tag
+      DcmTag key(0x0010, 0x1030); // This is PatientWeight
+      if (key.getEVR() != EVR_DS)
+      {
+        LOG(ERROR) << "The DICOM dictionary has not been correctly read";
+        throw OrthancException(ErrorCode_InternalError);
+      }
+    }
+  }
+
+
+  void FromDcmtkBridge::RegisterDictionaryTag(const DicomTag& tag,
+                                              const DcmEVR& vr,
+                                              const std::string& name,
+                                              unsigned int minMultiplicity,
+                                              unsigned int maxMultiplicity)
+  {
+    if (minMultiplicity < 1)
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    if (maxMultiplicity == 0)
+    {
+      maxMultiplicity = DcmVariableVM;
+    }
+    else if (maxMultiplicity < minMultiplicity)
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+    
+    std::auto_ptr<DcmDictEntry>  entry(new DcmDictEntry(tag.GetGroup(),
+                                                        tag.GetElement(),
+                                                        vr, name.c_str(),
+                                                        static_cast<int>(minMultiplicity),
+                                                        static_cast<int>(maxMultiplicity),
+                                                        NULL    /* version */,
+                                                        OFTrue  /* doCopyString */,
+                                                        NULL    /* private creator */));
+
+    entry->setGroupRangeRestriction(DcmDictRange_Unspecified);
+    entry->setElementRangeRestriction(DcmDictRange_Unspecified);
+
+    {
+      DictionaryLocker locker;
+      locker->addEntry(entry.release());
+    }
+  }
+
+
   Encoding FromDcmtkBridge::DetectEncoding(DcmDataset& dataset)
   {
     // By default, Latin1 encoding is assumed
-    std::string s = Configuration::GetGlobalStringParameter("DefaultEncoding", "");
+    std::string s = Configuration::GetGlobalStringParameter("DefaultEncoding", "Latin1");
     Encoding encoding = s.empty() ? Encoding_Latin1 : StringToEncoding(s.c_str());
 
     OFString tmp;
@@ -215,18 +380,25 @@
   {
     if (!element.isLeaf())
     {
-      throw OrthancException("Only applicable to leaf elements");
+      // This function is only applicable to leaf elements
+      throw OrthancException(ErrorCode_BadParameterType);
     }
 
     if (element.isaString())
     {
       char *c;
-      if (element.getString(c).good() &&
-          c != NULL)
+      if (element.getString(c).good())
       {
-        std::string s(c);
-        std::string utf8 = Toolbox::ConvertToUtf8(s, encoding);
-        return new DicomString(utf8);
+        if (c == NULL)  // This case corresponds to the empty string
+        {
+          return new DicomString("");
+        }
+        else
+        {
+          std::string s(c);
+          std::string utf8 = Toolbox::ConvertToUtf8(s, encoding);
+          return new DicomString(utf8);
+        }
       }
       else
       {
@@ -588,7 +760,7 @@
     if (entry == NULL)
     {
       dcmDataDict.unlock();
-      throw OrthancException("Unknown DICOM tag");
+      throw OrthancException(ErrorCode_UnknownDicomTag);
     }
     else
     {
@@ -605,12 +777,19 @@
     }
     else
     {
-      throw OrthancException("Unknown DICOM tag");
+      throw OrthancException(ErrorCode_UnknownDicomTag);
     }
 #endif
   }
 
 
+  bool FromDcmtkBridge::IsUnknownTag(const DicomTag& tag)
+  {
+    DcmTag tmp(tag.GetGroup(), tag.GetElement());
+    return tmp.isUnknownVR();
+  }
+
+
   void FromDcmtkBridge::Print(FILE* fp, const DicomMap& m)
   {
     for (DicomMap::Map::const_iterator 
@@ -624,7 +803,8 @@
 
 
   void FromDcmtkBridge::ToJson(Json::Value& result,
-                               const DicomMap& values)
+                               const DicomMap& values,
+                               bool simplify)
   {
     if (result.type() != Json::objectValue)
     {
@@ -636,7 +816,29 @@
     for (DicomMap::Map::const_iterator 
            it = values.map_.begin(); it != values.map_.end(); ++it)
     {
-      result[GetName(it->first)] = it->second->AsString();
+      if (simplify)
+      {
+        result[GetName(it->first)] = it->second->AsString();
+      }
+      else
+      {
+        Json::Value value = Json::objectValue;
+
+        value["Name"] = GetName(it->first);
+
+        if (it->second->IsNull())
+        {
+          value["Type"] = "Null";
+          value["Value"] = Json::nullValue;
+        }
+        else
+        {
+          value["Type"] = "String";
+          value["Value"] = it->second->AsString();
+        }
+
+        result[it->first.Format()] = value;
+      }
     }
   }
 
@@ -696,6 +898,7 @@
     // Create the meta-header information
     DcmFileFormat ff(&dataSet);
     ff.validateMetaInfo(xfer);
+    ff.removeInvalidGroups();
 
     // Create a memory buffer with the proper size
     uint32_t s = ff.calcElementLength(xfer, encodingType);
@@ -720,4 +923,28 @@
       return false;
     }
   }
+
+
+  ValueRepresentation FromDcmtkBridge::GetValueRepresentation(const DicomTag& tag)
+  {
+    DcmTag t(tag.GetGroup(), tag.GetElement());
+    switch (t.getEVR())
+    {
+      case EVR_PN:
+        return ValueRepresentation_PatientName;
+
+      case EVR_DA:
+        return ValueRepresentation_Date;
+
+      case EVR_DT:
+        return ValueRepresentation_DateTime;
+
+      case EVR_TM:
+        return ValueRepresentation_Time;
+
+      default:
+        return ValueRepresentation_Other;
+    }
+  }
+
 }
--- a/OrthancServer/FromDcmtkBridge.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/FromDcmtkBridge.h	Wed Sep 30 13:23:31 2015 +0200
@@ -44,6 +44,14 @@
   class FromDcmtkBridge
   {
   public:
+    static void InitializeDictionary();
+
+    static void RegisterDictionaryTag(const DicomTag& tag,
+                                      const DcmEVR& vr,
+                                      const std::string& name,
+                                      unsigned int minMultiplicity,
+                                      unsigned int maxMultiplicity);
+
     static Encoding DetectEncoding(DcmDataset& dataset);
 
     static void Convert(DicomMap& target, DcmDataset& dataset);
@@ -56,6 +64,8 @@
 
     static bool IsPrivateTag(const DicomTag& tag);
 
+    static bool IsUnknownTag(const DicomTag& tag);
+
     static DicomValue* ConvertLeafElement(DcmElement& element,
                                           Encoding encoding);
 
@@ -99,11 +109,14 @@
                       const DicomMap& m);
 
     static void ToJson(Json::Value& result,
-                       const DicomMap& values);
+                       const DicomMap& values,
+                       bool simplify);
 
     static std::string GenerateUniqueIdentifier(ResourceType level);
 
     static bool SaveToMemoryBuffer(std::string& buffer,
                                    DcmDataset& dataSet);
+
+    static ValueRepresentation GetValueRepresentation(const DicomTag& tag);
   };
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/IDatabaseListener.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,55 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include <string>
+#include "ServerEnumerations.h"
+#include "ServerIndexChange.h"
+
+namespace Orthanc
+{
+  class IDatabaseListener
+  {
+  public:
+    virtual ~IDatabaseListener()
+    {
+    }
+
+    virtual void SignalRemainingAncestor(ResourceType parentType,
+                                         const std::string& publicId) = 0;
+
+    virtual void SignalFileDeleted(const FileInfo& info) = 0;
+
+    virtual void SignalChange(const ServerIndexChange& change) = 0;
+  };
+}
--- a/OrthancServer/IDatabaseWrapper.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/IDatabaseWrapper.h	Wed Sep 30 13:23:31 2015 +0200
@@ -34,8 +34,9 @@
 
 #include "../Core/DicomFormat/DicomMap.h"
 #include "../Core/SQLite/ITransaction.h"
+#include "../Core/FileStorage/IStorageArea.h"
 #include "../Core/FileStorage/FileInfo.h"
-#include "IServerIndexListener.h"
+#include "IDatabaseListener.h"
 #include "ExportedResource.h"
 
 #include <list>
@@ -81,6 +82,10 @@
     virtual void GetAllPublicIds(std::list<std::string>& target,
                                  ResourceType resourceType) = 0;
 
+    virtual void GetAllPublicIds(std::list<std::string>& target,
+                                 ResourceType resourceType,
+                                 size_t since,
+                                 size_t limit) = 0;
 
     virtual void GetChanges(std::list<ServerIndexChange>& target /*out*/,
                             bool& done /*out*/,
@@ -176,6 +181,11 @@
 
     virtual SQLite::ITransaction* StartTransaction() = 0;
 
-    virtual void SetListener(IServerIndexListener& listener) = 0;
+    virtual void SetListener(IDatabaseListener& listener) = 0;
+
+    virtual unsigned int GetDatabaseVersion() = 0;
+
+    virtual void Upgrade(unsigned int targetVersion,
+                         IStorageArea& storageArea) = 0;
   };
 }
--- a/OrthancServer/IServerIndexListener.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include <string>
-#include "ServerEnumerations.h"
-#include "ServerIndexChange.h"
-
-namespace Orthanc
-{
-  class IServerIndexListener
-  {
-  public:
-    virtual ~IServerIndexListener()
-    {
-    }
-
-    virtual void SignalRemainingAncestor(ResourceType parentType,
-                                         const std::string& publicId) = 0;
-
-    virtual void SignalFileDeleted(const FileInfo& info) = 0;
-
-    virtual void SignalChange(const ServerIndexChange& change) = 0;
-  };
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/IServerListener.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,58 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "DicomInstanceToStore.h"
+#include "ServerIndexChange.h"
+
+#include <json/value.h>
+
+namespace Orthanc
+{
+  class IServerListener
+  {
+  public:
+    virtual ~IServerListener()
+    {
+    }
+
+    virtual void SignalStoredInstance(const std::string& publicId,
+                                      DicomInstanceToStore& instance,
+                                      const Json::Value& simplifiedTags) = 0;                                      
+
+    virtual void SignalChange(const ServerIndexChange& change) = 0;
+
+    virtual bool FilterIncomingInstance(const DicomInstanceToStore& instance,
+                                        const Json::Value& simplified) = 0;
+  };
+}
--- a/OrthancServer/Internals/CommandDispatcher.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Internals/CommandDispatcher.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -86,10 +86,10 @@
 #include "StoreScp.h"
 #include "MoveScp.h"
 #include "../../Core/Toolbox.h"
+#include "../../Core/Logging.h"
 
 #include <dcmtk/dcmnet/dcasccfg.h>      /* for class DcmAssociationConfiguration */
 #include <boost/lexical_cast.hpp>
-#include <glog/logging.h>
 
 #define ORTHANC_PROMISCUOUS 1
 
@@ -771,11 +771,11 @@
         if (supported && 
             request != DicomRequestType_Echo &&  // Always allow incoming ECHO requests
             filter_ != NULL &&
-            !filter_->IsAllowedRequest(callingIP_, callingAETitle_, request))
+            !filter_->IsAllowedRequest(remoteIp_, remoteAet_, request))
         {
           LOG(ERROR) << EnumerationToString(request) 
                      << " requests are disallowed for the AET \"" 
-                     << callingAETitle_ << "\"";
+                     << remoteAet_ << "\"";
           cond = DIMSE_ILLEGALASSOCIATION;
           supported = false;
           finished = true;
@@ -798,7 +798,7 @@
               {
                 std::auto_ptr<IStoreRequestHandler> handler
                   (server_.GetStoreRequestHandlerFactory().ConstructStoreRequestHandler());
-                cond = Internals::storeScp(assoc_, &msg, presID, *handler);
+                cond = Internals::storeScp(assoc_, &msg, presID, *handler, remoteIp_);
               }
               break;
 
@@ -807,7 +807,7 @@
               {
                 std::auto_ptr<IMoveRequestHandler> handler
                   (server_.GetMoveRequestHandlerFactory().ConstructMoveRequestHandler());
-                cond = Internals::moveScp(assoc_, &msg, presID, *handler);
+                cond = Internals::moveScp(assoc_, &msg, presID, *handler, remoteIp_, remoteAet_);
               }
               break;
 
@@ -816,7 +816,7 @@
               {
                 std::auto_ptr<IFindRequestHandler> handler
                   (server_.GetFindRequestHandlerFactory().ConstructFindRequestHandler());
-                cond = Internals::findScp(assoc_, &msg, presID, *handler, callingAETitle_);
+                cond = Internals::findScp(assoc_, &msg, presID, *handler, remoteIp_, remoteAet_);
               }
               break;
 
--- a/OrthancServer/Internals/CommandDispatcher.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Internals/CommandDispatcher.h	Wed Sep 30 13:23:31 2015 +0200
@@ -50,20 +50,20 @@
       uint32_t elapsedTimeSinceLastCommand_;
       const DicomServer& server_;
       T_ASC_Association* assoc_;
-      std::string callingIP_;
-      std::string callingAETitle_;
+      std::string remoteIp_;
+      std::string remoteAet_;
       IApplicationEntityFilter* filter_;
 
     public:
       CommandDispatcher(const DicomServer& server,
                         T_ASC_Association* assoc,
-                        const std::string& callingIP,
-                        const std::string& callingAETitle,
+                        const std::string& remoteIp,
+                        const std::string& remoteAet,
                         IApplicationEntityFilter* filter) :
         server_(server),
         assoc_(assoc),
-        callingIP_(callingIP),
-        callingAETitle_(callingAETitle),
+        remoteIp_(remoteIp),
+        remoteAet_(remoteAet),
         filter_(filter)
       {
         clientTimeout_ = server.GetClientTimeout();
--- a/OrthancServer/Internals/DicomImageDecoder.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Internals/DicomImageDecoder.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -80,15 +80,14 @@
 #include "../PrecompiledHeadersServer.h"
 #include "DicomImageDecoder.h"
 
+#include "../../Core/Logging.h"
 #include "../../Core/OrthancException.h"
-#include "../../Core/ImageFormats/ImageProcessing.h"
-#include "../../Core/ImageFormats/PngWriter.h"  // TODO REMOVE THIS
+#include "../../Core/Images/ImageProcessing.h"
+#include "../../Core/Images/PngWriter.h"  // TODO REMOVE THIS
 #include "../../Core/DicomFormat/DicomIntegerPixelAccessor.h"
 #include "../ToDcmtkBridge.h"
 #include "../FromDcmtkBridge.h"
 
-#include <glog/logging.h>
-
 #include <boost/lexical_cast.hpp>
 
 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
--- a/OrthancServer/Internals/DicomImageDecoder.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Internals/DicomImageDecoder.h	Wed Sep 30 13:23:31 2015 +0200
@@ -34,7 +34,7 @@
 
 #include <dcmtk/dcmdata/dcfilefo.h>
 
-#include "../../Core/ImageFormats/ImageBuffer.h"
+#include "../../Core/Images/ImageBuffer.h"
 
 namespace Orthanc
 {
--- a/OrthancServer/Internals/FindScp.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Internals/FindScp.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -84,9 +84,9 @@
 
 #include "../FromDcmtkBridge.h"
 #include "../ToDcmtkBridge.h"
+#include "../../Core/Logging.h"
 #include "../../Core/OrthancException.h"
 
-#include <glog/logging.h>
 
 
 namespace Orthanc
@@ -99,7 +99,8 @@
       DicomMap input_;
       DicomFindAnswers answers_;
       DcmDataset* lastRequest_;
-      const std::string* callingAETitle_;
+      const std::string* remoteIp_;
+      const std::string* remoteAet_;
       bool noCroppingOfResults_;
     };
 
@@ -126,7 +127,8 @@
 
         try
         {
-          data.noCroppingOfResults_ = data.handler_->Handle(data.answers_, data.input_, *data.callingAETitle_);
+          data.noCroppingOfResults_ = data.handler_->Handle(data.answers_, data.input_, 
+                                                            *data.remoteIp_, *data.remoteAet_);
         }
         catch (OrthancException& e)
         {
@@ -174,12 +176,14 @@
                                  T_DIMSE_Message * msg, 
                                  T_ASC_PresentationContextID presID,
                                  IFindRequestHandler& handler,
-                                 const std::string& callingAETitle)
+                                 const std::string& remoteIp,
+                                 const std::string& remoteAet)
   {
     FindScpData data;
     data.lastRequest_ = NULL;
     data.handler_ = &handler;
-    data.callingAETitle_ = &callingAETitle;
+    data.remoteIp_ = &remoteIp;
+    data.remoteAet_ = &remoteAet;
     data.noCroppingOfResults_ = true;
 
     OFCondition cond = DIMSE_findProvider(assoc, presID, &msg->msg.CFindRQ, 
--- a/OrthancServer/Internals/FindScp.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Internals/FindScp.h	Wed Sep 30 13:23:31 2015 +0200
@@ -44,6 +44,7 @@
                         T_DIMSE_Message * msg, 
                         T_ASC_PresentationContextID presID,
                         IFindRequestHandler& handler,
-                        const std::string& callingAETitle);
+                        const std::string& remoteIp,
+                        const std::string& remoteAet);
   }
 }
--- a/OrthancServer/Internals/MoveScp.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Internals/MoveScp.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -86,10 +86,9 @@
 
 #include "../FromDcmtkBridge.h"
 #include "../ToDcmtkBridge.h"
+#include "../../Core/Logging.h"
 #include "../../Core/OrthancException.h"
 
-#include <glog/logging.h>
-
 
 namespace Orthanc
 {
@@ -105,6 +104,8 @@
       unsigned int failureCount_;
       unsigned int warningCount_;
       std::auto_ptr<IMoveRequestIterator> iterator_;
+      const std::string* remoteIp_;
+      const std::string* remoteAet_;
     };
 
 
@@ -131,7 +132,8 @@
 
         try
         {
-          data.iterator_.reset(data.handler_->Handle(data.target_, data.input_));
+          data.iterator_.reset(data.handler_->Handle(data.target_, data.input_, 
+                                                     *data.remoteIp_, *data.remoteAet_));
 
           if (data.iterator_.get() == NULL)
           {
@@ -211,12 +213,16 @@
   OFCondition Internals::moveScp(T_ASC_Association * assoc, 
                                  T_DIMSE_Message * msg, 
                                  T_ASC_PresentationContextID presID,
-                                 IMoveRequestHandler& handler)
+                                 IMoveRequestHandler& handler,
+                                 const std::string& remoteIp,
+                                 const std::string& remoteAet)
   {
     MoveScpData data;
     data.target_ = std::string(msg->msg.CMoveRQ.MoveDestination);
     data.lastRequest_ = NULL;
     data.handler_ = &handler;
+    data.remoteIp_ = &remoteIp;
+    data.remoteAet_ = &remoteAet;
 
     OFCondition cond = DIMSE_moveProvider(assoc, presID, &msg->msg.CMoveRQ, 
                                           MoveScpCallback, &data,
--- a/OrthancServer/Internals/MoveScp.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Internals/MoveScp.h	Wed Sep 30 13:23:31 2015 +0200
@@ -43,6 +43,8 @@
     OFCondition moveScp(T_ASC_Association * assoc, 
                         T_DIMSE_Message * msg, 
                         T_ASC_PresentationContextID presID,
-                        IMoveRequestHandler& handler);
+                        IMoveRequestHandler& handler,
+                        const std::string& remoteIp,
+                        const std::string& remoteAet);
   }
 }
--- a/OrthancServer/Internals/StoreScp.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Internals/StoreScp.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -86,13 +86,13 @@
 #include "../ServerToolbox.h"
 #include "../ToDcmtkBridge.h"
 #include "../../Core/OrthancException.h"
+#include "../../Core/Logging.h"
 
 #include <dcmtk/dcmdata/dcfilefo.h>
 #include <dcmtk/dcmdata/dcmetinf.h>
 #include <dcmtk/dcmdata/dcostrmb.h>
 #include <dcmtk/dcmdata/dcdeftag.h>
 #include <dcmtk/dcmnet/diutil.h>
-#include <glog/logging.h>
 
 
 namespace Orthanc
@@ -102,6 +102,7 @@
     struct StoreCallbackData
     {
       IStoreRequestHandler* handler;
+      const std::string* remoteIp;
       const char* remoteAET;
       const char* calledAET;
       const char* modality;
@@ -182,7 +183,7 @@
 
           // check the image to make sure it is consistent, i.e. that its sopClass and sopInstance correspond
           // to those mentioned in the request. If not, set the status in the response message variable.
-          if ((rsp->DimseStatus == STATUS_Success))
+          if (rsp->DimseStatus == STATUS_Success)
           {
             // which SOP class and SOP instance ?
             if (!DU_findSOPClassAndInstanceInDataSet(*imageDataSet, sopClass, sopInstance, /*opt_correctUIDPadding*/ OFFalse))
@@ -202,7 +203,7 @@
             {
               try
               {
-                cbdata->handler->Handle(buffer, summary, dicomJson, cbdata->remoteAET, cbdata->calledAET);
+                cbdata->handler->Handle(buffer, summary, dicomJson, *cbdata->remoteIp, cbdata->remoteAET, cbdata->calledAET);
               }
               catch (OrthancException& e)
               {
@@ -237,7 +238,8 @@
   OFCondition Internals::storeScp(T_ASC_Association * assoc, 
                                   T_DIMSE_Message * msg, 
                                   T_ASC_PresentationContextID presID,
-                                  IStoreRequestHandler& handler)
+                                  IStoreRequestHandler& handler,
+                                  const std::string& remoteIp)
   {
     OFCondition cond = EC_Normal;
     T_DIMSE_C_StoreRQ *req;
@@ -246,23 +248,24 @@
     req = &msg->msg.CStoreRQ;
 
     // intialize some variables
-    StoreCallbackData callbackData;
-    callbackData.handler = &handler;
-    callbackData.modality = dcmSOPClassUIDToModality(req->AffectedSOPClassUID/*, "UNKNOWN"*/);
-    if (callbackData.modality == NULL)
-      callbackData.modality = "UNKNOWN";
+    StoreCallbackData data;
+    data.handler = &handler;
+    data.remoteIp = &remoteIp;
+    data.modality = dcmSOPClassUIDToModality(req->AffectedSOPClassUID/*, "UNKNOWN"*/);
+    if (data.modality == NULL)
+      data.modality = "UNKNOWN";
 
-    callbackData.affectedSOPInstanceUID = req->AffectedSOPInstanceUID;
-    callbackData.messageID = req->MessageID;
+    data.affectedSOPInstanceUID = req->AffectedSOPInstanceUID;
+    data.messageID = req->MessageID;
     if (assoc && assoc->params)
     {
-      callbackData.remoteAET = assoc->params->DULparams.callingAPTitle;
-      callbackData.calledAET = assoc->params->DULparams.calledAPTitle;
+      data.remoteAET = assoc->params->DULparams.callingAPTitle;
+      data.calledAET = assoc->params->DULparams.calledAPTitle;
     }
     else
     {
-      callbackData.remoteAET = "";
-      callbackData.calledAET = "";
+      data.remoteAET = "";
+      data.calledAET = "";
     }
 
     DcmFileFormat dcmff;
@@ -278,7 +281,7 @@
     DcmDataset *dset = dcmff.getDataset();
 
     cond = DIMSE_storeProvider(assoc, presID, req, NULL, /*opt_useMetaheader*/OFFalse, &dset,
-                               storeScpCallback, &callbackData, 
+                               storeScpCallback, &data, 
                                /*opt_blockMode*/ DIMSE_BLOCKING, 
                                /*opt_dimse_timeout*/ 0);
 
--- a/OrthancServer/Internals/StoreScp.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Internals/StoreScp.h	Wed Sep 30 13:23:31 2015 +0200
@@ -43,6 +43,7 @@
     OFCondition storeScp(T_ASC_Association * assoc, 
                          T_DIMSE_Message * msg, 
                          T_ASC_PresentationContextID presID,
-                         IStoreRequestHandler& handler);
+                         IStoreRequestHandler& handler,
+                         const std::string& remoteIp);
   }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/LuaScripting.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,538 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "PrecompiledHeadersServer.h"
+#include "LuaScripting.h"
+
+#include "ServerContext.h"
+#include "OrthancInitialization.h"
+#include "../Core/Lua/LuaFunctionCall.h"
+#include "../Core/HttpServer/StringHttpOutput.h"
+#include "../Core/Logging.h"
+
+#include "Scheduler/DeleteInstanceCommand.h"
+#include "Scheduler/StoreScuCommand.h"
+#include "Scheduler/StorePeerCommand.h"
+#include "Scheduler/ModifyInstanceCommand.h"
+#include "Scheduler/CallSystemCommand.h"
+#include "OrthancRestApi/OrthancRestApi.h"
+
+#include <EmbeddedResources.h>
+
+
+namespace Orthanc
+{
+  ServerContext* LuaScripting::GetServerContext(lua_State *state)
+  {
+    const void* value = LuaContext::GetGlobalVariable(state, "_ServerContext");
+    return const_cast<ServerContext*>(reinterpret_cast<const ServerContext*>(value));
+  }
+
+
+  // Syntax in Lua: RestApiGet(uri, builtin)
+  int LuaScripting::RestApiGet(lua_State *state)
+  {
+    ServerContext* serverContext = GetServerContext(state);
+    if (serverContext == NULL)
+    {
+      LOG(ERROR) << "Lua: The Orthanc API is unavailable";
+      lua_pushnil(state);
+      return 1;
+    }
+
+    // Check the types of the arguments
+    int nArgs = lua_gettop(state);
+    if ((nArgs != 1 && nArgs != 2) || 
+        !lua_isstring(state, 1) ||                 // URI
+        (nArgs == 2 && !lua_isboolean(state, 2)))  // Restrict to built-in API?
+    {
+      LOG(ERROR) << "Lua: Bad parameters to RestApiGet()";
+      lua_pushnil(state);
+      return 1;
+    }
+
+    const char* uri = lua_tostring(state, 1);
+    bool builtin = (nArgs == 2 ? lua_toboolean(state, 2) != 0 : false);
+
+    try
+    {
+      std::string result;
+      if (HttpToolbox::SimpleGet(result, serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin), 
+                                 RequestOrigin_Lua, uri))
+      {
+        lua_pushlstring(state, result.c_str(), result.size());
+        return 1;
+      }
+    }
+    catch (OrthancException& e)
+    {
+      LOG(ERROR) << "Lua: " << e.What();
+    }
+
+    LOG(ERROR) << "Lua: Error in RestApiGet() for URI: " << uri;
+    lua_pushnil(state);
+    return 1;
+  }
+
+
+  int LuaScripting::RestApiPostOrPut(lua_State *state,
+                                     bool isPost)
+  {
+    ServerContext* serverContext = GetServerContext(state);
+    if (serverContext == NULL)
+    {
+      LOG(ERROR) << "Lua: The Orthanc API is unavailable";
+      lua_pushnil(state);
+      return 1;
+    }
+
+    // Check the types of the arguments
+    int nArgs = lua_gettop(state);
+    if ((nArgs != 2 && nArgs != 3) || 
+        !lua_isstring(state, 1) ||                 // URI
+        !lua_isstring(state, 2) ||                 // Body
+        (nArgs == 3 && !lua_isboolean(state, 3)))  // Restrict to built-in API?
+    {
+      LOG(ERROR) << "Lua: Bad parameters to " << (isPost ? "RestApiPost()" : "RestApiPut()");
+      lua_pushnil(state);
+      return 1;
+    }
+
+    const char* uri = lua_tostring(state, 1);
+    size_t bodySize = 0;
+    const char* bodyData = lua_tolstring(state, 2, &bodySize);
+    bool builtin = (nArgs == 3 ? lua_toboolean(state, 3) != 0 : false);
+
+    try
+    {
+      std::string result;
+      if (isPost ?
+          HttpToolbox::SimplePost(result, serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin), 
+                                  RequestOrigin_Lua, uri, bodyData, bodySize) :
+          HttpToolbox::SimplePut(result, serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin), 
+                                 RequestOrigin_Lua, uri, bodyData, bodySize))
+      {
+        lua_pushlstring(state, result.c_str(), result.size());
+        return 1;
+      }
+    }
+    catch (OrthancException& e)
+    {
+      LOG(ERROR) << "Lua: " << e.What();
+    }
+
+    LOG(ERROR) << "Lua: Error in " << (isPost ? "RestApiPost()" : "RestApiPut()") << " for URI: " << uri;
+    lua_pushnil(state);
+    return 1;
+  }
+
+
+  // Syntax in Lua: RestApiPost(uri, body, builtin)
+  int LuaScripting::RestApiPost(lua_State *state)
+  {
+    return RestApiPostOrPut(state, true);
+  }
+
+
+  // Syntax in Lua: RestApiPut(uri, body, builtin)
+  int LuaScripting::RestApiPut(lua_State *state)
+  {
+    return RestApiPostOrPut(state, false);
+  }
+
+
+  // Syntax in Lua: RestApiDelete(uri, builtin)
+  int LuaScripting::RestApiDelete(lua_State *state)
+  {
+    ServerContext* serverContext = GetServerContext(state);
+    if (serverContext == NULL)
+    {
+      LOG(ERROR) << "Lua: The Orthanc API is unavailable";
+      lua_pushnil(state);
+      return 1;
+    }
+
+    // Check the types of the arguments
+    int nArgs = lua_gettop(state);
+    if ((nArgs != 1 && nArgs != 2) || 
+        !lua_isstring(state, 1) ||                 // URI
+        (nArgs == 2 && !lua_isboolean(state, 2)))  // Restrict to built-in API?
+    {
+      LOG(ERROR) << "Lua: Bad parameters to RestApiDelete()";
+      lua_pushnil(state);
+      return 1;
+    }
+
+    const char* uri = lua_tostring(state, 1);
+    bool builtin = (nArgs == 2 ? lua_toboolean(state, 2) != 0 : false);
+
+    try
+    {
+      if (HttpToolbox::SimpleDelete(serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin), 
+                                    RequestOrigin_Lua, uri))
+      {
+        lua_pushboolean(state, 1);
+        return 1;
+      }
+    }
+    catch (OrthancException& e)
+    {
+      LOG(ERROR) << "Lua: " << e.What();
+    }
+
+    LOG(ERROR) << "Lua: Error in RestApiDelete() for URI: " << uri;
+      lua_pushnil(state);
+
+    return 1;
+  }
+
+
+  // Syntax in Lua: GetOrthancConfiguration()
+  int LuaScripting::GetOrthancConfiguration(lua_State *state)
+  {
+    Json::Value configuration;
+    Configuration::GetConfiguration(configuration);
+
+    LuaContext::GetLuaContext(state).PushJson(configuration);
+
+    return 1;
+  }
+
+
+  IServerCommand* LuaScripting::ParseOperation(const std::string& operation,
+                                               const Json::Value& parameters)
+  {
+    if (operation == "delete")
+    {
+      LOG(INFO) << "Lua script to delete resource " << parameters["Resource"].asString();
+      return new DeleteInstanceCommand(context_);
+    }
+
+    if (operation == "store-scu")
+    {
+      std::string localAet;
+      if (parameters.isMember("LocalAet"))
+      {
+        localAet = parameters["LocalAet"].asString();
+      }
+      else
+      {
+        localAet = context_.GetDefaultLocalApplicationEntityTitle();
+      }
+
+      std::string modality = parameters["Modality"].asString();
+      LOG(INFO) << "Lua script to send resource " << parameters["Resource"].asString()
+                << " to modality " << modality << " using Store-SCU";
+      return new StoreScuCommand(context_, localAet,
+                                 Configuration::GetModalityUsingSymbolicName(modality), true);
+    }
+
+    if (operation == "store-peer")
+    {
+      std::string peer = parameters["Peer"].asString();
+      LOG(INFO) << "Lua script to send resource " << parameters["Resource"].asString()
+                << " to peer " << peer << " using HTTP";
+
+      OrthancPeerParameters parameters;
+      Configuration::GetOrthancPeer(parameters, peer);
+      return new StorePeerCommand(context_, parameters, true);
+    }
+
+    if (operation == "modify")
+    {
+      LOG(INFO) << "Lua script to modify resource " << parameters["Resource"].asString();
+      DicomModification modification;
+      OrthancRestApi::ParseModifyRequest(modification, parameters);
+
+      std::auto_ptr<ModifyInstanceCommand> command(new ModifyInstanceCommand(context_, RequestOrigin_Lua, modification));
+      return command.release();
+    }
+
+    if (operation == "call-system")
+    {
+      LOG(INFO) << "Lua script to call system command on " << parameters["Resource"].asString();
+
+      const Json::Value& argsIn = parameters["Arguments"];
+      if (argsIn.type() != Json::arrayValue)
+      {
+        throw OrthancException(ErrorCode_BadParameterType);
+      }
+
+      std::vector<std::string> args;
+      args.reserve(argsIn.size());
+      for (Json::Value::ArrayIndex i = 0; i < argsIn.size(); ++i)
+      {
+        // http://jsoncpp.sourceforge.net/namespace_json.html#7d654b75c16a57007925868e38212b4e
+        switch (argsIn[i].type())
+        {
+          case Json::stringValue:
+            args.push_back(argsIn[i].asString());
+            break;
+
+          case Json::intValue:
+            args.push_back(boost::lexical_cast<std::string>(argsIn[i].asInt()));
+            break;
+
+          case Json::uintValue:
+            args.push_back(boost::lexical_cast<std::string>(argsIn[i].asUInt()));
+            break;
+
+          case Json::realValue:
+            args.push_back(boost::lexical_cast<std::string>(argsIn[i].asFloat()));
+            break;
+
+          default:
+            throw OrthancException(ErrorCode_BadParameterType);
+        }
+      }
+
+      return new CallSystemCommand(context_, parameters["Command"].asString(), args);
+    }
+
+    throw OrthancException(ErrorCode_ParameterOutOfRange);
+  }
+
+
+  void LuaScripting::InitializeJob()
+  {
+    lua_.Execute("_InitializeJob()");
+  }
+
+
+  void LuaScripting::SubmitJob(const std::string& description)
+  {
+    Json::Value operations;
+    LuaFunctionCall call2(lua_, "_AccessJob");
+    call2.ExecuteToJson(operations, false);
+     
+    if (operations.type() != Json::arrayValue)
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+
+    ServerJob job;
+    ServerCommandInstance* previousCommand = NULL;
+
+    for (Json::Value::ArrayIndex i = 0; i < operations.size(); ++i)
+    {
+      if (operations[i].type() != Json::objectValue ||
+          !operations[i].isMember("Operation"))
+      {
+        throw OrthancException(ErrorCode_InternalError);
+      }
+
+      const Json::Value& parameters = operations[i];
+      std::string operation = parameters["Operation"].asString();
+
+      ServerCommandInstance& command = job.AddCommand(ParseOperation(operation, operations[i]));
+        
+      if (!parameters.isMember("Resource"))
+      {
+        throw OrthancException(ErrorCode_InternalError);
+      }
+
+      std::string resource = parameters["Resource"].asString();
+      if (resource.empty())
+      {
+        previousCommand->ConnectOutput(command);
+      }
+      else 
+      {
+        command.AddInput(resource);
+      }
+
+      previousCommand = &command;
+    }
+
+    job.SetDescription(description);
+    context_.GetScheduler().Submit(job);
+  }
+
+
+  LuaScripting::LuaScripting(ServerContext& context) : context_(context)
+  {
+    lua_.SetGlobalVariable("_ServerContext", &context);
+    lua_.RegisterFunction("RestApiGet", RestApiGet);
+    lua_.RegisterFunction("RestApiPost", RestApiPost);
+    lua_.RegisterFunction("RestApiPut", RestApiPut);
+    lua_.RegisterFunction("RestApiDelete", RestApiDelete);
+    lua_.RegisterFunction("GetOrthancConfiguration", GetOrthancConfiguration);
+
+    lua_.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX);
+    lua_.SetHttpProxy(Configuration::GetGlobalStringParameter("HttpProxy", ""));
+  }
+
+
+  void LuaScripting::ApplyOnStoredInstance(const std::string& instanceId,
+                                           const Json::Value& simplifiedTags,
+                                           const Json::Value& metadata,
+                                           const DicomInstanceToStore& instance)
+  {
+    static const char* NAME = "OnStoredInstance";
+
+    if (lua_.IsExistingFunction(NAME))
+    {
+      InitializeJob();
+
+      LuaFunctionCall call(lua_, NAME);
+      call.PushString(instanceId);
+      call.PushJson(simplifiedTags);
+      call.PushJson(metadata);
+
+      Json::Value origin;
+      instance.GetOriginInformation(origin);
+      call.PushJson(origin);
+
+      call.Execute();
+
+      SubmitJob(std::string("Lua script: ") + NAME);
+    }
+  }
+
+
+  void LuaScripting::SignalStoredInstance(const std::string& publicId,
+                                          DicomInstanceToStore& instance,
+                                          const Json::Value& simplifiedTags)
+  {
+    boost::recursive_mutex::scoped_lock lock(mutex_);
+
+    Json::Value metadata = Json::objectValue;
+
+    for (ServerIndex::MetadataMap::const_iterator 
+           it = instance.GetMetadata().begin(); 
+         it != instance.GetMetadata().end(); ++it)
+    {
+      if (it->first.first == ResourceType_Instance)
+      {
+        metadata[EnumerationToString(it->first.second)] = it->second;
+      }
+    }
+
+    ApplyOnStoredInstance(publicId, simplifiedTags, metadata, instance);
+  }
+
+
+  
+  void LuaScripting::OnStableResource(const ServerIndexChange& change)
+  {
+    const char* name;
+
+    switch (change.GetChangeType())
+    {
+      case ChangeType_StablePatient:
+        name = "OnStablePatient";
+        break;
+
+      case ChangeType_StableStudy:
+        name = "OnStableStudy";
+        break;
+
+      case ChangeType_StableSeries:
+        name = "OnStableSeries";
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+    }
+
+
+    Json::Value tags, metadata;
+    if (context_.GetIndex().LookupResource(tags, change.GetPublicId(), change.GetResourceType()) &&
+        context_.GetIndex().GetMetadata(metadata, change.GetPublicId()))
+    {
+      boost::recursive_mutex::scoped_lock lock(mutex_);
+
+      if (lua_.IsExistingFunction(name))
+      {
+        InitializeJob();
+
+        LuaFunctionCall call(lua_, name);
+        call.PushString(change.GetPublicId());
+        call.PushJson(tags["MainDicomTags"]);
+        call.PushJson(metadata);
+        call.Execute();
+
+        SubmitJob(std::string("Lua script: ") + name);
+      }
+    }
+  }
+
+
+
+  void LuaScripting::SignalChange(const ServerIndexChange& change)
+  {
+    if (change.GetChangeType() == ChangeType_StablePatient ||
+        change.GetChangeType() == ChangeType_StableStudy ||
+        change.GetChangeType() == ChangeType_StableSeries)
+    {
+      OnStableResource(change);
+    }
+  }
+
+
+  bool LuaScripting::FilterIncomingInstance(const DicomInstanceToStore& instance,
+                                            const Json::Value& simplified)
+  {
+    static const char* NAME = "ReceivedInstanceFilter";
+
+    boost::recursive_mutex::scoped_lock lock(mutex_);
+
+    if (lua_.IsExistingFunction(NAME))
+    {
+      LuaFunctionCall call(lua_, NAME);
+      call.PushJson(simplified);
+
+      Json::Value origin;
+      instance.GetOriginInformation(origin);
+      call.PushJson(origin);
+
+      if (!call.ExecutePredicate())
+      {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+
+  void LuaScripting::Execute(const std::string& command)
+  {
+    LuaScripting::Locker locker(*this);
+      
+    if (locker.GetLua().IsExistingFunction(command.c_str()))
+    {
+      LuaFunctionCall call(locker.GetLua(), command.c_str());
+      call.Execute();
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/LuaScripting.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,107 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IServerListener.h"
+#include "../Core/Lua/LuaContext.h"
+#include "Scheduler/IServerCommand.h"
+
+namespace Orthanc
+{
+  class ServerContext;
+
+  class LuaScripting : public IServerListener
+  {
+  private:
+    static ServerContext* GetServerContext(lua_State *state);
+
+    static int RestApiPostOrPut(lua_State *state,
+                                bool isPost);
+    static int RestApiGet(lua_State *state);
+    static int RestApiPost(lua_State *state);
+    static int RestApiPut(lua_State *state);
+    static int RestApiDelete(lua_State *state);
+    static int GetOrthancConfiguration(lua_State *state);
+
+    void ApplyOnStoredInstance(const std::string& instanceId,
+                               const Json::Value& simplifiedDicom,
+                               const Json::Value& metadata,
+                               const DicomInstanceToStore& instance);
+
+    IServerCommand* ParseOperation(const std::string& operation,
+                                   const Json::Value& parameters);
+
+    void InitializeJob();
+
+    void SubmitJob(const std::string& description);
+
+    void OnStableResource(const ServerIndexChange& change);
+
+    boost::recursive_mutex    mutex_;
+    LuaContext      lua_;
+    ServerContext&  context_;
+
+  public:
+    class Locker : public boost::noncopyable
+    {
+    private:
+      LuaScripting& that_;
+      boost::recursive_mutex::scoped_lock lock_;
+
+    public:
+      Locker(LuaScripting& that) : 
+        that_(that), 
+        lock_(that.mutex_)
+      {
+      }
+
+      LuaContext& GetLua()
+      {
+        return that_.lua_;
+      }
+    };
+
+    LuaScripting(ServerContext& context);
+    
+    virtual void SignalStoredInstance(const std::string& publicId,
+                                      DicomInstanceToStore& instance,
+                                      const Json::Value& simplifiedTags);
+
+    virtual void SignalChange(const ServerIndexChange& change);
+
+    virtual bool FilterIncomingInstance(const DicomInstanceToStore& instance,
+                                        const Json::Value& simplifiedTags);
+
+    void Execute(const std::string& command);
+  };
+}
--- a/OrthancServer/OrthancFindRequestHandler.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/OrthancFindRequestHandler.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,168 +33,20 @@
 #include "PrecompiledHeadersServer.h"
 #include "OrthancFindRequestHandler.h"
 
-#include <glog/logging.h>
-#include <boost/regex.hpp> 
-
+#include "../Core/Logging.h"
 #include "../Core/DicomFormat/DicomArray.h"
 #include "ServerToolbox.h"
 #include "OrthancInitialization.h"
 #include "FromDcmtkBridge.h"
 
-namespace Orthanc
-{
-  static bool IsWildcard(const std::string& constraint)
-  {
-    return (constraint.find('-') != std::string::npos ||
-            constraint.find('*') != std::string::npos ||
-            constraint.find('\\') != std::string::npos ||
-            constraint.find('?') != std::string::npos);
-  }
-
-  static bool ApplyRangeConstraint(const std::string& value,
-                                   const std::string& constraint)
-  {
-    size_t separator = constraint.find('-');
-    std::string lower, upper, v;
-    Toolbox::ToLowerCase(lower, constraint.substr(0, separator));
-    Toolbox::ToLowerCase(upper, constraint.substr(separator + 1));
-    Toolbox::ToLowerCase(v, value);
-
-    if (lower.size() == 0 && upper.size() == 0)
-    {
-      return false;
-    }
-
-    if (lower.size() == 0)
-    {
-      return v <= upper;
-    }
+#include "ResourceFinder.h"
+#include "DicomFindQuery.h"
 
-    if (upper.size() == 0)
-    {
-      return v >= lower;
-    }
-    
-    return (v >= lower && v <= upper);
-  }
-
-
-  static bool ApplyListConstraint(const std::string& value,
-                                  const std::string& constraint)
-  {
-    std::string v1;
-    Toolbox::ToLowerCase(v1, value);
-
-    std::vector<std::string> items;
-    Toolbox::TokenizeString(items, constraint, '\\');
-
-    for (size_t i = 0; i < items.size(); i++)
-    {
-      std::string lower;
-      Toolbox::ToLowerCase(lower, items[i]);
-      if (lower == v1)
-      {
-        return true;
-      }
-    }
-
-    return false;
-  }
+#include <boost/regex.hpp> 
 
 
-  static bool Matches(const std::string& value,
-                      const std::string& constraint)
-  {
-    // http://www.itk.org/Wiki/DICOM_QueryRetrieve_Explained
-    // http://dicomiseasy.blogspot.be/2012/01/dicom-queryretrieve-part-i.html  
-
-    if (constraint.find('-') != std::string::npos)
-    {
-      return ApplyRangeConstraint(value, constraint);
-    }
-    
-    if (constraint.find('\\') != std::string::npos)
-    {
-      return ApplyListConstraint(value, constraint);
-    }
-
-    if (constraint.find('*') != std::string::npos ||
-        constraint.find('?') != std::string::npos)
-    {
-      // TODO - Cache the constructed regular expression
-      boost::regex pattern(Toolbox::WildcardToRegularExpression(constraint),
-                           boost::regex::icase /* case insensitive search */);
-      return boost::regex_match(value, pattern);
-    }
-    else
-    {
-      std::string v, c;
-      Toolbox::ToLowerCase(v, value);
-      Toolbox::ToLowerCase(c, constraint);
-      return v == c;
-    }
-  }
-
-
-  static bool LookupOneInstance(std::string& result,
-                                ServerIndex& index,
-                                const std::string& id,
-                                ResourceType type)
-  {
-    if (type == ResourceType_Instance)
-    {
-      result = id;
-      return true;
-    }
-
-    std::string childId;
-    
-    {
-      std::list<std::string> children;
-      index.GetChildInstances(children, id);
-
-      if (children.empty())
-      {
-        return false;
-      }
-
-      childId = children.front();
-    }
-
-    return LookupOneInstance(result, index, childId, GetChildResourceType(type));
-  }
-
-
-  static bool Matches(const Json::Value& resource,
-                      const DicomArray& query)
-  {
-    for (size_t i = 0; i < query.GetSize(); i++)
-    {
-      if (query.GetElement(i).GetValue().IsNull() ||
-          query.GetElement(i).GetTag() == DICOM_TAG_QUERY_RETRIEVE_LEVEL ||
-          query.GetElement(i).GetTag() == DICOM_TAG_SPECIFIC_CHARACTER_SET ||
-          query.GetElement(i).GetTag() == DICOM_TAG_MODALITIES_IN_STUDY)
-      {
-        continue;
-      }
-
-      std::string tag = query.GetElement(i).GetTag().Format();
-      std::string value;
-      if (resource.isMember(tag))
-      {
-        value = resource.get(tag, Json::arrayValue).get("Value", "").asString();
-      }
-
-      if (!Matches(value, query.GetElement(i).GetValue().AsString()))
-      {
-        return false;
-      }
-    }
-
-    return true;
-  }
-
-
+namespace Orthanc
+{
   static void AddAnswer(DicomFindAnswers& answers,
                         const Json::Value& resource,
                         const DicomArray& query)
@@ -203,8 +55,15 @@
 
     for (size_t i = 0; i < query.GetSize(); i++)
     {
-      if (query.GetElement(i).GetTag() != DICOM_TAG_QUERY_RETRIEVE_LEVEL &&
-          query.GetElement(i).GetTag() != DICOM_TAG_SPECIFIC_CHARACTER_SET)
+      // Fix issue 30 (QR response missing "Query/Retrieve Level" (008,0052))
+      if (query.GetElement(i).GetTag() == DICOM_TAG_QUERY_RETRIEVE_LEVEL)
+      {
+        result.SetValue(query.GetElement(i).GetTag(), query.GetElement(i).GetValue());
+      }
+      else if (query.GetElement(i).GetTag() == DICOM_TAG_SPECIFIC_CHARACTER_SET)
+      {
+      }
+      else
       {
         std::string tag = query.GetElement(i).GetTag().Format();
         std::string value;
@@ -220,267 +79,160 @@
       }
     }
 
-    answers.Add(result);
-  }
-
-
-  static bool ApplyModalitiesInStudyFilter(std::list<std::string>& filteredStudies,
-                                           const std::list<std::string>& studies,
-                                           const DicomMap& input,
-                                           ServerIndex& index)
-  {
-    filteredStudies.clear();
-
-    const DicomValue& v = input.GetValue(DICOM_TAG_MODALITIES_IN_STUDY);
-    if (v.IsNull())
-    {
-      return false;
-    }
-
-    // Move the allowed modalities into a "std::set"
-    std::vector<std::string>  tmp;
-    Toolbox::TokenizeString(tmp, v.AsString(), '\\'); 
-
-    std::set<std::string> modalities;
-    for (size_t i = 0; i < tmp.size(); i++)
-    {
-      modalities.insert(tmp[i]);
-    }
-
-    // Loop over the studies
-    for (std::list<std::string>::const_iterator 
-           it = studies.begin(); it != studies.end(); ++it)
+    if (result.GetSize() == 0)
     {
-      try
-      {
-        // We are considering a single study. Check whether one of
-        // its child series matches one of the modalities.
-        Json::Value study;
-        if (index.LookupResource(study, *it, ResourceType_Study))
-        {
-          // Loop over the series of the considered study.
-          for (Json::Value::ArrayIndex j = 0; j < study["Series"].size(); j++)   // (*)
-          {
-            Json::Value series;
-            if (index.LookupResource(series, study["Series"][j].asString(), ResourceType_Series))
-            {
-              // Get the modality of this series
-              if (series["MainDicomTags"].isMember("Modality"))
-              {
-                std::string modality = series["MainDicomTags"]["Modality"].asString();
-                if (modalities.find(modality) != modalities.end())
-                {
-                  // This series of the considered study matches one
-                  // of the required modalities. Take the study into
-                  // consideration for future filtering.
-                  filteredStudies.push_back(*it);
-
-                  // We have finished considering this study. Break the study loop at (*).
-                  break;
-                }
-              }
-            }
-          }
-        }
-      }
-      catch (OrthancException&)
-      {
-        // This resource has probably been deleted during the find request
-      }
+      LOG(WARNING) << "The C-FIND request does not return any DICOM tag";
     }
-
-    return true;
+    else
+    {
+      answers.Add(result);
+    }
   }
 
 
   namespace
   {
-    class CandidateResources
+    class CFindQuery : public DicomFindQuery
     {
     private:
-      ServerIndex&  index_;
-      ModalityManufacturer manufacturer_;
-      ResourceType  level_;
-      bool  isFilterApplied_;
-      std::set<std::string>  filtered_;
-
-      static void ListToSet(std::set<std::string>& target,
-                            const std::list<std::string>& source)
-      {
-        for (std::list<std::string>::const_iterator
-               it = source.begin(); it != source.end(); ++it)
-        {
-          target.insert(*it);
-        }
-      }
-
-      void ApplyExactFilter(const DicomTag& tag, const std::string& value)
-      {
-        LOG(INFO) << "Applying exact filter on tag "
-                  << FromDcmtkBridge::GetName(tag) << " (value: " << value << ")";
-
-        std::list<std::string> resources;
-        index_.LookupIdentifier(resources, tag, value, level_);
-
-        if (isFilterApplied_)
-        {
-          std::set<std::string>  s;
-          ListToSet(s, resources);
-
-          std::set<std::string> tmp = filtered_;
-          filtered_.clear();
-
-          for (std::set<std::string>::const_iterator 
-                 it = tmp.begin(); it != tmp.end(); ++it)
-          {
-            if (s.find(*it) != s.end())
-            {
-              filtered_.insert(*it);
-            }
-          }
-        }
-        else
-        {
-          assert(filtered_.empty());
-          isFilterApplied_ = true;
-          ListToSet(filtered_, resources);
-        }
-      }
+      DicomFindAnswers&      answers_;
+      ServerIndex&           index_;
+      const DicomArray&      query_;
+      bool                   hasModalitiesInStudy_;
+      std::set<std::string>  modalitiesInStudy_;
 
     public:
-      CandidateResources(ServerIndex& index,
-                         ModalityManufacturer manufacturer) : 
-        index_(index), 
-        manufacturer_(manufacturer),
-        level_(ResourceType_Patient), 
-        isFilterApplied_(false)
+      CFindQuery(DicomFindAnswers& answers,
+                 ServerIndex& index,
+                 const DicomArray& query) :
+        answers_(answers),
+        index_(index),
+        query_(query),
+        hasModalitiesInStudy_(false)
       {
       }
 
-      ResourceType GetLevel() const
-      {
-        return level_;
-      }
-
-      void GoDown()
+      void SetModalitiesInStudy(const std::string& value)
       {
-        assert(level_ != ResourceType_Instance);
-
-        if (isFilterApplied_)
-        {
-          std::set<std::string> tmp = filtered_;
-
-          filtered_.clear();
+        hasModalitiesInStudy_ = true;
+        
+        std::vector<std::string>  tmp;
+        Toolbox::TokenizeString(tmp, value, '\\'); 
 
-          for (std::set<std::string>::const_iterator 
-                 it = tmp.begin(); it != tmp.end(); ++it)
-          {
-            std::list<std::string> children;
-            index_.GetChildren(children, *it);
-            ListToSet(filtered_, children);
-          }
-        }
-
-        switch (level_)
+        for (size_t i = 0; i < tmp.size(); i++)
         {
-          case ResourceType_Patient:
-            level_ = ResourceType_Study;
-            break;
-
-          case ResourceType_Study:
-            level_ = ResourceType_Series;
-            break;
-
-          case ResourceType_Series:
-            level_ = ResourceType_Instance;
-            break;
-
-          default:
-            throw OrthancException(ErrorCode_InternalError);
+          modalitiesInStudy_.insert(tmp[i]);
         }
       }
 
-      void Flatten(std::list<std::string>& resources) const
+      virtual bool HasMainDicomTagsFilter(ResourceType level) const
       {
-        resources.clear();
-
-        if (isFilterApplied_)
+        if (DicomFindQuery::HasMainDicomTagsFilter(level))
         {
-          for (std::set<std::string>::const_iterator 
-                 it = filtered_.begin(); it != filtered_.end(); ++it)
-          {
-            resources.push_back(*it);
-          }
+          return true;
         }
-        else
-        {
-          Json::Value tmp;
-          index_.GetAllUuids(tmp, level_);
-          for (Json::Value::ArrayIndex i = 0; i < tmp.size(); i++)
-          {
-            resources.push_back(tmp[i].asString());
-          }
-        }
+
+        return (level == ResourceType_Study &&
+                hasModalitiesInStudy_);
       }
 
-      void ApplyFilter(const DicomTag& tag, const DicomMap& query)
+      virtual bool FilterMainDicomTags(const std::string& resourceId,
+                                       ResourceType level,
+                                       const DicomMap& mainTags) const
       {
-        if (query.HasTag(tag))
+        if (!DicomFindQuery::FilterMainDicomTags(resourceId, level, mainTags))
+        {
+          return false;
+        }
+
+        if (level != ResourceType_Study ||
+            !hasModalitiesInStudy_)
+        {
+          return true;
+        }
+
+        try
         {
-          const DicomValue& value = query.GetValue(tag);
-          if (!value.IsNull())
+          // We are considering a single study, and the
+          // "MODALITIES_IN_STUDY" tag is set in the C-Find. Check
+          // whether one of its child series matches one of the
+          // modalities.
+
+          Json::Value study;
+          if (index_.LookupResource(study, resourceId, ResourceType_Study))
           {
-            std::string value = query.GetValue(tag).AsString();
-            if (!IsWildcard(value))
+            // Loop over the series of the considered study.
+            for (Json::Value::ArrayIndex j = 0; j < study["Series"].size(); j++)
             {
-              ApplyExactFilter(tag, value);
+              Json::Value series;
+              if (index_.LookupResource(series, study["Series"][j].asString(), ResourceType_Series))
+              {
+                // Get the modality of this series
+                if (series["MainDicomTags"].isMember("Modality"))
+                {
+                  std::string modality = series["MainDicomTags"]["Modality"].asString();
+                  if (modalitiesInStudy_.find(modality) != modalitiesInStudy_.end())
+                  {
+                    // This series of the considered study matches one
+                    // of the required modalities. Take the study into
+                    // consideration for future filtering.
+                    return true;
+                  }
+                }
+              }
             }
           }
         }
+        catch (OrthancException&)
+        {
+          // This resource has probably been deleted during the find request
+        }
+
+        return false;
+      }
+
+      virtual bool HasInstanceFilter() const
+      {
+        return true;
+      }
+
+      virtual bool FilterInstance(const std::string& instanceId,
+                                  const Json::Value& content) const
+      {
+        bool ok = DicomFindQuery::FilterInstance(instanceId, content);
+
+        if (ok)
+        {
+          // Add this resource to the answers
+          AddAnswer(answers_, content, query_);
+        }
+
+        return ok;
       }
     };
   }
 
 
-  bool OrthancFindRequestHandler::HasReachedLimit(const DicomFindAnswers& answers,
-                                                  ResourceType level) const
-  {
-    switch (level)
-    {
-      case ResourceType_Patient:
-      case ResourceType_Study:
-      case ResourceType_Series:
-        return (maxResults_ != 0 && answers.GetSize() >= maxResults_);
-
-      case ResourceType_Instance:
-        return (maxInstances_ != 0 && answers.GetSize() >= maxInstances_);
-
-      default:
-        throw OrthancException(ErrorCode_InternalError);
-    }
-  }
-
 
   bool OrthancFindRequestHandler::Handle(DicomFindAnswers& answers,
                                          const DicomMap& input,
-                                         const std::string& callingAETitle)
+                                         const std::string& remoteIp,
+                                         const std::string& remoteAet)
   {
     /**
-     * Retrieve the manufacturer of this modality.
+     * Ensure that the calling modality is known to Orthanc.
      **/
 
-    ModalityManufacturer manufacturer;
-
-    {
-      RemoteModalityParameters modality;
+    RemoteModalityParameters modality;
 
-      if (!Configuration::LookupDicomModalityUsingAETitle(modality, callingAETitle))
-      {
-        throw OrthancException("Unknown modality");
-      }
+    if (!Configuration::LookupDicomModalityUsingAETitle(modality, remoteAet))
+    {
+      throw OrthancException(ErrorCode_UnknownModality);
+    }
 
-      manufacturer = modality.GetManufacturer();
-    }
+    // ModalityManufacturer manufacturer = modality.GetManufacturer();
+
+    bool caseSensitivePN = Configuration::GetGlobalBoolParameter("CaseSensitivePN", false);
 
 
     /**
@@ -519,134 +271,68 @@
 
 
     /**
-     * Retrieve the candidate resources for this query level. Whenever
-     * possible, we avoid returning ALL the resources for this query
-     * level, as it would imply reading the JSON file on the harddisk
-     * for each of them.
+     * Build up the query object.
      **/
 
-    CandidateResources candidates(context_.GetIndex(), manufacturer);
-
-    for (;;)
+    CFindQuery findQuery(answers, context_.GetIndex(), query);
+    findQuery.SetLevel(level);
+        
+    for (size_t i = 0; i < query.GetSize(); i++)
     {
-      switch (candidates.GetLevel())
-      {
-        case ResourceType_Patient:
-          candidates.ApplyFilter(DICOM_TAG_PATIENT_ID, input);
-          break;
-
-        case ResourceType_Study:
-          candidates.ApplyFilter(DICOM_TAG_STUDY_INSTANCE_UID, input);
-          candidates.ApplyFilter(DICOM_TAG_ACCESSION_NUMBER, input);
-          break;
+      const DicomTag tag = query.GetElement(i).GetTag();
 
-        case ResourceType_Series:
-          candidates.ApplyFilter(DICOM_TAG_SERIES_INSTANCE_UID, input);
-          break;
-
-        case ResourceType_Instance:
-          candidates.ApplyFilter(DICOM_TAG_SOP_INSTANCE_UID, input);
-          break;
-
-        default:
-          throw OrthancException(ErrorCode_InternalError);
-      }      
-
-      if (candidates.GetLevel() == level)
+      if (query.GetElement(i).GetValue().IsNull() ||
+          tag == DICOM_TAG_QUERY_RETRIEVE_LEVEL ||
+          tag == DICOM_TAG_SPECIFIC_CHARACTER_SET)
       {
-        break;
+        continue;
       }
 
-      candidates.GoDown();
-    }
-
-    std::list<std::string>  resources;
-    candidates.Flatten(resources);
-
-    LOG(INFO) << "Number of candidate resources after exact filtering: " << resources.size();
+      std::string value = query.GetElement(i).GetValue().AsString();
+      if (value.size() == 0)
+      {
+        // An empty string corresponds to a "*" wildcard constraint, so we ignore it
+        continue;
+      }
 
-    /**
-     * Apply filtering on modalities for studies, if asked (this is an
-     * extension to standard DICOM)
-     * http://www.medicalconnections.co.uk/kb/Filtering_on_and_Retrieving_the_Modality_in_a_C_FIND
-     **/
-
-    if (level == ResourceType_Study &&
-        input.HasTag(DICOM_TAG_MODALITIES_IN_STUDY))
-    {
-      std::list<std::string> filtered;
-      if (ApplyModalitiesInStudyFilter(filtered, resources, input, context_.GetIndex()))
+      if (tag == DICOM_TAG_MODALITIES_IN_STUDY)
       {
-        resources = filtered;
+        findQuery.SetModalitiesInStudy(value);
+      }
+      else
+      {
+        findQuery.SetConstraint(tag, value, caseSensitivePN);
       }
     }
 
 
     /**
-     * Loop over all the resources for this query level.
+     * Run the query.
      **/
 
-    for (std::list<std::string>::const_iterator 
-           resource = resources.begin(); resource != resources.end(); ++resource)
+    ResourceFinder finder(context_);
+
+    switch (level)
     {
-      try
-      {
-        std::string instance;
-        if (LookupOneInstance(instance, context_.GetIndex(), *resource, level))
-        {
-          Json::Value info;
-          context_.ReadJson(info, instance);
-        
-          if (Matches(info, query))
-          {
-            if (HasReachedLimit(answers, level))
-            {
-              // Too many results, stop before recording this new match
-              return false;
-            }
+      case ResourceType_Patient:
+      case ResourceType_Study:
+      case ResourceType_Series:
+        finder.SetMaxResults(maxResults_);
+        break;
 
-            AddAnswer(answers, info, query);
-          }
-        }
-      }
-      catch (OrthancException&)
-      {
-        // This resource has probably been deleted during the find request
-      }
+      case ResourceType_Instance:
+        finder.SetMaxResults(maxInstances_);
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
     }
 
-    return true;  // All the matching resources have been returned
+    std::list<std::string> tmp;
+    bool finished = finder.Apply(tmp, findQuery);
+
+    LOG(INFO) << "Number of matching resources: " << tmp.size();
+
+    return finished;
   }
 }
-
-
-
-/**
- * TODO : Case-insensitive match for PN value representation (Patient
- * Name). Case-senstive match for all the other value representations.
- *
- * Reference: DICOM PS 3.4
- *   - C.2.2.2.1 ("Single Value Matching") 
- *   - C.2.2.2.4 ("Wild Card Matching")
- * http://medical.nema.org/Dicom/2011/11_04pu.pdf
- *
- * "Except for Attributes with a PN Value Representation, only
- * entities with values which match exactly the value specified in the
- * request shall match. This matching is case-sensitive, i.e.,
- * sensitive to the exact encoding of the key attribute value in
- * character sets where a letter may have multiple encodings (e.g.,
- * based on its case, its position in a word, or whether it is
- * accented)
- * 
- * For Attributes with a PN Value Representation (e.g., Patient Name
- * (0010,0010)), an application may perform literal matching that is
- * either case-sensitive, or that is insensitive to some or all
- * aspects of case, position, accent, or other character encoding
- * variants."
- *
- * (0008,0018) UI SOPInstanceUID     => Case-sensitive
- * (0008,0050) SH AccessionNumber    => Case-sensitive
- * (0010,0020) LO PatientID          => Case-sensitive
- * (0020,000D) UI StudyInstanceUID   => Case-sensitive
- * (0020,000E) UI SeriesInstanceUID  => Case-sensitive
- **/
--- a/OrthancServer/OrthancFindRequestHandler.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/OrthancFindRequestHandler.h	Wed Sep 30 13:23:31 2015 +0200
@@ -57,7 +57,8 @@
 
     virtual bool Handle(DicomFindAnswers& answers,
                         const DicomMap& input,
-                        const std::string& callingAETitle);
+                        const std::string& remoteIp,
+                        const std::string& remoteAet);
 
     unsigned int GetMaxResults() const
     {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/OrthancHttpHandler.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,93 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "PrecompiledHeadersServer.h"
+#include "OrthancHttpHandler.h"
+
+#include "../Core/OrthancException.h"
+
+
+namespace Orthanc
+{
+  bool OrthancHttpHandler::Handle(HttpOutput& output,
+                                  RequestOrigin origin,
+                                  const char* remoteIp,
+                                  const char* username,
+                                  HttpMethod method,
+                                  const UriComponents& uri,
+                                  const Arguments& headers,
+                                  const GetArguments& getArguments,
+                                  const char* bodyData,
+                                  size_t bodySize)
+  {
+    bool found = false;
+
+    for (Handlers::const_iterator it = handlers_.begin(); 
+         it != handlers_.end() && !found; ++it) 
+    {
+      found = (*it)->Handle(output, origin, remoteIp, username, method, uri, 
+                            headers, getArguments, bodyData, bodySize);
+    }
+
+    return found;
+  }
+
+
+  void OrthancHttpHandler::Register(IHttpHandler& handler,
+                                    bool isOrthancRestApi)
+  {
+    handlers_.push_back(&handler);
+
+    if (isOrthancRestApi)
+    {
+      orthancRestApi_ = &handler;
+    }
+  }
+
+
+  IHttpHandler& OrthancHttpHandler::RestrictToOrthancRestApi(bool restrict)
+  {
+    if (restrict)
+    {
+      if (orthancRestApi_ == NULL)
+      {
+        throw OrthancException(ErrorCode_InternalError);
+      }
+
+      return *orthancRestApi_;
+    }
+    else
+    {
+      return *this;
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/OrthancHttpHandler.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,73 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Core/HttpServer/IHttpHandler.h"
+
+namespace Orthanc
+{
+  class OrthancHttpHandler : public IHttpHandler
+  {
+  private:
+    typedef std::list<IHttpHandler*> Handlers;
+
+    Handlers      handlers_;
+    IHttpHandler *orthancRestApi_;
+
+  public:
+    OrthancHttpHandler() : orthancRestApi_(NULL)
+    {
+    }
+
+    virtual bool Handle(HttpOutput& output,
+                        RequestOrigin origin,
+                        const char* remoteIp,
+                        const char* username,
+                        HttpMethod method,
+                        const UriComponents& uri,
+                        const Arguments& headers,
+                        const GetArguments& getArguments,
+                        const char* bodyData,
+                        size_t bodySize);
+
+    void Register(IHttpHandler& handler,
+                  bool isOrthancRestApi);
+
+    bool HasOrthancRestApi() const
+    {
+      return orthancRestApi_ != NULL;
+    }
+
+    IHttpHandler& RestrictToOrthancRestApi(bool restrict);
+  };
+}
--- a/OrthancServer/OrthancInitialization.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/OrthancInitialization.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -34,19 +34,29 @@
 #include "OrthancInitialization.h"
 
 #include "../Core/HttpClient.h"
+#include "../Core/Logging.h"
 #include "../Core/OrthancException.h"
 #include "../Core/Toolbox.h"
-#include "DicomProtocol/DicomServer.h"
+#include "../Core/FileStorage/FilesystemStorage.h"
+
 #include "ServerEnumerations.h"
+#include "DatabaseWrapper.h"
+#include "FromDcmtkBridge.h"
 
 #include <boost/lexical_cast.hpp>
 #include <boost/filesystem.hpp>
 #include <curl/curl.h>
 #include <boost/thread.hpp>
-#include <glog/logging.h>
+
 
-#include "DatabaseWrapper.h"
-#include "../Core/FileStorage/FilesystemStorage.h"
+#if ORTHANC_SSL_ENABLED == 1
+// For OpenSSL initialization and finalization
+#include <openssl/conf.h>
+#include <openssl/engine.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/ssl.h>
+#endif
 
 
 #if ORTHANC_JPEG_ENABLED == 1
@@ -62,23 +72,154 @@
 namespace Orthanc
 {
   static boost::mutex globalMutex_;
-  static std::auto_ptr<Json::Value> configuration_;
+  static Json::Value configuration_;
   static boost::filesystem::path defaultDirectory_;
   static std::string configurationAbsolutePath_;
+  static FontRegistry fontRegistry_;
+
+
+  static std::string GetGlobalStringParameterInternal(const std::string& parameter,
+                                                      const std::string& defaultValue)
+  {
+    if (configuration_.isMember(parameter))
+    {
+      if (configuration_[parameter].type() != Json::stringValue)
+      {
+        LOG(ERROR) << "The configuration option \"" << parameter << "\" must be a string";
+        throw OrthancException(ErrorCode_BadParameterType);
+      }
+      else
+      {
+        return configuration_[parameter].asString();
+      }
+    }
+    else
+    {
+      return defaultValue;
+    }
+  }
+
+
+  static bool GetGlobalBoolParameterInternal(const std::string& parameter,
+                                             bool defaultValue)
+  {
+    if (configuration_.isMember(parameter))
+    {
+      if (configuration_[parameter].type() != Json::booleanValue)
+      {
+        LOG(ERROR) << "The configuration option \"" << parameter << "\" must be a Boolean (true or false)";
+        throw OrthancException(ErrorCode_BadParameterType);
+      }
+      else
+      {
+        return configuration_[parameter].asBool();
+      }
+    }
+    else
+    {
+      return defaultValue;
+    }
+  }
+
+
+
+  static void AddFileToConfiguration(const boost::filesystem::path& path)
+  {
+    LOG(WARNING) << "Reading the configuration from: " << path;
+
+    Json::Value config;
+
+    {
+      std::string content;
+      Toolbox::ReadFile(content, path.string());
+
+      Json::Value tmp;
+      Json::Reader reader;
+      if (!reader.parse(content, tmp) ||
+          tmp.type() != Json::objectValue)
+      {
+        LOG(ERROR) << "The configuration file does not follow the JSON syntax: " << path;
+        throw OrthancException(ErrorCode_BadJson);
+      }
+
+      Toolbox::CopyJsonWithoutComments(config, tmp);
+    }
+
+    if (configuration_.size() == 0)
+    {
+      configuration_ = config;
+    }
+    else
+    {
+      Json::Value::Members members = config.getMemberNames();
+      for (Json::Value::ArrayIndex i = 0; i < members.size(); i++)
+      {
+        if (configuration_.isMember(members[i]))
+        {
+          LOG(ERROR) << "The configuration section \"" << members[i] << "\" is defined in 2 different configuration files";
+          throw OrthancException(ErrorCode_BadFileFormat);          
+        }
+        else
+        {
+          configuration_[members[i]] = config[members[i]];
+        }
+      }
+    }
+  }
+
+
+  static void ScanFolderForConfiguration(const char* folder)
+  {
+    using namespace boost::filesystem;
+
+    LOG(WARNING) << "Scanning folder \"" << folder << "\" for configuration files";
+
+    directory_iterator end_it; // default construction yields past-the-end
+    for (directory_iterator it(folder);
+         it != end_it;
+         ++it)
+    {
+      if (!is_directory(it->status()))
+      {
+        std::string extension = boost::filesystem::extension(it->path());
+        Toolbox::ToLowerCase(extension);
+
+        if (extension == ".json")
+        {
+          AddFileToConfiguration(it->path().string());
+        }
+      }
+    }
+  }
 
 
   static void ReadGlobalConfiguration(const char* configurationFile)
   {
-    configuration_.reset(new Json::Value);
-
-    std::string content;
+    // Prepare the default configuration
+    defaultDirectory_ = boost::filesystem::current_path();
+    configuration_ = Json::objectValue;
+    configurationAbsolutePath_ = "";
 
     if (configurationFile)
     {
-      Toolbox::ReadFile(content, configurationFile);
-      defaultDirectory_ = boost::filesystem::path(configurationFile).parent_path();
-      LOG(WARNING) << "Using the configuration from: " << configurationFile;
-      configurationAbsolutePath_ = boost::filesystem::absolute(configurationFile).string();
+      if (!boost::filesystem::exists(configurationFile))
+      {
+        LOG(ERROR) << "Inexistent path to configuration: " << configurationFile;
+        throw OrthancException(ErrorCode_InexistentFile);
+      }
+      
+      if (boost::filesystem::is_directory(configurationFile))
+      {
+        defaultDirectory_ = boost::filesystem::path(configurationFile);
+        configurationAbsolutePath_ = boost::filesystem::absolute(configurationFile).parent_path().string();
+        ScanFolderForConfiguration(configurationFile);
+      }
+      else
+      {
+        defaultDirectory_ = boost::filesystem::path(configurationFile).parent_path();
+        configurationAbsolutePath_ = boost::filesystem::absolute(configurationFile).string();
+        AddFileToConfiguration(configurationFile);
+      }
     }
     else
     {
@@ -91,38 +232,22 @@
       // In a non-standalone build, we use the
       // "Resources/Configuration.json" from the Orthanc source code
 
-      try
-      {
-        boost::filesystem::path p = ORTHANC_PATH;
-        p /= "Resources";
-        p /= "Configuration.json";
-        Toolbox::ReadFile(content, p.string());
-        LOG(WARNING) << "Using the configuration from: " << p.string();
-        configurationAbsolutePath_ = boost::filesystem::absolute(p).string();
-      }
-      catch (OrthancException&)
-      {
-        // No configuration file found, give up with empty configuration
-        LOG(WARNING) << "Using the default Orthanc configuration";
-        return;
-      }
+      boost::filesystem::path p = ORTHANC_PATH;
+      p /= "Resources";
+      p /= "Configuration.json";
+      configurationAbsolutePath_ = boost::filesystem::absolute(p).string();
+
+      AddFileToConfiguration(p);      
 #endif
     }
-
-
-    Json::Reader reader;
-    if (!reader.parse(content, *configuration_))
-    {
-      throw OrthancException("Unable to read the configuration file");
-    }
   }
 
 
   static void RegisterUserMetadata()
   {
-    if (configuration_->isMember("UserMetadata"))
+    if (configuration_.isMember("UserMetadata"))
     {
-      const Json::Value& parameter = (*configuration_) ["UserMetadata"];
+      const Json::Value& parameter = configuration_["UserMetadata"];
 
       Json::Value::Members members = parameter.getMemberNames();
       for (size_t i = 0; i < members.size(); i++)
@@ -154,9 +279,9 @@
 
   static void RegisterUserContentType()
   {
-    if (configuration_->isMember("UserContentType"))
+    if (configuration_.isMember("UserContentType"))
     {
-      const Json::Value& parameter = (*configuration_) ["UserContentType"];
+      const Json::Value& parameter = configuration_["UserContentType"];
 
       Json::Value::Members members = parameter.getMemberNames();
       for (size_t i = 0; i < members.size(); i++)
@@ -186,66 +311,35 @@
   }
 
 
-  static std::string GetStringValue(const Json::Value& configuration,
-                                    const std::string& key,
-                                    const std::string& defaultValue)
-  {
-    if (configuration.type() != Json::objectValue)
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-
-    if (!configuration.isMember(key))
-    {
-      return defaultValue;
-    }
-
-    if (configuration[key].type() != Json::stringValue)
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-
-    return configuration[key].asString();
-  }
-
-
-  static int GetIntegerValue(const Json::Value& configuration,
-                             const std::string& key,
-                             int defaultValue)
-  {
-    if (configuration.type() != Json::objectValue)
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-
-    if (!configuration.isMember(key))
-    {
-      return defaultValue;
-    }
-
-    if (configuration[key].type() != Json::intValue)
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-
-    return configuration[key].asInt();
-  }
-
 
   void OrthancInitialize(const char* configurationFile)
   {
     boost::mutex::scoped_lock lock(globalMutex_);
 
+#if ORTHANC_SSL_ENABLED == 1
+    // https://wiki.openssl.org/index.php/Library_Initialization
+    SSL_library_init();
+    SSL_load_error_strings();
+    OpenSSL_add_all_algorithms();
+    ERR_load_crypto_strings();
+
+    curl_global_init(CURL_GLOBAL_ALL);
+#else
+    curl_global_init(CURL_GLOBAL_ALL & ~CURL_GLOBAL_SSL);
+#endif
+
     InitializeServerEnumerations();
-    defaultDirectory_ = boost::filesystem::current_path();
+
+    // Read the user-provided configuration
     ReadGlobalConfiguration(configurationFile);
 
-    HttpClient::GlobalInitialize();
+    HttpClient::GlobalInitialize(GetGlobalBoolParameterInternal("HttpsVerifyPeers", true),
+                                 GetGlobalStringParameterInternal("HttpsCACertificates", ""));
 
     RegisterUserMetadata();
     RegisterUserContentType();
 
-    DicomServer::InitializeDictionary();
+    FromDcmtkBridge::InitializeDictionary();
 
 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
     LOG(WARNING) << "Registering JPEG Lossless codecs";
@@ -256,6 +350,8 @@
     LOG(WARNING) << "Registering JPEG codecs";
     DJDecoderRegistration::registerCodecs(); 
 #endif
+
+    fontRegistry_.AddFromResource(EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16);
   }
 
 
@@ -264,7 +360,6 @@
   {
     boost::mutex::scoped_lock lock(globalMutex_);
     HttpClient::GlobalFinalize();
-    configuration_.reset(NULL);
 
 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
     // Unregister JPEG-LS codecs
@@ -275,23 +370,28 @@
     // Unregister JPEG codecs
     DJDecoderRegistration::cleanup();
 #endif
+
+    curl_global_cleanup();
+
+#if ORTHANC_SSL_ENABLED == 1
+    // Finalize OpenSSL
+    // https://wiki.openssl.org/index.php/Library_Initialization#Cleanup
+    FIPS_mode_set(0);
+    ENGINE_cleanup();
+    CONF_modules_unload(1);
+    EVP_cleanup();
+    CRYPTO_cleanup_all_ex_data();
+    ERR_remove_state(0);
+    ERR_free_strings();
+#endif
   }
 
 
-
   std::string Configuration::GetGlobalStringParameter(const std::string& parameter,
                                                       const std::string& defaultValue)
   {
     boost::mutex::scoped_lock lock(globalMutex_);
-
-    if (configuration_->isMember(parameter))
-    {
-      return (*configuration_) [parameter].asString();
-    }
-    else
-    {
-      return defaultValue;
-    }
+    return GetGlobalStringParameterInternal(parameter, defaultValue);
   }
 
 
@@ -300,9 +400,17 @@
   {
     boost::mutex::scoped_lock lock(globalMutex_);
 
-    if (configuration_->isMember(parameter))
+    if (configuration_.isMember(parameter))
     {
-      return (*configuration_) [parameter].asInt();
+      if (configuration_[parameter].type() != Json::intValue)
+      {
+        LOG(ERROR) << "The configuration option \"" << parameter << "\" must be an integer";
+        throw OrthancException(ErrorCode_BadParameterType);
+      }
+      else
+      {
+        return configuration_[parameter].asInt();
+      }
     }
     else
     {
@@ -315,15 +423,7 @@
                                              bool defaultValue)
   {
     boost::mutex::scoped_lock lock(globalMutex_);
-
-    if (configuration_->isMember(parameter))
-    {
-      return (*configuration_) [parameter].asBool();
-    }
-    else
-    {
-      return defaultValue;
-    }
+    return GetGlobalBoolParameterInternal(parameter, defaultValue);
   }
 
 
@@ -332,12 +432,13 @@
   {
     boost::mutex::scoped_lock lock(globalMutex_);
 
-    if (!configuration_->isMember("DicomModalities"))
+    if (!configuration_.isMember("DicomModalities"))
     {
-      throw OrthancException(ErrorCode_BadFileFormat);
+      LOG(ERROR) << "No modality with symbolic name: " << name;
+      throw OrthancException(ErrorCode_InexistentItem);
     }
 
-    const Json::Value& modalities = (*configuration_) ["DicomModalities"];
+    const Json::Value& modalities = configuration_["DicomModalities"];
     if (modalities.type() != Json::objectValue ||
         !modalities.isMember(name))
     {
@@ -349,9 +450,9 @@
     {
       modality.FromJson(modalities[name]);
     }
-    catch (OrthancException& e)
+    catch (OrthancException&)
     {
-      LOG(ERROR) << "Syntax error in the definition of modality \"" << name 
+      LOG(ERROR) << "Syntax error in the definition of DICOM modality \"" << name 
                  << "\". Please check your configuration file.";
       throw;
     }
@@ -364,14 +465,15 @@
   {
     boost::mutex::scoped_lock lock(globalMutex_);
 
-    if (!configuration_->isMember("OrthancPeers"))
+    if (!configuration_.isMember("OrthancPeers"))
     {
-      throw OrthancException(ErrorCode_BadFileFormat);
+      LOG(ERROR) << "No peer with symbolic name: " << name;
+      throw OrthancException(ErrorCode_InexistentItem);
     }
 
     try
     {
-      const Json::Value& modalities = (*configuration_) ["OrthancPeers"];
+      const Json::Value& modalities = configuration_["OrthancPeers"];
       if (modalities.type() != Json::objectValue ||
           !modalities.isMember(name))
       {
@@ -381,7 +483,7 @@
 
       peer.FromJson(modalities[name]);
     }
-    catch (OrthancException& e)
+    catch (OrthancException&)
     {
       LOG(ERROR) << "Syntax error in the definition of peer \"" << name 
                  << "\". Please check your configuration file.";
@@ -398,14 +500,15 @@
 
     target.clear();
   
-    if (!configuration_->isMember(parameter))
+    if (!configuration_.isMember(parameter))
     {
       return true;
     }
 
-    const Json::Value& modalities = (*configuration_) [parameter];
+    const Json::Value& modalities = configuration_[parameter];
     if (modalities.type() != Json::objectValue)
     {
+      LOG(ERROR) << "Bad format of the \"DicomModalities\" configuration section";
       throw OrthancException(ErrorCode_BadFileFormat);
     }
 
@@ -434,7 +537,8 @@
   {
     if (!ReadKeys(target, "DicomModalities", true))
     {
-      throw OrthancException("Only alphanumeric and dash characters are allowed in the names of the modalities");
+      LOG(ERROR) << "Only alphanumeric and dash characters are allowed in the names of the modalities";
+      throw OrthancException(ErrorCode_BadFileFormat);
     }
   }
 
@@ -443,7 +547,8 @@
   {
     if (!ReadKeys(target, "OrthancPeers", true))
     {
-      throw OrthancException("Only alphanumeric and dash characters are allowed in the names of Orthanc peers");
+      LOG(ERROR) << "Only alphanumeric and dash characters are allowed in the names of Orthanc peers";
+      throw OrthancException(ErrorCode_BadFileFormat);
     }
   }
 
@@ -455,15 +560,16 @@
 
     httpServer.ClearUsers();
 
-    if (!configuration_->isMember("RegisteredUsers"))
+    if (!configuration_.isMember("RegisteredUsers"))
     {
       return;
     }
 
-    const Json::Value& users = (*configuration_) ["RegisteredUsers"];
+    const Json::Value& users = configuration_["RegisteredUsers"];
     if (users.type() != Json::objectValue)
     {
-      throw OrthancException("Badly formatted list of users");
+      LOG(ERROR) << "Badly formatted list of users";
+      throw OrthancException(ErrorCode_BadFileFormat);
     }
 
     Json::Value::Members usernames = users.getMemberNames();
@@ -516,16 +622,17 @@
 
     target.clear();
   
-    if (!configuration_->isMember(key))
+    if (!configuration_.isMember(key))
     {
       return;
     }
 
-    const Json::Value& lst = (*configuration_) [key];
+    const Json::Value& lst = configuration_[key];
 
     if (lst.type() != Json::arrayValue)
     {
-      throw OrthancException("Badly formatted list of strings");
+      LOG(ERROR) << "Badly formatted list of strings";
+      throw OrthancException(ErrorCode_BadFileFormat);
     }
 
     for (Json::Value::ArrayIndex i = 0; i < lst.size(); i++)
@@ -607,7 +714,8 @@
     }
     else
     {
-      throw OrthancException("Unknown modality for AET: " + aet);
+      LOG(ERROR) << "Unknown modality for AET: " << aet;
+      throw OrthancException(ErrorCode_InexistentItem);
     }
   }
 
@@ -617,14 +725,15 @@
   {
     boost::mutex::scoped_lock lock(globalMutex_);
 
-    if (!configuration_->isMember("DicomModalities"))
+    if (!configuration_.isMember("DicomModalities"))
     {
-      (*configuration_) ["DicomModalities"] = Json::objectValue;
+      configuration_["DicomModalities"] = Json::objectValue;
     }
 
-    Json::Value& modalities = (*configuration_) ["DicomModalities"];
+    Json::Value& modalities = configuration_["DicomModalities"];
     if (modalities.type() != Json::objectValue)
     {
+      LOG(ERROR) << "Bad file format for modality: " << symbolicName;
       throw OrthancException(ErrorCode_BadFileFormat);
     }
 
@@ -640,14 +749,16 @@
   {
     boost::mutex::scoped_lock lock(globalMutex_);
 
-    if (!configuration_->isMember("DicomModalities"))
+    if (!configuration_.isMember("DicomModalities"))
     {
+      LOG(ERROR) << "No modality with symbolic name: " << symbolicName;
       throw OrthancException(ErrorCode_BadFileFormat);
     }
 
-    Json::Value& modalities = (*configuration_) ["DicomModalities"];
+    Json::Value& modalities = configuration_["DicomModalities"];
     if (modalities.type() != Json::objectValue)
     {
+      LOG(ERROR) << "Bad file format for the \"DicomModalities\" configuration section";
       throw OrthancException(ErrorCode_BadFileFormat);
     }
 
@@ -660,14 +771,16 @@
   {
     boost::mutex::scoped_lock lock(globalMutex_);
 
-    if (!configuration_->isMember("OrthancPeers"))
+    if (!configuration_.isMember("OrthancPeers"))
     {
-      (*configuration_) ["OrthancPeers"] = Json::objectValue;
+      LOG(ERROR) << "No peer with symbolic name: " << symbolicName;
+      configuration_["OrthancPeers"] = Json::objectValue;
     }
 
-    Json::Value& peers = (*configuration_) ["OrthancPeers"];
+    Json::Value& peers = configuration_["OrthancPeers"];
     if (peers.type() != Json::objectValue)
     {
+      LOG(ERROR) << "Bad file format for the \"OrthancPeers\" configuration section";
       throw OrthancException(ErrorCode_BadFileFormat);
     }
 
@@ -683,14 +796,16 @@
   {
     boost::mutex::scoped_lock lock(globalMutex_);
 
-    if (!configuration_->isMember("OrthancPeers"))
+    if (!configuration_.isMember("OrthancPeers"))
     {
+      LOG(ERROR) << "No peer with symbolic name: " << symbolicName;
       throw OrthancException(ErrorCode_BadFileFormat);
     }
 
-    Json::Value& peers = (*configuration_) ["OrthancPeers"];
+    Json::Value& peers = configuration_["OrthancPeers"];
     if (peers.type() != Json::objectValue)
     {
+      LOG(ERROR) << "Bad file format for the \"OrthancPeers\" configuration section";
       throw OrthancException(ErrorCode_BadFileFormat);
     }
 
@@ -807,4 +922,27 @@
   {
     return CreateFilesystemStorage();
   }  
+
+
+  void Configuration::GetConfiguration(Json::Value& result)
+  {
+    boost::mutex::scoped_lock lock(globalMutex_);
+    result = configuration_;
+  }
+
+
+  void Configuration::FormatConfiguration(std::string& result)
+  {
+    Json::Value config;
+    GetConfiguration(config);
+
+    Json::StyledWriter w;
+    result = w.write(config);
+  }
+
+
+  const FontRegistry& Configuration::GetFontRegistry()
+  {
+    return fontRegistry_;
+  }
 }
--- a/OrthancServer/OrthancInitialization.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/OrthancInitialization.h	Wed Sep 30 13:23:31 2015 +0200
@@ -42,6 +42,7 @@
 #include "OrthancPeerParameters.h"
 #include "IDatabaseWrapper.h"
 #include "../Core/FileStorage/IStorageArea.h"
+#include "../Core/Images/FontRegistry.h"
 
 namespace Orthanc
 {
@@ -108,5 +109,11 @@
     static IDatabaseWrapper* CreateDatabaseWrapper();
 
     static IStorageArea* CreateStorageArea();
+
+    static void GetConfiguration(Json::Value& result);
+
+    static void FormatConfiguration(std::string& result);
+
+    static const FontRegistry& GetFontRegistry();
   };
 }
--- a/OrthancServer/OrthancMoveRequestHandler.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/OrthancMoveRequestHandler.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,9 +33,10 @@
 #include "PrecompiledHeadersServer.h"
 #include "OrthancMoveRequestHandler.h"
 
-#include <glog/logging.h>
-
 #include "OrthancInitialization.h"
+#include "FromDcmtkBridge.h"
+#include "../Core/DicomFormat/DicomArray.h"
+#include "../Core/Logging.h"
 
 namespace Orthanc
 {
@@ -47,6 +48,7 @@
     {
     private:
       ServerContext& context_;
+      const std::string& localAet_;
       std::vector<std::string> instances_;
       size_t position_;
       RemoteModalityParameters remote_;
@@ -56,6 +58,7 @@
                                  const std::string& aet,
                                  const std::string& publicId) :
         context_(context),
+        localAet_(context.GetDefaultLocalApplicationEntityTitle()),
         position_(0)
       {
         LOG(INFO) << "Sending resource " << publicId << " to modality \"" << aet << "\"";
@@ -91,7 +94,7 @@
 
         {
           ReusableDicomUserConnection::Locker locker
-            (context_.GetReusableDicomUserConnection(), remote_);
+            (context_.GetReusableDicomUserConnection(), localAet_, remote_);
           locker.GetConnection().Store(dicom);
         }
 
@@ -127,10 +130,27 @@
   }
 
 
-  IMoveRequestIterator* OrthancMoveRequestHandler::Handle(const std::string& aet,
-                                                          const DicomMap& input)
+  IMoveRequestIterator* OrthancMoveRequestHandler::Handle(const std::string& targetAet,
+                                                          const DicomMap& input,
+                                                          const std::string& remoteIp,
+                                                          const std::string& remoteAet)
   {
-    LOG(WARNING) << "Move-SCU request received for AET \"" << aet << "\"";
+    LOG(WARNING) << "Move-SCU request received for AET \"" << targetAet << "\"";
+
+    {
+      DicomArray query(input);
+      for (size_t i = 0; i < query.GetSize(); i++)
+      {
+        if (!query.GetElement(i).GetValue().IsNull())
+        {
+          LOG(INFO) << "  " << query.GetElement(i).GetTag()
+                    << "  " << FromDcmtkBridge::GetName(query.GetElement(i).GetTag())
+                    << " = " << query.GetElement(i).GetValue().AsString();
+        }
+      }
+    }
+
+
 
     /**
      * Retrieve the query level.
@@ -158,7 +178,7 @@
           LookupIdentifier(publicId, DICOM_TAG_STUDY_INSTANCE_UID, input) ||
           LookupIdentifier(publicId, DICOM_TAG_PATIENT_ID, input))
       {
-        return new OrthancMoveRequestIterator(context_, aet, publicId);
+        return new OrthancMoveRequestIterator(context_, targetAet, publicId);
       }
       else
       {
@@ -203,6 +223,6 @@
       throw OrthancException(ErrorCode_BadRequest);
     }
 
-    return new OrthancMoveRequestIterator(context_, aet, publicId);
+    return new OrthancMoveRequestIterator(context_, targetAet, publicId);
   }
 }
--- a/OrthancServer/OrthancMoveRequestHandler.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/OrthancMoveRequestHandler.h	Wed Sep 30 13:23:31 2015 +0200
@@ -51,7 +51,9 @@
     {
     }
 
-    virtual IMoveRequestIterator* Handle(const std::string& target,
-                                         const DicomMap& input);
+    virtual IMoveRequestIterator* Handle(const std::string& targetAet,
+                                         const DicomMap& input,
+                                         const std::string& remoteIp,
+                                         const std::string& remoteAet);
   };
 }
--- a/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,10 +33,14 @@
 #include "../PrecompiledHeadersServer.h"
 #include "OrthancRestApi.h"
 
-#include "../FromDcmtkBridge.h"
+#include "../../Core/Logging.h"
 #include "../../Core/Uuid.h"
+#include "../FromDcmtkBridge.h"
+#include "../ServerContext.h"
+#include "../OrthancInitialization.h"
 
-#include <glog/logging.h>
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string/predicate.hpp>
 
 namespace Orthanc
 {
@@ -275,6 +279,7 @@
       modification.Apply(*modified);
 
       DicomInstanceToStore toStore;
+      toStore.SetRestOrigin(call);
       toStore.SetParsedDicomFile(*modified);
 
 
@@ -312,7 +317,7 @@
       if (context.Store(modifiedInstance, toStore) != StoreStatus_Success)
       {
         LOG(ERROR) << "Error while storing a modified instance " << *it;
-        return;
+        throw OrthancException(ErrorCode_CannotStoreInstance);
       }
 
       // Sanity checks in debug mode
@@ -428,46 +433,425 @@
   }
 
 
-  static void CreateDicom(RestApiPostCall& call)
+  static void StoreCreatedInstance(std::string& id /* out */,
+                                   RestApiPostCall& call,
+                                   ParsedDicomFile& dicom)
+  {
+    DicomInstanceToStore toStore;
+    toStore.SetRestOrigin(call);
+    toStore.SetParsedDicomFile(dicom);
+
+    ServerContext& context = OrthancRestApi::GetContext(call);
+    StoreStatus status = context.Store(id, toStore);
+
+    if (status == StoreStatus_Failure)
+    {
+      throw OrthancException(ErrorCode_CannotStoreInstance);
+    }
+  }
+
+
+  static void CreateDicomV1(ParsedDicomFile& dicom,
+                            RestApiPostCall& call,
+                            const Json::Value& request)
   {
     // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World"}'
     // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World","PixelData":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDDcB53FulQAAAElJREFUGNNtj0sSAEEEQ1+U+185s1CtmRkblQ9CZldsKHJDk6DLGLJa6chjh0ooQmpjXMM86zPwydGEj6Ed/UGykkEM8X+p3u8/8LcOJIWLGeMAAAAASUVORK5CYII="}'
 
-    Json::Value replacements;
-    if (call.ParseJsonRequest(replacements) && replacements.isObject())
+    assert(request.isObject());
+    LOG(WARNING) << "Using a deprecated call to /tools/create-dicom";
+
+    Json::Value::Members members = request.getMemberNames();
+    for (size_t i = 0; i < members.size(); i++)
     {
-      ParsedDicomFile dicom;
+      const std::string& name = members[i];
+      if (request[name].type() != Json::stringValue)
+      {
+        throw OrthancException(ErrorCode_CreateDicomNotString);
+      }
+
+      std::string value = request[name].asString();
+
+      DicomTag tag = FromDcmtkBridge::ParseTag(name);
+      if (tag == DICOM_TAG_PIXEL_DATA)
+      {
+        dicom.EmbedContent(value);
+      }
+      else
+      {
+        dicom.Replace(tag, value);
+      }
+    }
+  }
+
 
-      Json::Value::Members members = replacements.getMemberNames();
-      for (size_t i = 0; i < members.size(); i++)
+  static void InjectTags(ParsedDicomFile& dicom,
+                         const Json::Value& tags,
+                         bool interpretBinaryTags)
+  {
+    if (tags.type() != Json::objectValue)
+    {
+      throw OrthancException(ErrorCode_BadRequest);
+    }
+
+    // Inject the user-specified tags
+    Json::Value::Members members = tags.getMemberNames();
+    for (size_t i = 0; i < members.size(); i++)
+    {
+      const std::string& name = members[i];
+      if (tags[name].type() != Json::stringValue)
       {
-        const std::string& name = members[i];
-        std::string value = replacements[name].asString();
+        throw OrthancException(ErrorCode_CreateDicomNotString);
+      }
+
+      std::string value = tags[name].asString();
+      DicomTag tag = FromDcmtkBridge::ParseTag(name);
 
-        DicomTag tag = FromDcmtkBridge::ParseTag(name);
+      if (tag != DICOM_TAG_SPECIFIC_CHARACTER_SET)
+      {
+        if (tag != DICOM_TAG_PATIENT_ID &&
+            tag != DICOM_TAG_ACQUISITION_DATE &&
+            tag != DICOM_TAG_ACQUISITION_TIME &&
+            tag != DICOM_TAG_CONTENT_DATE &&
+            tag != DICOM_TAG_CONTENT_TIME &&
+            tag != DICOM_TAG_INSTANCE_CREATION_DATE &&
+            tag != DICOM_TAG_INSTANCE_CREATION_TIME &&
+            tag != DICOM_TAG_SERIES_DATE &&
+            tag != DICOM_TAG_SERIES_TIME &&
+            tag != DICOM_TAG_STUDY_DATE &&
+            tag != DICOM_TAG_STUDY_TIME &&
+            dicom.HasTag(tag))
+        {
+          throw OrthancException(ErrorCode_CreateDicomOverrideTag);
+        }
+
         if (tag == DICOM_TAG_PIXEL_DATA)
         {
-          dicom.EmbedImage(value);
+          throw OrthancException(ErrorCode_CreateDicomUseContent);
+        }
+        else if (interpretBinaryTags &&
+                 boost::starts_with(value, "data:application/octet-stream;base64,"))
+        {
+          std::string mime, binary;
+          Toolbox::DecodeDataUriScheme(mime, binary, value);
+          dicom.Replace(tag, binary);
         }
         else
         {
-          dicom.Replace(tag, value);
+          dicom.Replace(tag, Toolbox::ConvertFromUtf8(value, dicom.GetEncoding()));
+        }
+      }
+    }
+  }
+
+
+  static void CreateSeries(RestApiPostCall& call,
+                           ParsedDicomFile& base /* in */,
+                           const Json::Value& content,
+                           bool interpretBinaryTags)
+  {
+    assert(content.isArray());
+    assert(content.size() > 0);
+    ServerContext& context = OrthancRestApi::GetContext(call);
+
+    base.Replace(DICOM_TAG_IMAGES_IN_ACQUISITION, boost::lexical_cast<std::string>(content.size()));
+    base.Replace(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS, "1");
+
+    std::string someInstance;
+
+    try
+    {
+      for (Json::ArrayIndex i = 0; i < content.size(); i++)
+      {
+        std::auto_ptr<ParsedDicomFile> dicom(base.Clone());
+        const Json::Value* payload = NULL;
+
+        if (content[i].type() == Json::stringValue)
+        {
+          payload = &content[i];
+        }
+        else if (content[i].type() == Json::objectValue)
+        {
+          if (!content[i].isMember("Content"))
+          {
+            throw OrthancException(ErrorCode_CreateDicomNoPayload);
+          }
+
+          payload = &content[i]["Content"];
+
+          if (content[i].isMember("Tags"))
+          {
+            InjectTags(*dicom, content[i]["Tags"], interpretBinaryTags);
+          }
+        }
+
+        if (payload == NULL ||
+            payload->type() != Json::stringValue)
+        {
+          throw OrthancException(ErrorCode_CreateDicomUseDataUriScheme);
+        }
+
+        dicom->EmbedContent(payload->asString());
+        dicom->Replace(DICOM_TAG_INSTANCE_NUMBER, boost::lexical_cast<std::string>(i + 1));
+        dicom->Replace(DICOM_TAG_IMAGE_INDEX, boost::lexical_cast<std::string>(i + 1));
+
+        StoreCreatedInstance(someInstance, call, *dicom);
+      }
+    }
+    catch (OrthancException&)
+    {
+      // Error: Remove the newly-created series
+      
+      std::string series;
+      if (context.GetIndex().LookupParent(series, someInstance))
+      {
+        Json::Value dummy;
+        context.GetIndex().DeleteResource(dummy, series, ResourceType_Series);
+      }
+
+      throw;
+    }
+
+    std::string series;
+    if (context.GetIndex().LookupParent(series, someInstance))
+    {
+      OrthancRestApi::GetApi(call).AnswerStoredResource(call, series, ResourceType_Series, StoreStatus_Success);
+    }
+  }
+
+
+  static void CreateDicomV2(RestApiPostCall& call,
+                            const Json::Value& request)
+  {
+    assert(request.isObject());
+    ServerContext& context = OrthancRestApi::GetContext(call);
+
+    if (!request.isMember("Tags") ||
+        request["Tags"].type() != Json::objectValue)
+    {
+      throw OrthancException(ErrorCode_BadRequest);
+    }
+
+    ParsedDicomFile dicom;
+
+    {
+      Encoding encoding;
+
+      if (request["Tags"].isMember("SpecificCharacterSet"))
+      {
+        const char* tmp = request["Tags"]["SpecificCharacterSet"].asCString();
+        if (!GetDicomEncoding(encoding, tmp))
+        {
+          LOG(ERROR) << "Unknown specific character set: " << std::string(tmp);
+          throw OrthancException(ErrorCode_ParameterOutOfRange);
+        }
+      }
+      else
+      {
+        std::string tmp = Configuration::GetGlobalStringParameter("DefaultEncoding", "Latin1");
+        encoding = StringToEncoding(tmp.c_str());
+      }
+
+      dicom.SetEncoding(encoding);
+    }
+
+    ResourceType parentType = ResourceType_Instance;
+
+    if (request.isMember("Parent"))
+    {
+      // Locate the parent tags
+      std::string parent = request["Parent"].asString();
+      if (!context.GetIndex().LookupResourceType(parentType, parent))
+      {
+        throw OrthancException(ErrorCode_CreateDicomBadParent);
+      }
+
+      if (parentType == ResourceType_Instance)
+      {
+        throw OrthancException(ErrorCode_CreateDicomParentIsInstance);
+      }
+
+      // Select one existing child instance of the parent resource, to
+      // retrieve all its tags
+      Json::Value siblingTags;
+
+      {
+        // Retrieve all the instances of the parent resource
+        std::list<std::string>  siblingInstances;
+        context.GetIndex().GetChildInstances(siblingInstances, parent);
+
+        if (siblingInstances.empty())
+	{
+	  // Error: No instance (should never happen)
+          throw OrthancException(ErrorCode_InternalError);
+        }
+
+        context.ReadJson(siblingTags, siblingInstances.front());
+      }
+
+
+      // Choose the same encoding as the parent resource
+      {
+        static const char* SPECIFIC_CHARACTER_SET = "0008,0005";
+
+        if (siblingTags.isMember(SPECIFIC_CHARACTER_SET))
+        {
+          Encoding encoding;
+          if (!siblingTags[SPECIFIC_CHARACTER_SET].isMember("Value") ||
+              siblingTags[SPECIFIC_CHARACTER_SET]["Value"].type() != Json::stringValue ||
+              !GetDicomEncoding(encoding, siblingTags[SPECIFIC_CHARACTER_SET]["Value"].asCString()))
+          {
+            throw OrthancException(ErrorCode_CreateDicomParentEncoding);
+          }
+
+          dicom.SetEncoding(encoding);
         }
       }
 
-      DicomInstanceToStore toStore;
-      toStore.SetParsedDicomFile(dicom);
+
+      // Retrieve the tags for all the parent modules
+      typedef std::set<DicomTag> ModuleTags;
+      ModuleTags moduleTags;
+
+      ResourceType type = parentType;
+      for (;;)
+      {
+        DicomTag::AddTagsForModule(moduleTags, GetModule(type));
+      
+        if (type == ResourceType_Patient)
+        {
+          break;   // We're done
+        }
+
+        // Go up
+        std::string tmp;
+        if (!context.GetIndex().LookupParent(tmp, parent))
+        {
+          throw OrthancException(ErrorCode_InternalError);
+        }
+
+        parent = tmp;
+        type = GetParentResourceType(type);
+      }
+
+      for (ModuleTags::const_iterator it = moduleTags.begin();
+           it != moduleTags.end(); ++it)
+      {
+        std::string t = it->Format();
+        if (siblingTags.isMember(t))
+        {
+          const Json::Value& tag = siblingTags[t];
+          if (tag["Type"] == "Null")
+          {
+            dicom.Replace(*it, "");
+          }
+          else if (tag["Type"] == "String")
+          {
+            std::string value = tag["Value"].asString();
+            dicom.Replace(*it, Toolbox::ConvertFromUtf8(value, dicom.GetEncoding()));
+          }
+        }
+      }
+    }
+
+
+    bool interpretBinaryTags = true;
+    if (request.isMember("InterpretBinaryTags"))
+    {
+      const Json::Value& v = request["InterpretBinaryTags"];
+      if (v.type() != Json::booleanValue)
+      {
+        throw OrthancException(ErrorCode_BadRequest);
+      }
+
+      interpretBinaryTags = v.asBool();
+    }
+
+    
+    // Inject time-related information
+    std::string date, time;
+    Toolbox::GetNowDicom(date, time);
+    dicom.Replace(DICOM_TAG_ACQUISITION_DATE, date);
+    dicom.Replace(DICOM_TAG_ACQUISITION_TIME, time);
+    dicom.Replace(DICOM_TAG_CONTENT_DATE, date);
+    dicom.Replace(DICOM_TAG_CONTENT_TIME, time);
+    dicom.Replace(DICOM_TAG_INSTANCE_CREATION_DATE, date);
+    dicom.Replace(DICOM_TAG_INSTANCE_CREATION_TIME, time);
+
+    if (parentType == ResourceType_Patient ||
+        parentType == ResourceType_Study ||
+        parentType == ResourceType_Instance /* no parent */)
+    {
+      dicom.Replace(DICOM_TAG_SERIES_DATE, date);
+      dicom.Replace(DICOM_TAG_SERIES_TIME, time);
+    }
+
+    if (parentType == ResourceType_Patient ||
+        parentType == ResourceType_Instance /* no parent */)
+    {
+      dicom.Replace(DICOM_TAG_STUDY_DATE, date);
+      dicom.Replace(DICOM_TAG_STUDY_TIME, time);
+    }
+
+
+    InjectTags(dicom, request["Tags"], interpretBinaryTags);
+
+
+    // Inject the content (either an image, or a PDF file)
+    if (request.isMember("Content"))
+    {
+      const Json::Value& content = request["Content"];
+
+      if (content.type() == Json::stringValue)
+      {
+        dicom.EmbedContent(request["Content"].asString());
+
+      }
+      else if (content.type() == Json::arrayValue)
+      {
+        if (content.size() > 0)
+        {
+          // Let's create a series instead of a single instance
+          CreateSeries(call, dicom, content, interpretBinaryTags);
+          return;
+        }
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_CreateDicomUseDataUriScheme);
+      }
+    }
+
+    std::string id;
+    StoreCreatedInstance(id, call, dicom);
+    OrthancRestApi::GetApi(call).AnswerStoredResource(call, id, ResourceType_Instance, StoreStatus_Success);
+
+    return;
+  }
+
+
+  static void CreateDicom(RestApiPostCall& call)
+  {
+    Json::Value request;
+    if (!call.ParseJsonRequest(request) ||
+        !request.isObject())
+    {
+      throw OrthancException(ErrorCode_BadRequest);
+    }
+
+    if (request.isMember("Tags"))
+    {
+      CreateDicomV2(call, request);
+    }
+    else
+    {
+      // Compatibility with Orthanc <= 0.9.3
+      ParsedDicomFile dicom;
+      CreateDicomV1(dicom, call, request);
 
       std::string id;
-      StoreStatus status = OrthancRestApi::GetContext(call).Store(id, toStore);
-
-      if (status == StoreStatus_Failure)
-      {
-        LOG(ERROR) << "Error while storing a manually-created instance";
-        return;
-      }
-
-      OrthancRestApi::GetApi(call).AnswerStoredInstance(call, id, status);
+      StoreCreatedInstance(id, call, dicom);
+      OrthancRestApi::GetApi(call).AnswerStoredResource(call, id, ResourceType_Instance, StoreStatus_Success);
     }
   }
 
--- a/OrthancServer/OrthancRestApi/OrthancRestApi.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestApi.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,14 +33,15 @@
 #include "../PrecompiledHeadersServer.h"
 #include "OrthancRestApi.h"
 
+#include "../../Core/Logging.h"
 #include "../DicomModification.h"
-
-#include <glog/logging.h>
+#include "../ServerContext.h"
 
 namespace Orthanc
 {
-  void OrthancRestApi::AnswerStoredInstance(RestApiPostCall& call,
+  void OrthancRestApi::AnswerStoredResource(RestApiPostCall& call,
                                             const std::string& publicId,
+                                            ResourceType resourceType,
                                             StoreStatus status) const
   {
     Json::Value result = Json::objectValue;
@@ -48,7 +49,7 @@
     if (status != StoreStatus_Failure)
     {
       result["ID"] = publicId;
-      result["Path"] = GetBasePath(ResourceType_Instance, publicId);
+      result["Path"] = GetBasePath(resourceType, publicId);
     }
 
     result["Status"] = EnumerationToString(status);
@@ -72,21 +73,24 @@
   {
     ServerContext& context = OrthancRestApi::GetContext(call);
 
-    const std::string& postData = call.GetPostBody();
-    if (postData.size() == 0)
+    if (call.GetBodySize() == 0)
     {
       return;
     }
 
-    LOG(INFO) << "Receiving a DICOM file of " << postData.size() << " bytes through HTTP";
+    LOG(INFO) << "Receiving a DICOM file of " << call.GetBodySize() << " bytes through HTTP";
+
+    // TODO Remove unneccessary memcpy
+    std::string postData(call.GetBodyData(), call.GetBodySize());
 
     DicomInstanceToStore toStore;
+    toStore.SetRestOrigin(call);
     toStore.SetBuffer(postData);
 
     std::string publicId;
     StoreStatus status = context.Store(publicId, toStore);
 
-    OrthancRestApi::GetApi(call).AnswerStoredInstance(call, publicId, status);
+    OrthancRestApi::GetApi(call).AnswerStoredResource(call, publicId, ResourceType_Instance, status);
   }
 
 
@@ -112,4 +116,16 @@
     Register("/tools/reset", ResetOrthanc);
     Register("/instances/{id}/frames/{frame}", RestApi::AutoListChildren);
   }
+
+
+  ServerContext& OrthancRestApi::GetContext(RestApiCall& call)
+  {
+    return GetApi(call).context_;
+  }
+
+
+  ServerIndex& OrthancRestApi::GetIndex(RestApiCall& call)
+  {
+    return GetContext(call).GetIndex();
+  }
 }
--- a/OrthancServer/OrthancRestApi/OrthancRestApi.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestApi.h	Wed Sep 30 13:23:31 2015 +0200
@@ -34,12 +34,14 @@
 
 #include "../../Core/RestApi/RestApi.h"
 #include "../DicomModification.h"
-#include "../ServerContext.h"
 
 #include <set>
 
 namespace Orthanc
 {
+  class ServerContext;
+  class ServerIndex;
+
   class OrthancRestApi : public RestApi
   {
   public:
@@ -76,18 +78,13 @@
       return dynamic_cast<OrthancRestApi&>(call.GetContext());
     }
 
-    static ServerContext& GetContext(RestApiCall& call)
-    {
-      return GetApi(call).context_;
-    }
+    static ServerContext& GetContext(RestApiCall& call);
+
+    static ServerIndex& GetIndex(RestApiCall& call);
 
-    static ServerIndex& GetIndex(RestApiCall& call)
-    {
-      return GetContext(call).GetIndex();
-    }
-
-    void AnswerStoredInstance(RestApiPostCall& call,
+    void AnswerStoredResource(RestApiPostCall& call,
                               const std::string& publicId,
+                              ResourceType resourceType,
                               StoreStatus status) const;
 
     static bool ParseModifyRequest(DicomModification& target,
--- a/OrthancServer/OrthancRestApi/OrthancRestArchive.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestArchive.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -36,9 +36,10 @@
 #include "../DicomDirWriter.h"
 #include "../../Core/Compression/HierarchicalZipWriter.h"
 #include "../../Core/HttpServer/FilesystemHttpSender.h"
+#include "../../Core/Logging.h"
 #include "../../Core/Uuid.h"
+#include "../ServerContext.h"
 
-#include <glog/logging.h>
 #include <stdio.h>
 
 #if defined(_MSC_VER)
@@ -56,27 +57,34 @@
                                                ResourceType resourceType)
   {
     std::string s;
+    const Json::Value& tags = resource["MainDicomTags"];
 
     switch (resourceType)
     {
       case ResourceType_Patient:
       {
-        std::string p = resource["MainDicomTags"]["PatientID"].asString();
-        std::string n = resource["MainDicomTags"]["PatientName"].asString();
+        std::string p = tags["PatientID"].asString();
+        std::string n = tags["PatientName"].asString();
         s = p + " " + n;
         break;
       }
 
       case ResourceType_Study:
       {
-        s = resource["MainDicomTags"]["StudyDescription"].asString();
+        std::string p;
+        if (tags.isMember("AccessionNumber"))
+        {
+          p = tags["AccessionNumber"].asString() + " ";
+        }
+
+        s = p + tags["StudyDescription"].asString();
         break;
       }
         
       case ResourceType_Series:
       {
-        std::string d = resource["MainDicomTags"]["SeriesDescription"].asString();
-        std::string m = resource["MainDicomTags"]["Modality"].asString();
+        std::string d = tags["SeriesDescription"].asString();
+        std::string m = tags["Modality"].asString();
         s = m + " " + d;
         break;
       }
@@ -191,7 +199,7 @@
       case ResourceType_Series:
       {
         // Create a filename prefix, depending on the modality
-        char format[16] = "%08d";
+        char format[24] = "%08d.dcm";
 
         if (resource["MainDicomTags"].isMember("Modality"))
         {
@@ -199,15 +207,15 @@
 
           if (modality.size() == 1)
           {
-            snprintf(format, sizeof(format) - 1, "%c%%07d", toupper(modality[0]));
+            snprintf(format, sizeof(format) - 1, "%c%%07d.dcm", toupper(modality[0]));
           }
           else if (modality.size() >= 2)
           {
-            snprintf(format, sizeof(format) - 1, "%c%c%%06d", toupper(modality[0]), toupper(modality[1]));
+            snprintf(format, sizeof(format) - 1, "%c%c%%06d.dcm", toupper(modality[0]), toupper(modality[1]));
           }
         }
 
-        char filename[16];
+        char filename[24];
 
         for (Json::Value::ArrayIndex i = 0; i < resource["Instances"].size(); i++)
         {
@@ -288,12 +296,12 @@
     }
 
     // Prepare the sending of the ZIP file
-    FilesystemHttpSender sender(tmp.GetPath().c_str());
+    FilesystemHttpSender sender(tmp.GetPath());
     sender.SetContentType("application/zip");
-    sender.SetDownloadFilename(id + ".zip");
+    sender.SetContentFilename(id + ".zip");
 
     // Send the ZIP
-    call.GetOutput().AnswerFile(sender);
+    call.GetOutput().AnswerStream(sender);
 
     // The temporary file is automatically removed thanks to the RAII
   }
@@ -349,12 +357,12 @@
     }
 
     // Prepare the sending of the ZIP file
-    FilesystemHttpSender sender(tmp.GetPath().c_str());
+    FilesystemHttpSender sender(tmp.GetPath());
     sender.SetContentType("application/zip");
-    sender.SetDownloadFilename(id + ".zip");
+    sender.SetContentFilename(id + ".zip");
 
     // Send the ZIP
-    call.GetOutput().AnswerFile(sender);
+    call.GetOutput().AnswerStream(sender);
 
     // The temporary file is automatically removed thanks to the RAII
   }
--- a/OrthancServer/OrthancRestApi/OrthancRestChanges.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestChanges.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,7 +33,7 @@
 #include "../PrecompiledHeadersServer.h"
 #include "OrthancRestApi.h"
 
-#include <glog/logging.h>
+#include "../ServerContext.h"
 
 namespace Orthanc
 {
--- a/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -35,46 +35,27 @@
 
 #include "../OrthancInitialization.h"
 #include "../../Core/HttpClient.h"
+#include "../../Core/Logging.h"
 #include "../FromDcmtkBridge.h"
 #include "../Scheduler/ServerJob.h"
 #include "../Scheduler/StoreScuCommand.h"
 #include "../Scheduler/StorePeerCommand.h"
-
-#include <glog/logging.h>
+#include "../QueryRetrieveHandler.h"
+#include "../ServerToolbox.h"
 
 namespace Orthanc
 {
-  // DICOM SCU ----------------------------------------------------------------
-
-  static bool MergeQueryAndTemplate(DicomMap& result,
-                                    const std::string& postData)
-  {
-    Json::Value query;
-    Json::Reader reader;
-
-    if (!reader.parse(postData, query) ||
-        query.type() != Json::objectValue)
-    {
-      return false;
-    }
-
-    Json::Value::Members members = query.getMemberNames();
-    for (size_t i = 0; i < members.size(); i++)
-    {
-      DicomTag t = FromDcmtkBridge::ParseTag(members[i]);
-      result.SetValue(t, query[members[i]].asString());
-    }
-
-    return true;
-  }
-
+  /***************************************************************************
+   * DICOM C-Echo SCU
+   ***************************************************************************/
 
   static void DicomEcho(RestApiPostCall& call)
   {
     ServerContext& context = OrthancRestApi::GetContext(call);
 
+    const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle();
     RemoteModalityParameters remote = Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", ""));
-    ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), remote);
+    ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), localAet, remote);
 
     try
     {
@@ -94,148 +75,246 @@
   }
 
 
+
+  /***************************************************************************
+   * DICOM C-Find SCU => DEPRECATED!
+   ***************************************************************************/
+
+  static bool MergeQueryAndTemplate(DicomMap& result,
+                                    const char* postData,
+                                    size_t postSize)
+  {
+    Json::Value query;
+    Json::Reader reader;
+
+    if (!reader.parse(postData, postData + postSize, query) ||
+        query.type() != Json::objectValue)
+    {
+      return false;
+    }
+
+    Json::Value::Members members = query.getMemberNames();
+    for (size_t i = 0; i < members.size(); i++)
+    {
+      DicomTag t = FromDcmtkBridge::ParseTag(members[i]);
+      result.SetValue(t, query[members[i]].asString());
+    }
+
+    return true;
+  }
+
+
+  static void FindPatient(DicomFindAnswers& result,
+                          DicomUserConnection& connection,
+                          const DicomMap& fields)
+  {
+    // Only keep the filters from "fields" that are related to the patient
+    DicomMap s;
+    fields.ExtractPatientInformation(s);
+    connection.Find(result, ResourceType_Patient, s);
+  }
+
+
+  static void FindStudy(DicomFindAnswers& result,
+                        DicomUserConnection& connection,
+                        const DicomMap& fields)
+  {
+    // Only keep the filters from "fields" that are related to the study
+    DicomMap s;
+    fields.ExtractStudyInformation(s);
+
+    s.CopyTagIfExists(fields, DICOM_TAG_PATIENT_ID);
+    s.CopyTagIfExists(fields, DICOM_TAG_ACCESSION_NUMBER);
+    s.CopyTagIfExists(fields, DICOM_TAG_MODALITIES_IN_STUDY);
+
+    connection.Find(result, ResourceType_Study, s);
+  }
+
+  static void FindSeries(DicomFindAnswers& result,
+                         DicomUserConnection& connection,
+                         const DicomMap& fields)
+  {
+    // Only keep the filters from "fields" that are related to the series
+    DicomMap s;
+    fields.ExtractSeriesInformation(s);
+
+    s.CopyTagIfExists(fields, DICOM_TAG_PATIENT_ID);
+    s.CopyTagIfExists(fields, DICOM_TAG_ACCESSION_NUMBER);
+    s.CopyTagIfExists(fields, DICOM_TAG_STUDY_INSTANCE_UID);
+
+    connection.Find(result, ResourceType_Series, s);
+  }
+
+  static void FindInstance(DicomFindAnswers& result,
+                           DicomUserConnection& connection,
+                           const DicomMap& fields)
+  {
+    // Only keep the filters from "fields" that are related to the instance
+    DicomMap s;
+    fields.ExtractInstanceInformation(s);
+
+    s.CopyTagIfExists(fields, DICOM_TAG_PATIENT_ID);
+    s.CopyTagIfExists(fields, DICOM_TAG_ACCESSION_NUMBER);
+    s.CopyTagIfExists(fields, DICOM_TAG_STUDY_INSTANCE_UID);
+    s.CopyTagIfExists(fields, DICOM_TAG_SERIES_INSTANCE_UID);
+
+    connection.Find(result, ResourceType_Instance, s);
+  }
+
+
   static void DicomFindPatient(RestApiPostCall& call)
   {
+    LOG(WARNING) << "This URI is deprecated: " << call.FlattenUri();
     ServerContext& context = OrthancRestApi::GetContext(call);
 
-    DicomMap m;
-    DicomMap::SetupFindPatientTemplate(m);
-    if (!MergeQueryAndTemplate(m, call.GetPostBody()))
+    DicomMap fields;
+    DicomMap::SetupFindPatientTemplate(fields);
+    if (!MergeQueryAndTemplate(fields, call.GetBodyData(), call.GetBodySize()))
     {
       return;
     }
 
+    const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle();
     RemoteModalityParameters remote = Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", ""));
-    ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), remote);
+    ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), localAet, remote);
 
     DicomFindAnswers answers;
-    locker.GetConnection().FindPatient(answers, m);
+    FindPatient(answers, locker.GetConnection(), fields);
 
     Json::Value result;
-    answers.ToJson(result);
+    answers.ToJson(result, true);
     call.GetOutput().AnswerJson(result);
   }
 
   static void DicomFindStudy(RestApiPostCall& call)
   {
+    LOG(WARNING) << "This URI is deprecated: " << call.FlattenUri();
     ServerContext& context = OrthancRestApi::GetContext(call);
 
-    DicomMap m;
-    DicomMap::SetupFindStudyTemplate(m);
-    if (!MergeQueryAndTemplate(m, call.GetPostBody()))
+    DicomMap fields;
+    DicomMap::SetupFindStudyTemplate(fields);
+    if (!MergeQueryAndTemplate(fields, call.GetBodyData(), call.GetBodySize()))
     {
       return;
     }
 
-    if (m.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 &&
-        m.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2)
+    if (fields.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 &&
+        fields.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2)
     {
       return;
     }        
       
+    const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle();
     RemoteModalityParameters remote = Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", ""));
-    ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), remote);
+    ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), localAet, remote);
 
     DicomFindAnswers answers;
-    locker.GetConnection().FindStudy(answers, m);
+    FindStudy(answers, locker.GetConnection(), fields);
 
     Json::Value result;
-    answers.ToJson(result);
+    answers.ToJson(result, true);
     call.GetOutput().AnswerJson(result);
   }
 
   static void DicomFindSeries(RestApiPostCall& call)
   {
+    LOG(WARNING) << "This URI is deprecated: " << call.FlattenUri();
     ServerContext& context = OrthancRestApi::GetContext(call);
 
-    DicomMap m;
-    DicomMap::SetupFindSeriesTemplate(m);
-    if (!MergeQueryAndTemplate(m, call.GetPostBody()))
+    DicomMap fields;
+    DicomMap::SetupFindSeriesTemplate(fields);
+    if (!MergeQueryAndTemplate(fields, call.GetBodyData(), call.GetBodySize()))
     {
       return;
     }
 
-    if ((m.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 &&
-         m.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2) ||
-        m.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString().size() <= 2)
+    if ((fields.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 &&
+         fields.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2) ||
+        fields.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString().size() <= 2)
     {
       return;
     }        
          
+    const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle();
     RemoteModalityParameters remote = Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", ""));
-    ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), remote);
+    ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), localAet, remote);
 
     DicomFindAnswers answers;
-    locker.GetConnection().FindSeries(answers, m);
+    FindSeries(answers, locker.GetConnection(), fields);
 
     Json::Value result;
-    answers.ToJson(result);
+    answers.ToJson(result, true);
     call.GetOutput().AnswerJson(result);
   }
 
   static void DicomFindInstance(RestApiPostCall& call)
   {
+    LOG(WARNING) << "This URI is deprecated: " << call.FlattenUri();
     ServerContext& context = OrthancRestApi::GetContext(call);
 
-    DicomMap m;
-    DicomMap::SetupFindInstanceTemplate(m);
-    if (!MergeQueryAndTemplate(m, call.GetPostBody()))
+    DicomMap fields;
+    DicomMap::SetupFindInstanceTemplate(fields);
+    if (!MergeQueryAndTemplate(fields, call.GetBodyData(), call.GetBodySize()))
     {
       return;
     }
 
-    if ((m.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 &&
-         m.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2) ||
-        m.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString().size() <= 2 ||
-        m.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).AsString().size() <= 2)
+    if ((fields.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 &&
+         fields.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2) ||
+        fields.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString().size() <= 2 ||
+        fields.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).AsString().size() <= 2)
     {
       return;
     }        
          
+    const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle();
     RemoteModalityParameters remote = Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", ""));
-    ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), remote);
+    ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), localAet, remote);
 
     DicomFindAnswers answers;
-    locker.GetConnection().FindInstance(answers, m);
+    FindInstance(answers, locker.GetConnection(), fields);
 
     Json::Value result;
-    answers.ToJson(result);
+    answers.ToJson(result, true);
     call.GetOutput().AnswerJson(result);
   }
 
+
   static void DicomFind(RestApiPostCall& call)
   {
+    LOG(WARNING) << "This URI is deprecated: " << call.FlattenUri();
     ServerContext& context = OrthancRestApi::GetContext(call);
 
     DicomMap m;
     DicomMap::SetupFindPatientTemplate(m);
-    if (!MergeQueryAndTemplate(m, call.GetPostBody()))
+    if (!MergeQueryAndTemplate(m, call.GetBodyData(), call.GetBodySize()))
     {
       return;
     }
  
+    const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle();
     RemoteModalityParameters remote = Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", ""));
-    ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), remote);
+    ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), localAet, remote);
 
     DicomFindAnswers patients;
-    locker.GetConnection().FindPatient(patients, m);
+    FindPatient(patients, locker.GetConnection(), m);
 
     // Loop over the found patients
     Json::Value result = Json::arrayValue;
     for (size_t i = 0; i < patients.GetSize(); i++)
     {
       Json::Value patient(Json::objectValue);
-      FromDcmtkBridge::ToJson(patient, patients.GetAnswer(i));
+      FromDcmtkBridge::ToJson(patient, patients.GetAnswer(i), true);
 
       DicomMap::SetupFindStudyTemplate(m);
-      if (!MergeQueryAndTemplate(m, call.GetPostBody()))
+      if (!MergeQueryAndTemplate(m, call.GetBodyData(), call.GetBodySize()))
       {
         return;
       }
       m.CopyTagIfExists(patients.GetAnswer(i), DICOM_TAG_PATIENT_ID);
 
       DicomFindAnswers studies;
-      locker.GetConnection().FindStudy(studies, m);
+      FindStudy(studies, locker.GetConnection(), m);
 
       patient["Studies"] = Json::arrayValue;
       
@@ -243,10 +322,10 @@
       for (size_t j = 0; j < studies.GetSize(); j++)
       {
         Json::Value study(Json::objectValue);
-        FromDcmtkBridge::ToJson(study, studies.GetAnswer(j));
+        FromDcmtkBridge::ToJson(study, studies.GetAnswer(j), true);
 
         DicomMap::SetupFindSeriesTemplate(m);
-        if (!MergeQueryAndTemplate(m, call.GetPostBody()))
+        if (!MergeQueryAndTemplate(m, call.GetBodyData(), call.GetBodySize()))
         {
           return;
         }
@@ -254,14 +333,14 @@
         m.CopyTagIfExists(studies.GetAnswer(j), DICOM_TAG_STUDY_INSTANCE_UID);
 
         DicomFindAnswers series;
-        locker.GetConnection().FindSeries(series, m);
+        FindSeries(series, locker.GetConnection(), m);
 
         // Loop over the found series
         study["Series"] = Json::arrayValue;
         for (size_t k = 0; k < series.GetSize(); k++)
         {
           Json::Value series2(Json::objectValue);
-          FromDcmtkBridge::ToJson(series2, series.GetAnswer(k));
+          FromDcmtkBridge::ToJson(series2, series.GetAnswer(k), true);
           study["Series"].append(series2);
         }
 
@@ -275,19 +354,228 @@
   }
 
 
-  static bool GetInstancesToExport(std::list<std::string>& instances,
-                                   const std::string& remote,
-                                   RestApiPostCall& call)
+
+  /***************************************************************************
+   * DICOM C-Find and C-Move SCU => Recommended since Orthanc 0.9.0
+   ***************************************************************************/
+
+  static void DicomQuery(RestApiPostCall& call)
+  {
+    ServerContext& context = OrthancRestApi::GetContext(call);
+    Json::Value request;
+
+    if (call.ParseJsonRequest(request) &&
+        request.type() == Json::objectValue &&
+        request.isMember("Level") && request["Level"].type() == Json::stringValue &&
+        (!request.isMember("Query") || request["Query"].type() == Json::objectValue))
+    {
+      std::auto_ptr<QueryRetrieveHandler>  handler(new QueryRetrieveHandler(context));
+
+      handler->SetModality(call.GetUriComponent("id", ""));
+      handler->SetLevel(StringToResourceType(request["Level"].asString().c_str()));
+
+      if (request.isMember("Query"))
+      {
+        Json::Value::Members tags = request["Query"].getMemberNames();
+        for (size_t i = 0; i < tags.size(); i++)
+        {
+          handler->SetQuery(FromDcmtkBridge::ParseTag(tags[i].c_str()),
+                            request["Query"][tags[i]].asString());
+        }
+      }
+
+      handler->Run();
+
+      std::string s = context.GetQueryRetrieveArchive().Add(handler.release());
+      Json::Value result = Json::objectValue;
+      result["ID"] = s;
+      result["Path"] = "/queries/" + s;
+      call.GetOutput().AnswerJson(result);      
+    }
+  }
+
+
+  static void ListQueries(RestApiGetCall& call)
   {
     ServerContext& context = OrthancRestApi::GetContext(call);
 
-    std::string stripped = Toolbox::StripSpaces(call.GetPostBody());
+    std::list<std::string> queries;
+    context.GetQueryRetrieveArchive().List(queries);
+
+    Json::Value result = Json::arrayValue;
+    for (std::list<std::string>::const_iterator
+           it = queries.begin(); it != queries.end(); ++it)
+    {
+      result.append(*it);
+    }
+
+    call.GetOutput().AnswerJson(result);
+  }
+
+
+  namespace
+  {
+    class QueryAccessor
+    {
+    private:
+      ServerContext&            context_;
+      SharedArchive::Accessor   accessor_;
+      QueryRetrieveHandler&     handler_;
+
+    public:
+      QueryAccessor(RestApiCall& call) :
+        context_(OrthancRestApi::GetContext(call)),
+        accessor_(context_.GetQueryRetrieveArchive(), call.GetUriComponent("id", "")),
+        handler_(dynamic_cast<QueryRetrieveHandler&>(accessor_.GetItem()))
+      {
+      }                     
+
+      QueryRetrieveHandler* operator->()
+      {
+        return &handler_;
+      }
+    };
+
+    static void AnswerDicomMap(RestApiCall& call,
+                               const DicomMap& value,
+                               bool simplify)
+    {
+      Json::Value full = Json::objectValue;
+      FromDcmtkBridge::ToJson(full, value, simplify);
+      call.GetOutput().AnswerJson(full);
+    }
+  }
+
+
+  static void ListQueryAnswers(RestApiGetCall& call)
+  {
+    QueryAccessor query(call);
+    size_t count = query->GetAnswerCount();
+
+    Json::Value result = Json::arrayValue;
+    for (size_t i = 0; i < count; i++)
+    {
+      result.append(boost::lexical_cast<std::string>(i));
+    }
+
+    call.GetOutput().AnswerJson(result);
+  }
+
+
+  static void GetQueryOneAnswer(RestApiGetCall& call)
+  {
+    size_t index = boost::lexical_cast<size_t>(call.GetUriComponent("index", ""));
+    QueryAccessor query(call);
+    AnswerDicomMap(call, query->GetAnswer(index), call.HasArgument("simplify"));
+  }
+
+
+  static void RetrieveOneAnswer(RestApiPostCall& call)
+  {
+    size_t index = boost::lexical_cast<size_t>(call.GetUriComponent("index", ""));
+
+    std::string modality;
+    call.BodyToString(modality);
+
+    LOG(WARNING) << "Driving C-Move SCU on modality: " << modality;
+
+    QueryAccessor query(call);
+    query->Retrieve(modality, index);
+
+    // Retrieve has succeeded
+    call.GetOutput().AnswerBuffer("{}", "application/json");
+  }
+
+
+  static void RetrieveAllAnswers(RestApiPostCall& call)
+  {
+    std::string modality;
+    call.BodyToString(modality);
+
+    LOG(WARNING) << "Driving C-Move SCU on modality: " << modality;
+
+    QueryAccessor query(call);
+    query->Retrieve(modality);
+
+    // Retrieve has succeeded
+    call.GetOutput().AnswerBuffer("{}", "application/json");
+  }
+
+
+  static void GetQueryArguments(RestApiGetCall& call)
+  {
+    QueryAccessor query(call);
+    AnswerDicomMap(call, query->GetQuery(), call.HasArgument("simplify"));
+  }
+
+
+  static void GetQueryLevel(RestApiGetCall& call)
+  {
+    QueryAccessor query(call);
+    call.GetOutput().AnswerBuffer(EnumerationToString(query->GetLevel()), "text/plain");
+  }
+
+
+  static void GetQueryModality(RestApiGetCall& call)
+  {
+    QueryAccessor query(call);
+    call.GetOutput().AnswerBuffer(query->GetModalitySymbolicName(), "text/plain");
+  }
+
+
+  static void DeleteQuery(RestApiDeleteCall& call)
+  {
+    ServerContext& context = OrthancRestApi::GetContext(call);
+    context.GetQueryRetrieveArchive().Remove(call.GetUriComponent("id", ""));
+    call.GetOutput().AnswerBuffer("", "text/plain");
+  }
+
+
+  static void ListQueryOperations(RestApiGetCall& call)
+  {
+    // Ensure that the query of interest does exist
+    QueryAccessor query(call);  
+
+    RestApi::AutoListChildren(call);
+  }
+
+
+  static void ListQueryAnswerOperations(RestApiGetCall& call)
+  {
+    // Ensure that the query of interest does exist
+    QueryAccessor query(call);
+
+    // Ensure that the answer of interest does exist
+    size_t index = boost::lexical_cast<size_t>(call.GetUriComponent("index", ""));
+    query->GetAnswer(index);
+
+    RestApi::AutoListChildren(call);
+  }
+
+
+
+
+  /***************************************************************************
+   * DICOM C-Store SCU
+   ***************************************************************************/
+
+  static bool GetInstancesToExport(Json::Value& otherArguments,
+                                   std::list<std::string>& instances,
+                                   const std::string& remote,
+                                   RestApiPostCall& call)
+  {
+    otherArguments = Json::objectValue;
+    ServerContext& context = OrthancRestApi::GetContext(call);
 
     Json::Value request;
-    if (Toolbox::IsSHA1(stripped))
+    if (Toolbox::IsSHA1(call.GetBodyData(), call.GetBodySize()))
     {
+      std::string s;
+      call.BodyToString(s);
+
       // This is for compatibility with Orthanc <= 0.5.1.
-      request = stripped;
+      request = Json::arrayValue;
+      request.append(Toolbox::StripSpaces(s));
     }
     else if (!call.ParseJsonRequest(request))
     {
@@ -297,47 +585,64 @@
 
     if (request.isString())
     {
-      if (Configuration::GetGlobalBoolParameter("LogExportedResources", true))
-      {
-        context.GetIndex().LogExportedResource(request.asString(), remote);
-      }
-
-      context.GetIndex().GetChildInstances(instances, request.asString());
+      std::string item = request.asString();
+      request = Json::arrayValue;
+      request.append(item);
     }
-    else if (request.isArray())
-    {
-      for (Json::Value::ArrayIndex i = 0; i < request.size(); i++)
-      {
-        if (!request[i].isString())
-        {
-          return false;
-        }
 
-        std::string stripped = Toolbox::StripSpaces(request[i].asString());
-        if (!Toolbox::IsSHA1(stripped))
-        {
-          return false;
-        }
-
-        if (Configuration::GetGlobalBoolParameter("LogExportedResources", true))
-        {
-          context.GetIndex().LogExportedResource(stripped, remote);
-        }
-       
-        std::list<std::string> tmp;
-        context.GetIndex().GetChildInstances(tmp, stripped);
-
-        for (std::list<std::string>::const_iterator
-               it = tmp.begin(); it != tmp.end(); ++it)
-        {
-          instances.push_back(*it);
-        }
-      }
+    const Json::Value* resources;
+    if (request.isArray())
+    {
+      resources = &request;
     }
     else
     {
-      // Neither a string, nor a list of strings. Bad request.
-      return false;
+      if (request.type() != Json::objectValue ||
+          !request.isMember("Resources"))
+      {
+        return false;
+      }
+
+      resources = &request["Resources"];
+      if (!resources->isArray())
+      {
+        return false;
+      }
+
+      // Copy the remaining arguments
+      Json::Value::Members members = request.getMemberNames();
+      for (Json::Value::ArrayIndex i = 0; i < members.size(); i++)
+      {
+        otherArguments[members[i]] = request[members[i]];
+      }
+    }
+
+    for (Json::Value::ArrayIndex i = 0; i < resources->size(); i++)
+    {
+      if (!(*resources) [i].isString())
+      {
+        return false;
+      }
+
+      std::string stripped = Toolbox::StripSpaces((*resources) [i].asString());
+      if (!Toolbox::IsSHA1(stripped))
+      {
+        return false;
+      }
+
+      if (Configuration::GetGlobalBoolParameter("LogExportedResources", true))
+      {
+        context.GetIndex().LogExportedResource(stripped, remote);
+      }
+       
+      std::list<std::string> tmp;
+      context.GetIndex().GetChildInstances(tmp, stripped);
+
+      for (std::list<std::string>::const_iterator
+             it = tmp.begin(); it != tmp.end(); ++it)
+      {
+        instances.push_back(*it);
+      }
     }
 
     return true;
@@ -350,19 +655,26 @@
 
     std::string remote = call.GetUriComponent("id", "");
 
+    Json::Value request;
     std::list<std::string> instances;
-    if (!GetInstancesToExport(instances, remote, call))
+    if (!GetInstancesToExport(request, instances, remote, call))
     {
       return;
     }
 
+    std::string localAet = context.GetDefaultLocalApplicationEntityTitle();
+    if (request.isMember("LocalAet"))
+    {
+      localAet = request["LocalAet"].asString();
+    }
+
     RemoteModalityParameters p = Configuration::GetModalityUsingSymbolicName(remote);
 
     ServerJob job;
     for (std::list<std::string>::const_iterator 
            it = instances.begin(); it != instances.end(); ++it)
     {
-      job.AddCommand(new StoreScuCommand(context, p, false)).AddInput(*it);
+      job.AddCommand(new StoreScuCommand(context, localAet, p, false)).AddInput(*it);
     }
 
     job.SetDescription("HTTP request: Store-SCU to peer \"" + remote + "\"");
@@ -379,7 +691,9 @@
   }
 
 
-  // Orthanc Peers ------------------------------------------------------------
+  /***************************************************************************
+   * Orthanc Peers => Store client
+   ***************************************************************************/
 
   static bool IsExistingPeer(const OrthancRestApi::SetOfStrings& peers,
                              const std::string& id)
@@ -420,8 +734,9 @@
 
     std::string remote = call.GetUriComponent("id", "");
 
+    Json::Value request;
     std::list<std::string> instances;
-    if (!GetInstancesToExport(instances, remote, call))
+    if (!GetInstancesToExport(request, instances, remote, call))
     {
       return;
     }
@@ -491,7 +806,7 @@
   {
     Json::Value json;
     Json::Reader reader;
-    if (reader.parse(call.GetPutBody(), json))
+    if (reader.parse(call.GetBodyData(), call.GetBodyData() + call.GetBodySize(), json))
     {
       RemoteModalityParameters modality;
       modality.FromJson(json);
@@ -512,7 +827,7 @@
   {
     Json::Value json;
     Json::Reader reader;
-    if (reader.parse(call.GetPutBody(), json))
+    if (reader.parse(call.GetBodyData(), call.GetBodyData() + call.GetBodySize(), json))
     {
       OrthancPeerParameters peer;
       peer.FromJson(json);
@@ -543,6 +858,20 @@
     Register("/modalities/{id}/find", DicomFind);
     Register("/modalities/{id}/store", DicomStore);
 
+    // For Query/Retrieve
+    Register("/modalities/{id}/query", DicomQuery);
+    Register("/queries", ListQueries);
+    Register("/queries/{id}", DeleteQuery);
+    Register("/queries/{id}", ListQueryOperations);
+    Register("/queries/{id}/answers", ListQueryAnswers);
+    Register("/queries/{id}/answers/{index}", ListQueryAnswerOperations);
+    Register("/queries/{id}/answers/{index}/content", GetQueryOneAnswer);
+    Register("/queries/{id}/answers/{index}/retrieve", RetrieveOneAnswer);
+    Register("/queries/{id}/level", GetQueryLevel);
+    Register("/queries/{id}/modality", GetQueryModality);
+    Register("/queries/{id}/query", GetQueryArguments);
+    Register("/queries/{id}/retrieve", RetrieveAllAnswers);
+
     Register("/peers", ListPeers);
     Register("/peers/{id}", ListPeerOperations);
     Register("/peers/{id}", UpdatePeer);
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,21 +33,80 @@
 #include "../PrecompiledHeadersServer.h"
 #include "OrthancRestApi.h"
 
+#include "../../Core/Logging.h"
 #include "../ServerToolbox.h"
 #include "../FromDcmtkBridge.h"
+#include "../ResourceFinder.h"
+#include "../DicomFindQuery.h"
+#include "../ServerContext.h"
 
-#include <glog/logging.h>
 
 namespace Orthanc
 {
   // List all the patients, studies, series or instances ----------------------
  
+  static void AnswerListOfResources(RestApiOutput& output,
+                                    ServerIndex& index,
+                                    const std::list<std::string>& resources,
+                                    ResourceType level,
+                                    bool expand)
+  {
+    Json::Value answer = Json::arrayValue;
+
+    for (std::list<std::string>::const_iterator
+           resource = resources.begin(); resource != resources.end(); ++resource)
+    {
+      if (expand)
+      {
+        Json::Value item;
+        if (index.LookupResource(item, *resource, level))
+        {
+          answer.append(item);
+        }
+      }
+      else
+      {
+        answer.append(*resource);
+      }
+    }
+
+    output.AnswerJson(answer);
+  }
+
+
   template <enum ResourceType resourceType>
   static void ListResources(RestApiGetCall& call)
   {
-    Json::Value result;
-    OrthancRestApi::GetIndex(call).GetAllUuids(result, resourceType);
-    call.GetOutput().AnswerJson(result);
+    ServerIndex& index = OrthancRestApi::GetIndex(call);
+
+    std::list<std::string> result;
+
+    if (call.HasArgument("limit") ||
+        call.HasArgument("since"))
+    {
+      if (!call.HasArgument("limit"))
+      {
+        LOG(ERROR) << "Missing \"limit\" argument for GET request against: " << call.FlattenUri();
+        throw OrthancException(ErrorCode_BadRequest);
+      }
+
+      if (!call.HasArgument("since"))
+      {
+        LOG(ERROR) << "Missing \"since\" argument for GET request against: " << call.FlattenUri();
+        throw OrthancException(ErrorCode_BadRequest);
+      }
+
+      size_t since = boost::lexical_cast<size_t>(call.GetArgument("since", ""));
+      size_t limit = boost::lexical_cast<size_t>(call.GetArgument("limit", ""));
+      index.GetAllUuids(result, resourceType, since, limit);
+    }
+    else
+    {
+      index.GetAllUuids(result, resourceType);
+    }
+
+
+    AnswerListOfResources(call.GetOutput(), index, result, resourceType, call.HasArgument("expand"));
   }
 
   template <enum ResourceType resourceType>
@@ -86,14 +145,17 @@
     ServerContext& context = OrthancRestApi::GetContext(call);
 
     std::string publicId = call.GetUriComponent("id", "");
-    std::string s = Toolbox::StripSpaces(call.GetPutBody());
 
-    if (s == "0")
+    std::string body;
+    call.BodyToString(body);
+    body = Toolbox::StripSpaces(body);
+
+    if (body == "0")
     {
       context.GetIndex().SetProtectedPatient(publicId, false);
       call.GetOutput().AnswerBuffer("", "text/plain");
     }
-    else if (s == "1")
+    else if (body == "1")
     {
       context.GetIndex().SetProtectedPatient(publicId, true);
       call.GetOutput().AnswerBuffer("", "text/plain");
@@ -125,7 +187,9 @@
     std::string dicom;
     context.ReadFile(dicom, publicId, FileContentType_Dicom);
 
-    Toolbox::WriteFile(dicom, call.GetPostBody());
+    std::string target;
+    call.BodyToString(target);
+    Toolbox::WriteFile(dicom, target);
 
     call.GetOutput().AnswerBuffer("{}", "application/json");
   }
@@ -359,7 +423,9 @@
     std::string publicId = call.GetUriComponent("id", "");
     std::string name = call.GetUriComponent("name", "");
     MetadataType metadata = StringToMetadata(name);
-    std::string value = call.GetPutBody();
+
+    std::string value;
+    call.BodyToString(value);
 
     if (metadata >= MetadataType_StartUser &&
         metadata <= MetadataType_EndUser)
@@ -569,12 +635,10 @@
     std::string publicId = call.GetUriComponent("id", "");
     std::string name = call.GetUriComponent("name", "");
 
-    const void* data = call.GetPutBody().size() ? &call.GetPutBody()[0] : NULL;
-
     FileContentType contentType = StringToContentType(name);
     if (contentType >= FileContentType_StartUser &&  // It is forbidden to modify internal attachments
         contentType <= FileContentType_EndUser &&
-        context.AddAttachment(publicId, StringToContentType(name), data, call.GetPutBody().size()))
+        context.AddAttachment(publicId, StringToContentType(name), call.GetBodyData(), call.GetBodySize()))
     {
       call.GetOutput().AnswerBuffer("{}", "application/json");
     }
@@ -731,7 +795,7 @@
 
     typedef std::set<DicomTag> ModuleTags;
     ModuleTags moduleTags;
-    DicomTag::GetTagsForModule(moduleTags, module);
+    DicomTag::AddTagsForModule(moduleTags, module);
 
     Json::Value tags;
 
@@ -790,7 +854,8 @@
   {
     typedef std::list< std::pair<ResourceType, std::string> >  Resources;
 
-    std::string tag = call.GetPostBody();
+    std::string tag;
+    call.BodyToString(tag);
     Resources resources;
 
     OrthancRestApi::GetIndex(call).LookupIdentifier(resources, tag);
@@ -815,6 +880,61 @@
   }
 
 
+  static void Find(RestApiPostCall& call)
+  {
+    ServerContext& context = OrthancRestApi::GetContext(call);
+
+    Json::Value request;
+    if (call.ParseJsonRequest(request) &&
+        request.type() == Json::objectValue &&
+        request.isMember("Level") &&
+        request.isMember("Query") &&
+        request["Level"].type() == Json::stringValue &&
+        request["Query"].type() == Json::objectValue &&
+        (!request.isMember("CaseSensitive") || request["CaseSensitive"].type() == Json::booleanValue))
+    {
+      bool expand = false;
+      if (request.isMember("Expand"))
+      {
+        expand = request["Expand"].asBool();
+      }
+
+      bool caseSensitive = false;
+      if (request.isMember("CaseSensitive"))
+      {
+        caseSensitive = request["CaseSensitive"].asBool();
+      }
+
+      std::string level = request["Level"].asString();
+
+      DicomFindQuery query;
+      query.SetLevel(StringToResourceType(level.c_str()));
+
+      Json::Value::Members members = request["Query"].getMemberNames();
+      for (size_t i = 0; i < members.size(); i++)
+      {
+        if (request["Query"][members[i]].type() != Json::stringValue)
+        {
+          throw OrthancException(ErrorCode_BadRequest);
+        }
+
+        query.SetConstraint(FromDcmtkBridge::ParseTag(members[i]), 
+                            request["Query"][members[i]].asString(),
+                            caseSensitive);
+      }
+      
+      std::list<std::string> resources;
+      ResourceFinder finder(context);
+      finder.Apply(resources, query);
+      AnswerListOfResources(call.GetOutput(), context.GetIndex(), resources, query.GetLevel(), expand);
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_BadRequest);
+    }
+  }
+
+
   template <enum ResourceType start, 
             enum ResourceType end>
   static void GetChildResources(RestApiGetCall& call)
@@ -836,23 +956,7 @@
         b.splice(b.begin(), c);
       }
 
-      switch (type)
-      {
-        case ResourceType_Patient:
-          type = ResourceType_Study;
-          break;
-
-        case ResourceType_Study:
-          type = ResourceType_Series;
-          break;
-
-        case ResourceType_Series:
-          type = ResourceType_Instance;
-          break;
-
-        default:
-          throw OrthancException(ErrorCode_InternalError);
-      }
+      type = GetChildResourceType(type);
 
       a.clear();
       a.splice(a.begin(), b);
@@ -933,13 +1037,7 @@
       }
       
       current = parent;
-      switch (currentType)
-      {
-        case ResourceType_Instance:  currentType = ResourceType_Series; break;
-        case ResourceType_Series:    currentType = ResourceType_Study; break;
-        case ResourceType_Study:     currentType = ResourceType_Patient; break;
-        default:                     throw OrthancException(ErrorCode_InternalError);
-      }
+      currentType = GetParentResourceType(currentType);
     }
 
     assert(currentType == end);
@@ -952,6 +1050,20 @@
   }
 
 
+  static void ExtractPdf(RestApiGetCall& call)
+  {
+    const std::string id = call.GetUriComponent("id", "");
+
+    std::string pdf;
+    ServerContext::DicomCacheLocker locker(OrthancRestApi::GetContext(call), id);
+
+    if (locker.GetDicom().ExtractPdf(pdf))
+    {
+      call.GetOutput().AnswerBuffer(pdf, "application/pdf");
+      return;
+    }
+  }
+
 
   void OrthancRestApi::RegisterResources()
   {
@@ -995,6 +1107,7 @@
     Register("/instances/{id}/frames/{frame}/image-uint16", GetImage<ImageExtractionMode_UInt16>);
     Register("/instances/{id}/frames/{frame}/image-int16", GetImage<ImageExtractionMode_Int16>);
     Register("/instances/{id}/frames/{frame}/matlab", GetMatlabImage);
+    Register("/instances/{id}/pdf", ExtractPdf);
     Register("/instances/{id}/preview", GetImage<ImageExtractionMode_Preview>);
     Register("/instances/{id}/image-uint8", GetImage<ImageExtractionMode_UInt8>);
     Register("/instances/{id}/image-uint16", GetImage<ImageExtractionMode_UInt16>);
@@ -1022,6 +1135,7 @@
     Register("/{resourceType}/{id}/attachments/{name}", UploadAttachment);
 
     Register("/tools/lookup", Lookup);
+    Register("/tools/find", Find);
 
     Register("/patients/{id}/studies", GetChildResources<ResourceType_Patient, ResourceType_Study>);
     Register("/patients/{id}/series", GetChildResources<ResourceType_Patient, ResourceType_Series>);
--- a/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -37,8 +37,7 @@
 #include "../FromDcmtkBridge.h"
 #include "../../Plugins/Engine/PluginsManager.h"
 #include "../../Plugins/Engine/OrthancPlugins.h"
-
-#include <glog/logging.h>
+#include "../ServerContext.h"
 
 
 namespace Orthanc
@@ -54,9 +53,36 @@
   {
     Json::Value result = Json::objectValue;
 
-    result["Version"] = ORTHANC_VERSION;
+    std::string dbVersion = OrthancRestApi::GetIndex(call).GetGlobalProperty(GlobalProperty_DatabaseSchemaVersion, "0");
+
+    result["DatabaseVersion"] = boost::lexical_cast<int>(dbVersion);
+    result["DicomAet"] = Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC");
+    result["DicomPort"] = Configuration::GetGlobalIntegerParameter("DicomPort", 4242);
+    result["HttpPort"] = Configuration::GetGlobalIntegerParameter("HttpPort", 8042);
     result["Name"] = Configuration::GetGlobalStringParameter("Name", "");
-    result["DatabaseVersion"] = boost::lexical_cast<int>(OrthancRestApi::GetIndex(call).GetGlobalProperty(GlobalProperty_DatabaseSchemaVersion, "0"));
+    result["Version"] = ORTHANC_VERSION;
+
+    result["StorageAreaPlugin"] = Json::nullValue;
+    result["DatabaseBackendPlugin"] = Json::nullValue;
+
+#if ORTHANC_PLUGINS_ENABLED == 1
+    result["PluginsEnabled"] = true;
+    const OrthancPlugins& plugins = OrthancRestApi::GetContext(call).GetPlugins();
+
+    if (plugins.HasStorageArea())
+    {
+      std::string p = plugins.GetStorageAreaLibrary().GetPath();
+      result["StorageAreaPlugin"] = boost::filesystem::canonical(p).string();
+    }
+
+    if (plugins.HasDatabaseBackend())
+    {
+      std::string p = plugins.GetDatabaseBackendLibrary().GetPath();
+      result["DatabaseBackendPlugin"] = boost::filesystem::canonical(p).string();     
+    }
+#else
+    result["PluginsEnabled"] = false;
+#endif
 
     call.GetOutput().AnswerJson(result);
   }
@@ -94,9 +120,12 @@
     std::string result;
     ServerContext& context = OrthancRestApi::GetContext(call);
 
+    std::string command;
+    call.BodyToString(command);
+
     {
-      ServerContext::LuaContextLocker locker(context);
-      locker.GetLua().Execute(result, call.GetPostBody());
+      LuaScripting::Locker locker(context.GetLua());
+      locker.GetLua().Execute(result, command);
     }
 
     call.GetOutput().AnswerBuffer(result, "text/plain");
@@ -126,14 +155,16 @@
 
     if (OrthancRestApi::GetContext(call).HasPlugins())
     {
+#if ORTHANC_PLUGINS_ENABLED == 1
       std::list<std::string> plugins;
-      OrthancRestApi::GetContext(call).GetPluginsManager().ListPlugins(plugins);
+      OrthancRestApi::GetContext(call).GetPlugins().GetManager().ListPlugins(plugins);
 
       for (std::list<std::string>::const_iterator 
              it = plugins.begin(); it != plugins.end(); ++it)
       {
         v.append(*it);
       }
+#endif
     }
 
     call.GetOutput().AnswerJson(v);
@@ -147,7 +178,8 @@
       return;
     }
 
-    const PluginsManager& manager = OrthancRestApi::GetContext(call).GetPluginsManager();
+#if ORTHANC_PLUGINS_ENABLED == 1
+    const PluginsManager& manager = OrthancRestApi::GetContext(call).GetPlugins().GetManager();
     std::string id = call.GetUriComponent("id", "");
 
     if (manager.HasPlugin(id))
@@ -156,11 +188,21 @@
       v["ID"] = id;
       v["Version"] = manager.GetPluginVersion(id);
 
-      const OrthancPlugins& plugins = OrthancRestApi::GetContext(call).GetOrthancPlugins();
+      const OrthancPlugins& plugins = OrthancRestApi::GetContext(call).GetPlugins();
       const char *c = plugins.GetProperty(id.c_str(), _OrthancPluginProperty_RootUri);
       if (c != NULL)
       {
-        v["RootUri"] = c;
+        std::string root = c;
+        if (!root.empty())
+        {
+          // Turn the root URI into a URI relative to "/app/explorer.js"
+          if (root[0] == '/')
+          {
+            root = ".." + root;
+          }
+
+          v["RootUri"] = root;
+        }
       }
 
       c = plugins.GetProperty(id.c_str(), _OrthancPluginProperty_Description);
@@ -174,6 +216,7 @@
 
       call.GetOutput().AnswerJson(v);
     }
+#endif
   }
 
 
@@ -183,11 +226,12 @@
 
     if (OrthancRestApi::GetContext(call).HasPlugins())
     {
-      const PluginsManager& manager = OrthancRestApi::GetContext(call).GetPluginsManager();
-      const OrthancPlugins& plugins = OrthancRestApi::GetContext(call).GetOrthancPlugins();
+#if ORTHANC_PLUGINS_ENABLED == 1
+      const OrthancPlugins& plugins = OrthancRestApi::GetContext(call).GetPlugins();
+      const PluginsManager& manager = plugins.GetManager();
 
       std::list<std::string> lst;
-      OrthancRestApi::GetContext(call).GetPluginsManager().ListPlugins(lst);
+      manager.ListPlugins(lst);
 
       for (std::list<std::string>::const_iterator
              it = lst.begin(); it != lst.end(); ++it)
@@ -199,6 +243,7 @@
           s += std::string(tmp) + "\n\n";
         }
       }
+#endif
     }
 
     call.GetOutput().AnswerBuffer(s, "application/javascript");
--- a/OrthancServer/ParsedDicomFile.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/ParsedDicomFile.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -84,15 +84,16 @@
 #include "FromDcmtkBridge.h"
 #include "ToDcmtkBridge.h"
 #include "Internals/DicomImageDecoder.h"
+#include "../Core/Logging.h"
 #include "../Core/Toolbox.h"
 #include "../Core/OrthancException.h"
-#include "../Core/ImageFormats/ImageBuffer.h"
-#include "../Core/ImageFormats/PngWriter.h"
+#include "../Core/Images/ImageBuffer.h"
+#include "../Core/Images/PngWriter.h"
 #include "../Core/Uuid.h"
 #include "../Core/DicomFormat/DicomString.h"
 #include "../Core/DicomFormat/DicomNullValue.h"
 #include "../Core/DicomFormat/DicomIntegerPixelAccessor.h"
-#include "../Core/ImageFormats/PngReader.h"
+#include "../Core/Images/PngReader.h"
 
 #include <list>
 #include <limits>
@@ -106,6 +107,7 @@
 #include <dcmtk/dcmdata/dcistrmb.h>
 #include <dcmtk/dcmdata/dcuid.h>
 #include <dcmtk/dcmdata/dcmetinf.h>
+#include <dcmtk/dcmdata/dcdeftag.h>
 
 #include <dcmtk/dcmdata/dcvrae.h>
 #include <dcmtk/dcmdata/dcvras.h>
@@ -134,7 +136,6 @@
 
 
 #include <boost/math/special_functions/round.hpp>
-#include <glog/logging.h>
 #include <dcmtk/dcmdata/dcostrmb.h>
 
 
@@ -255,46 +256,94 @@
   }
 
 
-  static void AnswerDicomField(RestApiOutput& output,
-                               DcmElement& element,
-                               E_TransferSyntax transferSyntax)
+  namespace
   {
-    // This element is nor a sequence, neither a pixel-data
-    std::string buffer;
-    buffer.resize(65536);
-    Uint32 length = element.getLength(transferSyntax);
-    Uint32 offset = 0;
+    class DicomFieldStream : public IHttpStreamAnswer
+    {
+    private:
+      DcmElement&  element_;
+      uint32_t     length_;
+      uint32_t     offset_;
+      std::string  chunk_;
+      size_t       chunkSize_;
+      
+    public:
+      DicomFieldStream(DcmElement& element,
+                       E_TransferSyntax transferSyntax) :
+        element_(element),
+        length_(element.getLength(transferSyntax)),
+        offset_(0),
+        chunkSize_(0)
+      {
+        static const size_t CHUNK_SIZE = 64 * 1024;  // Use chunks of max 64KB
+        chunk_.resize(CHUNK_SIZE);
+      }
 
-    output.GetLowLevelOutput().SetContentType(CONTENT_TYPE_OCTET_STREAM);
-    output.GetLowLevelOutput().SetContentLength(length);
+      virtual HttpCompression SetupHttpCompression(bool /*gzipAllowed*/,
+                                                   bool /*deflateAllowed*/)
+      {
+        // No support for compression
+        return HttpCompression_None;
+      }
 
-    while (offset < length)
-    {
-      Uint32 nbytes;
-      if (length - offset < buffer.size())
+      virtual bool HasContentFilename(std::string& filename)
       {
-        nbytes = length - offset;
+        return false;
       }
-      else
+
+      virtual std::string GetContentType()
       {
-        nbytes = buffer.size();
+        return "";
       }
 
-      OFCondition cond = element.getPartialValue(&buffer[0], offset, nbytes);
-
-      if (cond.good())
+      virtual uint64_t  GetContentLength()
       {
-        output.GetLowLevelOutput().SendBody(&buffer[0], nbytes);
-        offset += nbytes;
+        return length_;
       }
-      else
+ 
+      virtual bool ReadNextChunk()
       {
-        LOG(ERROR) << "Error while sending a DICOM field: " << cond.text();
-        return;
+        assert(offset_ <= length_);
+
+        if (offset_ == length_)
+        {
+          return false;
+        }
+        else
+        {
+          if (length_ - offset_ < chunk_.size())
+          {
+            chunkSize_ = length_ - offset_;
+          }
+          else
+          {
+            chunkSize_ = chunk_.size();
+          }
+
+          OFCondition cond = element_.getPartialValue(&chunk_[0], offset_, chunkSize_);
+
+          offset_ += chunkSize_;
+
+          if (!cond.good())
+          {
+            LOG(ERROR) << "Error while sending a DICOM field: " << cond.text();
+            throw OrthancException(ErrorCode_InternalError);
+          }
+
+          return true;
+        }
       }
-    }
-
-    output.MarkLowLevelOutputDone();
+ 
+      virtual const char *GetChunkContent()
+      {
+        return chunk_.c_str();
+      }
+ 
+      virtual size_t GetChunkSize()
+      {
+        return chunkSize_;
+      }
+    };
   }
 
 
@@ -365,7 +414,8 @@
         {
           // This is the case for raw, uncompressed image buffers
           assert(*blockUri == "0");
-          AnswerDicomField(output, *element, transferSyntax);
+          DicomFieldStream stream(*element, transferSyntax);
+          output.AnswerStream(stream);
         }
       }
     }
@@ -406,7 +456,8 @@
         //element->getVR() != EVR_UNKNOWN &&  // This would forbid private tags
         element->getVR() != EVR_SQ)
     {
-      AnswerDicomField(output, *element, transferSyntax);
+      DicomFieldStream stream(*element, transferSyntax);
+      output.AnswerStream(stream);
     }
   }
 
@@ -813,7 +864,8 @@
   {
     OFCondition cond;
 
-    if (FromDcmtkBridge::IsPrivateTag(tag))
+    if (FromDcmtkBridge::IsPrivateTag(tag) ||
+        FromDcmtkBridge::IsUnknownTag(tag))
     {
       // This is a private tag
       // http://support.dcmtk.org/redmine/projects/dcmtk/wiki/howto_addprivatedata
@@ -864,7 +916,8 @@
     }
     else
     {
-      if (FromDcmtkBridge::IsPrivateTag(tag))
+      if (FromDcmtkBridge::IsPrivateTag(tag) ||
+          FromDcmtkBridge::IsUnknownTag(tag))
       {
         if (!element->putUint8Array((const Uint8*) value.c_str(), value.size()).good())
         {
@@ -916,7 +969,10 @@
     DcmTagKey k(tag.GetGroup(), tag.GetElement());
     DcmDataset& dataset = *pimpl_->file_->getDataset();
 
-    if (FromDcmtkBridge::IsPrivateTag(tag))
+    if (FromDcmtkBridge::IsPrivateTag(tag) ||
+        FromDcmtkBridge::IsUnknownTag(tag) ||
+        tag == DICOM_TAG_PIXEL_DATA ||
+        tag == DICOM_TAG_ENCAPSULATED_DOCUMENT)
     {
       const Uint8* data = NULL;   // This is freed in the destructor of the dataset
       long unsigned int count = 0;
@@ -950,7 +1006,7 @@
       }
 
       std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(*element, pimpl_->encoding_));
-
+      
       if (v.get() == NULL)
       {
         value = "";
@@ -959,7 +1015,7 @@
       {
         value = v->AsString();
       }
-
+      
       return true;
     }
   }
@@ -1059,8 +1115,10 @@
     pimpl_(new PImpl)
   {
     pimpl_->file_.reset(dynamic_cast<DcmFileFormat*>(other.pimpl_->file_->clone()));
+    pimpl_->encoding_ = other.pimpl_->encoding_;
 
-    pimpl_->encoding_ = other.pimpl_->encoding_;
+    // Create a new instance-level identifier
+    Replace(DICOM_TAG_SOP_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Instance));
   }
 
 
@@ -1082,18 +1140,35 @@
   }
 
 
-  void ParsedDicomFile::EmbedImage(const std::string& dataUriScheme)
+  void ParsedDicomFile::EmbedContent(const std::string& dataUriScheme)
   {
     std::string mime, content;
     Toolbox::DecodeDataUriScheme(mime, content, dataUriScheme);
-
-    std::string decoded;
-    Toolbox::DecodeBase64(decoded, content);
+    Toolbox::ToLowerCase(mime);
 
     if (mime == "image/png")
     {
+      EmbedImage(mime, content);
+    }
+    else if (mime == "application/pdf")
+    {
+      EmbedPdf(content);
+    }
+    else
+    {
+      LOG(ERROR) << "Unsupported MIME type for the content of a new DICOM file";
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+
+  void ParsedDicomFile::EmbedImage(const std::string& mime,
+                                   const std::string& content)
+  {
+    if (mime == "image/png")
+    {
       PngReader reader;
-      reader.ReadFromMemory(decoded);
+      reader.ReadFromMemory(content);
       EmbedImage(reader);
     }
     else
@@ -1276,68 +1351,16 @@
 
   void ParsedDicomFile::SetEncoding(Encoding encoding)
   {
-    std::string s;
-
-    // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/
-    switch (encoding)
+    if (encoding == Encoding_Windows1251)
     {
-      case Encoding_Utf8:
-      case Encoding_Ascii:
-        s = "ISO_IR 192";
-        break;
-
-      case Encoding_Latin1:
-        s = "ISO_IR 100";
-        break;
-
-      case Encoding_Latin2:
-        s = "ISO_IR 101";
-        break;
-
-      case Encoding_Latin3:
-        s = "ISO_IR 109";
-        break;
-
-      case Encoding_Latin4:
-        s = "ISO_IR 110";
-        break;
-
-      case Encoding_Latin5:
-        s = "ISO_IR 148";
-        break;
-
-      case Encoding_Cyrillic:
-        s = "ISO_IR 144";
-        break;
-
-      case Encoding_Arabic:
-        s = "ISO_IR 127";
-        break;
-
-      case Encoding_Greek:
-        s = "ISO_IR 126";
-        break;
-
-      case Encoding_Hebrew:
-        s = "ISO_IR 138";
-        break;
-
-      case Encoding_Japanese:
-        s = "ISO_IR 13";
-        break;
-
-      case Encoding_Chinese:
-        s = "GB18030";
-        break;
-
-      case Encoding_Thai:
-        s = "ISO_IR 166";
-        break;
-
-      default:
-        throw OrthancException(ErrorCode_ParameterOutOfRange);
+      // This Cyrillic codepage is not officially supported by the
+      // DICOM standard. Do not set the SpecificCharacterSet tag.
+      return;
     }
 
+    pimpl_->encoding_ = encoding;
+
+    std::string s = GetDicomSpecificCharacterSet(encoding);
     Replace(DICOM_TAG_SPECIFIC_CHARACTER_SET, s, DicomReplaceMode_InsertIfAbsent);
   }
 
@@ -1354,4 +1377,94 @@
       FromDcmtkBridge::ToJson(target, *pimpl_->file_->getDataset());
     }
   }
+
+
+  bool ParsedDicomFile::HasTag(const DicomTag& tag) const
+  {
+    DcmTag key(tag.GetGroup(), tag.GetElement());
+    return pimpl_->file_->getDataset()->tagExists(key);
+  }
+
+
+  void ParsedDicomFile::EmbedPdf(const std::string& pdf)
+  {
+    if (pdf.size() < 5 ||  // (*)
+        strncmp("%PDF-", pdf.c_str(), 5) != 0)
+    {
+      LOG(ERROR) << "Not a PDF file";
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    Replace(DICOM_TAG_SOP_CLASS_UID, UID_EncapsulatedPDFStorage);
+    Replace(FromDcmtkBridge::Convert(DCM_Modality), "OT");
+    Replace(FromDcmtkBridge::Convert(DCM_ConversionType), "WSD");
+    Replace(FromDcmtkBridge::Convert(DCM_MIMETypeOfEncapsulatedDocument), "application/pdf");
+    //Replace(FromDcmtkBridge::Convert(DCM_SeriesNumber), "1");
+
+    std::auto_ptr<DcmPolymorphOBOW> element(new DcmPolymorphOBOW(DCM_EncapsulatedDocument));
+
+    size_t s = pdf.size();
+    if (s & 1)
+    {
+      // The size of the buffer must be even
+      s += 1;
+    }
+
+    Uint8* bytes = NULL;
+    OFCondition result = element->createUint8Array(s, bytes);
+    if (!result.good() || bytes == NULL)
+    {
+      throw OrthancException(ErrorCode_NotEnoughMemory);
+    }
+
+    // Blank pad byte (no access violation, as "pdf.size() >= 5" because of (*) )
+    bytes[s - 1] = 0;
+
+    memcpy(bytes, pdf.c_str(), pdf.size());
+      
+    DcmPolymorphOBOW* obj = element.release();
+    result = pimpl_->file_->getDataset()->insert(obj);
+
+    if (!result.good())
+    {
+      delete obj;
+      throw OrthancException(ErrorCode_NotEnoughMemory);
+    }
+  }
+
+
+  bool ParsedDicomFile::ExtractPdf(std::string& pdf)
+  {
+    std::string sop, mime;
+    
+    if (!GetTagValue(sop, DICOM_TAG_SOP_CLASS_UID) ||
+        !GetTagValue(mime, FromDcmtkBridge::Convert(DCM_MIMETypeOfEncapsulatedDocument)) ||
+        sop != UID_EncapsulatedPDFStorage ||
+        mime != "application/pdf")
+    {
+      return false;
+    }
+
+    if (!GetTagValue(pdf, DICOM_TAG_ENCAPSULATED_DOCUMENT))
+    {
+      return false;
+    }
+
+    // Strip the possible pad byte at the end of file, because the
+    // encapsulated documents must always have an even length. The PDF
+    // format expects files to end with %%EOF followed by CR/LF. If
+    // the last character of the file is not a CR or LF, we assume it
+    // is a pad byte and remove it.
+    if (pdf.size() > 0)
+    {
+      char last = *pdf.rbegin();
+
+      if (last != 10 && last != 13)
+      {
+        pdf.resize(pdf.size() - 1);
+      }
+    }
+
+    return true;
+  }
 }
--- a/OrthancServer/ParsedDicomFile.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/ParsedDicomFile.h	Wed Sep 30 13:23:31 2015 +0200
@@ -35,8 +35,8 @@
 #include "../Core/DicomFormat/DicomInstanceHasher.h"
 #include "../Core/RestApi/RestApiOutput.h"
 #include "ServerEnumerations.h"
-#include "../Core/ImageFormats/ImageAccessor.h"
-#include "../Core/ImageFormats/ImageBuffer.h"
+#include "../Core/Images/ImageAccessor.h"
+#include "../Core/Images/ImageBuffer.h"
 
 namespace Orthanc
 {
@@ -100,9 +100,12 @@
 
     void SaveToFile(const std::string& path);
 
+    void EmbedContent(const std::string& dataUriScheme);
+
     void EmbedImage(const ImageAccessor& accessor);
 
-    void EmbedImage(const std::string& dataUriScheme);
+    void EmbedImage(const std::string& mime,
+                    const std::string& content);
 
     void ExtractImage(ImageBuffer& result,
                       unsigned int frame);
@@ -121,6 +124,12 @@
 
     void ToJson(Json::Value& target, 
                 bool simplify);
+
+    bool HasTag(const DicomTag& tag) const;
+
+    void EmbedPdf(const std::string& pdf);
+
+    bool ExtractPdf(std::string& pdf);
   };
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/QueryRetrieveHandler.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,133 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "PrecompiledHeadersServer.h"
+#include "QueryRetrieveHandler.h"
+
+#include "OrthancInitialization.h"
+
+
+namespace Orthanc
+{
+  void QueryRetrieveHandler::Invalidate()
+  {
+    done_ = false;
+    answers_.Clear();
+  }
+
+
+  void QueryRetrieveHandler::Run()
+  {
+    if (!done_)
+    {
+      ReusableDicomUserConnection::Locker locker(context_.GetReusableDicomUserConnection(), localAet_, modality_);
+      locker.GetConnection().Find(answers_, level_, query_);
+      done_ = true;
+    }
+  }
+
+
+  QueryRetrieveHandler::QueryRetrieveHandler(ServerContext& context) : 
+    context_(context),
+    localAet_(context.GetDefaultLocalApplicationEntityTitle()),
+    done_(false),
+    level_(ResourceType_Study)
+  {
+  }
+
+
+  void QueryRetrieveHandler::SetModality(const std::string& symbolicName)
+  {
+    Invalidate();
+    modalityName_ = symbolicName;
+    Configuration::GetDicomModalityUsingSymbolicName(modality_, symbolicName);
+  }
+
+
+  void QueryRetrieveHandler::SetLevel(ResourceType level)
+  {
+    Invalidate();
+    level_ = level;
+  }
+
+
+  void QueryRetrieveHandler::SetQuery(const DicomTag& tag,
+                                      const std::string& value)
+  {
+    Invalidate();
+    query_.SetValue(tag, value);
+  }
+
+
+  size_t QueryRetrieveHandler::GetAnswerCount()
+  {
+    Run();
+    return answers_.GetSize();
+  }
+
+
+  const DicomMap& QueryRetrieveHandler::GetAnswer(size_t i)
+  {
+    Run();
+
+    if (i >= answers_.GetSize())
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    return answers_.GetAnswer(i);
+  }
+
+
+  void QueryRetrieveHandler::Retrieve(const std::string& target,
+                                      size_t i)
+  {
+    Run();
+
+    if (i >= answers_.GetSize())
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    ReusableDicomUserConnection::Locker locker(context_.GetReusableDicomUserConnection(), localAet_, modality_);
+    locker.GetConnection().Move(target, answers_.GetAnswer(i));
+  }
+
+
+  void QueryRetrieveHandler::Retrieve(const std::string& target)
+  {
+    for (size_t i = 0; i < GetAnswerCount(); i++)
+    {
+      Retrieve(target, i);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/QueryRetrieveHandler.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,95 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ServerContext.h"
+
+namespace Orthanc
+{
+  class QueryRetrieveHandler : public IDynamicObject
+  {
+  private:
+    ServerContext&             context_;
+    const std::string&         localAet_;
+    bool                       done_;
+    RemoteModalityParameters   modality_;
+    ResourceType               level_;
+    DicomMap                   query_;
+    DicomFindAnswers           answers_;
+    std::string                modalityName_;
+
+    void Invalidate();
+
+
+  public:
+    QueryRetrieveHandler(ServerContext& context);
+
+    void SetModality(const std::string& symbolicName);
+
+    const RemoteModalityParameters& GetModality() const
+    {
+      return modality_;
+    }
+
+    const std::string& GetModalitySymbolicName() const
+    {
+      return modalityName_;
+    }
+
+    void SetLevel(ResourceType level);
+
+    ResourceType GetLevel() const
+    {
+      return level_;
+    }
+
+    void SetQuery(const DicomTag& tag,
+                  const std::string& value);
+
+    const DicomMap& GetQuery() const
+    {
+      return query_;
+    }
+
+    void Run();
+
+    size_t GetAnswerCount();
+
+    const DicomMap& GetAnswer(size_t i);
+
+    void Retrieve(const std::string& target,
+                  size_t i);
+
+    void Retrieve(const std::string& target);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/ResourceFinder.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,383 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "PrecompiledHeadersServer.h"
+#include "ResourceFinder.h"
+
+#include "../Core/Logging.h"
+#include "FromDcmtkBridge.h"
+#include "ServerContext.h"
+
+#include <boost/algorithm/string/predicate.hpp>
+
+namespace Orthanc
+{
+  class ResourceFinder::CandidateResources
+  {
+  private:
+    typedef std::map<DicomTag, std::string>  Query;
+
+    ResourceFinder&        finder_;
+    ServerIndex&           index_;
+    ResourceType           level_;
+    bool                   isFilterApplied_;
+    std::set<std::string>  filtered_;
+
+     
+    static void ListToSet(std::set<std::string>& target,
+                          const std::list<std::string>& source)
+    {
+      for (std::list<std::string>::const_iterator
+             it = source.begin(); it != source.end(); ++it)
+      {
+        target.insert(*it);
+      }
+    }
+
+
+  public:
+    CandidateResources(ResourceFinder& finder) : 
+      finder_(finder),
+      index_(finder.context_.GetIndex()),
+      level_(ResourceType_Patient), 
+      isFilterApplied_(false)
+    {
+    }
+
+    ResourceType GetLevel() const
+    {
+      return level_;
+    }
+
+    void GoDown()
+    {
+      assert(level_ != ResourceType_Instance);
+
+      if (isFilterApplied_)
+      {
+        std::set<std::string> tmp = filtered_;
+
+        filtered_.clear();
+
+        for (std::set<std::string>::const_iterator 
+               it = tmp.begin(); it != tmp.end(); ++it)
+        {
+          std::list<std::string> children;
+          try
+          {
+            index_.GetChildren(children, *it);
+            ListToSet(filtered_, children);
+          }
+          catch (OrthancException&)
+          {
+            // The resource was removed in the meantime
+          }
+        }
+      }
+
+      switch (level_)
+      {
+        case ResourceType_Patient:
+          level_ = ResourceType_Study;
+          break;
+
+        case ResourceType_Study:
+          level_ = ResourceType_Series;
+          break;
+
+        case ResourceType_Series:
+          level_ = ResourceType_Instance;
+          break;
+
+        default:
+          throw OrthancException(ErrorCode_InternalError);
+      }
+    }
+
+
+    void Flatten(std::list<std::string>& resources) const
+    {
+      resources.clear();
+
+      if (isFilterApplied_)
+      {
+        for (std::set<std::string>::const_iterator 
+               it = filtered_.begin(); it != filtered_.end(); ++it)
+        {
+          resources.push_back(*it);
+        }
+      }
+      else
+      {
+        index_.GetAllUuids(resources, level_);
+      }
+    }
+
+    
+    void RestrictIdentifier(const IQuery& query,
+                            const DicomTag& tag)
+    {
+      assert((level_ == ResourceType_Patient && tag == DICOM_TAG_PATIENT_ID) ||
+             (level_ == ResourceType_Study && tag == DICOM_TAG_STUDY_INSTANCE_UID) ||
+             (level_ == ResourceType_Study && tag == DICOM_TAG_ACCESSION_NUMBER) ||
+             (level_ == ResourceType_Series && tag == DICOM_TAG_SERIES_INSTANCE_UID) ||
+             (level_ == ResourceType_Instance && tag == DICOM_TAG_SOP_INSTANCE_UID));
+
+      std::string value;
+      if (!query.RestrictIdentifier(value, tag))
+      {
+        return;
+      }
+
+      LOG(INFO) << "Lookup for identifier tag "
+                << FromDcmtkBridge::GetName(tag) << " (value: " << value << ")";
+
+      std::list<std::string> resources;
+      index_.LookupIdentifier(resources, tag, value, level_);
+
+      if (isFilterApplied_)
+      {
+        std::set<std::string>  s;
+        ListToSet(s, resources);
+
+        std::set<std::string> tmp = filtered_;
+        filtered_.clear();
+
+        for (std::set<std::string>::const_iterator 
+               it = tmp.begin(); it != tmp.end(); ++it)
+        {
+          if (s.find(*it) != s.end())
+          {
+            filtered_.insert(*it);
+          }
+        }
+      }
+      else
+      {
+        assert(filtered_.empty());
+        isFilterApplied_ = true;
+        ListToSet(filtered_, resources);
+      }
+    }
+
+
+    void RestrictMainDicomTags(const IQuery& query)
+    {
+      if (!query.HasMainDicomTagsFilter(level_))
+      {
+        return;
+      }
+
+      std::list<std::string> resources;
+      Flatten(resources);
+
+      isFilterApplied_ = true;
+      filtered_.clear();
+
+      for (std::list<std::string>::const_iterator
+             it = resources.begin(); it != resources.end(); ++it)
+      {
+        DicomMap mainTags;
+        if (index_.GetMainDicomTags(mainTags, *it, level_))
+        {
+          if (query.FilterMainDicomTags(*it, level_, mainTags))
+          {
+            filtered_.insert(*it);
+          }
+        }
+      }
+    }
+  };
+
+
+  ResourceFinder::ResourceFinder(ServerContext& context) : 
+    context_(context),
+    maxResults_(0)
+  {
+  }
+
+
+  void ResourceFinder::ApplyAtLevel(CandidateResources& candidates,
+                                    const IQuery& query,
+                                    ResourceType level)
+  {
+    if (level != ResourceType_Patient)
+    {
+      candidates.GoDown();
+    }
+
+    switch (level)
+    {
+      case ResourceType_Patient:
+      {
+        candidates.RestrictIdentifier(query, DICOM_TAG_PATIENT_ID);
+        break;
+      }
+
+      case ResourceType_Study:
+      {
+        candidates.RestrictIdentifier(query, DICOM_TAG_STUDY_INSTANCE_UID);
+        candidates.RestrictIdentifier(query, DICOM_TAG_ACCESSION_NUMBER);
+        break;
+      }
+
+      case ResourceType_Series:
+      {
+        candidates.RestrictIdentifier(query, DICOM_TAG_SERIES_INSTANCE_UID);
+        break;
+      }
+
+      case ResourceType_Instance:
+      {
+        candidates.RestrictIdentifier(query, DICOM_TAG_SOP_INSTANCE_UID);
+        break;
+      }
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+    }
+
+    candidates.RestrictMainDicomTags(query);
+  }
+
+
+
+  static bool LookupOneInstance(std::string& result,
+                                ServerIndex& index,
+                                const std::string& id,
+                                ResourceType type)
+  {
+    if (type == ResourceType_Instance)
+    {
+      result = id;
+      return true;
+    }
+
+    std::string childId;
+    
+    {
+      std::list<std::string> children;
+      index.GetChildInstances(children, id);
+
+      if (children.empty())
+      {
+        return false;
+      }
+
+      childId = children.front();
+    }
+
+    return LookupOneInstance(result, index, childId, GetChildResourceType(type));
+  }
+
+
+  bool ResourceFinder::Apply(std::list<std::string>& result,
+                             const IQuery& query)
+  {
+    CandidateResources candidates(*this);
+
+    ApplyAtLevel(candidates, query, ResourceType_Patient);
+
+    const ResourceType level = query.GetLevel();
+
+    if (level == ResourceType_Study ||
+        level == ResourceType_Series ||
+        level == ResourceType_Instance)
+    {
+      ApplyAtLevel(candidates, query, ResourceType_Study);
+    }
+        
+    if (level == ResourceType_Series ||
+        level == ResourceType_Instance)
+    {
+      ApplyAtLevel(candidates, query, ResourceType_Series);
+    }
+        
+    if (level == ResourceType_Instance)
+    {
+      ApplyAtLevel(candidates, query, ResourceType_Instance);
+    }
+
+    if (!query.HasInstanceFilter())
+    {
+      candidates.Flatten(result);
+
+      if (maxResults_ != 0 &&
+          result.size() >= maxResults_)
+      {
+        result.resize(maxResults_);
+        return false;
+      }
+      else
+      {
+        return true;
+      }
+    }
+    else
+    {
+      std::list<std::string> tmp;
+      candidates.Flatten(tmp);
+      
+      result.clear();
+      for (std::list<std::string>::const_iterator 
+             resource = tmp.begin(); resource != tmp.end(); ++resource)
+      {
+        try
+        {
+          std::string instance;
+          if (LookupOneInstance(instance, context_.GetIndex(), *resource, level))
+          {
+            Json::Value content;
+            context_.ReadJson(content, instance);
+            if (query.FilterInstance(*resource, content))
+            {
+              result.push_back(*resource);
+
+              if (maxResults_ != 0 &&
+                  result.size() >= maxResults_)
+              {
+                // Too many results, stop before recording this new match
+                return false;
+              }
+            }
+          }
+        }
+        catch (OrthancException&)
+        {
+          // This resource has been deleted since the search was started
+        }
+      }      
+    }
+
+    return true;  // All the matching resources have been returned
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/ResourceFinder.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,101 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ServerIndex.h"
+
+#include <boost/noncopyable.hpp>
+
+namespace Orthanc
+{
+  class ResourceFinder : public boost::noncopyable
+  {
+  public:
+    class IQuery : public boost::noncopyable
+    {
+    public:
+      virtual ~IQuery()
+      {
+      }
+
+      virtual ResourceType GetLevel() const = 0;
+
+      virtual bool RestrictIdentifier(std::string& value,
+                                      DicomTag identifier) const = 0;
+
+      virtual bool HasMainDicomTagsFilter(ResourceType level) const = 0;
+
+      virtual bool FilterMainDicomTags(const std::string& resourceId,
+                                       ResourceType level,
+                                       const DicomMap& mainTags) const = 0;
+
+      virtual bool HasInstanceFilter() const = 0;
+
+      virtual bool FilterInstance(const std::string& instanceId,
+                                  const Json::Value& content) const = 0;
+    };
+
+
+  private:
+    typedef std::map<DicomTag, std::string>  Identifiers;
+
+    class CandidateResources;
+
+    ServerContext&    context_;
+    size_t            maxResults_;
+
+    void ApplyAtLevel(CandidateResources& candidates,
+                      const IQuery& query,
+                      ResourceType level);
+
+  public:
+    ResourceFinder(ServerContext& context);
+
+    void SetMaxResults(size_t value)
+    {
+      maxResults_ = value;
+    }
+
+    size_t GetMaxResults() const
+    {
+      return maxResults_;
+    }
+
+    // Returns "true" iff. all the matching resources have been
+    // returned. Will be "false" if the results were truncated by
+    // "SetMaxResults()".
+    bool Apply(std::list<std::string>& result,
+               const IQuery& query);
+  };
+
+}
--- a/OrthancServer/Scheduler/CallSystemCommand.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Scheduler/CallSystemCommand.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -30,9 +30,10 @@
  **/
 
 
+#include "../PrecompiledHeadersServer.h"
 #include "CallSystemCommand.h"
 
-#include <glog/logging.h>
+#include "../../Core/Logging.h"
 #include "../../Core/Toolbox.h"
 #include "../../Core/Uuid.h"
 
--- a/OrthancServer/Scheduler/DeleteInstanceCommand.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Scheduler/DeleteInstanceCommand.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -30,9 +30,10 @@
  **/
 
 
+#include "../PrecompiledHeadersServer.h"
 #include "DeleteInstanceCommand.h"
 
-#include <glog/logging.h>
+#include "../../Core/Logging.h"
 
 namespace Orthanc
 {
--- a/OrthancServer/Scheduler/ModifyInstanceCommand.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Scheduler/ModifyInstanceCommand.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -30,12 +30,47 @@
  **/
 
 
+#include "../PrecompiledHeadersServer.h"
 #include "ModifyInstanceCommand.h"
 
-#include <glog/logging.h>
+#include "../../Core/Logging.h"
 
 namespace Orthanc
 {
+  ModifyInstanceCommand::ModifyInstanceCommand(ServerContext& context,
+                                               RequestOrigin origin,
+                                               const DicomModification& modification) :
+    context_(context),
+    origin_(origin),
+    modification_(modification)
+  {
+    modification_.SetAllowManualIdentifiers(true);
+
+    if (modification_.IsReplaced(DICOM_TAG_PATIENT_ID))
+    {
+      modification_.SetLevel(ResourceType_Patient);
+    }
+    else if (modification_.IsReplaced(DICOM_TAG_STUDY_INSTANCE_UID))
+    {
+      modification_.SetLevel(ResourceType_Study);
+    }
+    else if (modification_.IsReplaced(DICOM_TAG_SERIES_INSTANCE_UID))
+    {
+      modification_.SetLevel(ResourceType_Series);
+    }
+    else
+    {
+      modification_.SetLevel(ResourceType_Instance);
+    }
+
+    if (origin_ != RequestOrigin_Lua)
+    {
+      // TODO If issued from HTTP, "remoteIp" and "username" must be provided
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+
   bool ModifyInstanceCommand::Apply(ListOfStrings& outputs,
                                     const ListOfStrings& inputs)
   {
@@ -56,6 +91,8 @@
         modification_.Apply(*modified);
 
         DicomInstanceToStore toStore;
+        assert(origin_ == RequestOrigin_Lua);
+        toStore.SetLuaOrigin();
         toStore.SetParsedDicomFile(*modified);
         // TODO other metadata
         toStore.AddMetadata(ResourceType_Instance, MetadataType_ModifiedFrom, *it);
--- a/OrthancServer/Scheduler/ModifyInstanceCommand.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Scheduler/ModifyInstanceCommand.h	Wed Sep 30 13:23:31 2015 +0200
@@ -42,18 +42,13 @@
   {
   private:
     ServerContext& context_;
+    RequestOrigin origin_;
     DicomModification modification_;
 
   public:
-    ModifyInstanceCommand(ServerContext& context) :
-      context_(context)
-    {
-    }
-
-    DicomModification& GetModification()
-    {
-      return modification_;
-    }
+    ModifyInstanceCommand(ServerContext& context,
+                          RequestOrigin origin,
+                          const DicomModification& modification);
 
     const DicomModification& GetModification() const
     {
--- a/OrthancServer/Scheduler/ServerCommandInstance.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Scheduler/ServerCommandInstance.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -30,6 +30,7 @@
  **/
 
 
+#include "../PrecompiledHeadersServer.h"
 #include "ServerCommandInstance.h"
 
 #include "../../Core/OrthancException.h"
--- a/OrthancServer/Scheduler/ServerJob.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Scheduler/ServerJob.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -30,6 +30,7 @@
  **/
 
 
+#include "../PrecompiledHeadersServer.h"
 #include "ServerJob.h"
 
 #include "../../Core/OrthancException.h"
@@ -61,7 +62,7 @@
             index[*next] <= index[*it])
         {
           // You must reorder your calls to "ServerJob::AddCommand"
-          throw OrthancException("Bad ordering of filters in a job");
+          throw OrthancException(ErrorCode_BadJobOrdering);
         }
       }
     }
--- a/OrthancServer/Scheduler/ServerScheduler.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Scheduler/ServerScheduler.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -30,11 +30,11 @@
  **/
 
 
+#include "../PrecompiledHeadersServer.h"
 #include "ServerScheduler.h"
 
 #include "../../Core/OrthancException.h"
-
-#include <glog/logging.h>
+#include "../../Core/Logging.h"
 
 namespace Orthanc
 {
@@ -199,8 +199,25 @@
 
   ServerScheduler::~ServerScheduler()
   {
-    finish_ = true;
-    worker_.join();
+    if (!finish_)
+    {
+      LOG(ERROR) << "INTERNAL ERROR: ServerScheduler::Finalize() should be invoked manually to avoid mess in the destruction order!";
+      Stop();
+    }
+  }
+
+
+  void ServerScheduler::Stop()
+  {
+    if (!finish_)
+    {
+      finish_ = true;
+
+      if (worker_.joinable())
+      {
+        worker_.join();
+      }
+    }
   }
 
 
@@ -313,7 +330,7 @@
 
     if (job->second.size_ == 1)
     {
-      return job->second.success_;
+      return static_cast<float>(job->second.success_);
     }
 
     return (static_cast<float>(job->second.success_) / 
--- a/OrthancServer/Scheduler/ServerScheduler.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Scheduler/ServerScheduler.h	Wed Sep 30 13:23:31 2015 +0200
@@ -86,6 +86,8 @@
 
     ~ServerScheduler();
 
+    void Stop();
+
     void Submit(ServerJob& job);
 
     bool SubmitAndWait(ListOfStrings& outputs,
--- a/OrthancServer/Scheduler/StorePeerCommand.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Scheduler/StorePeerCommand.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -30,12 +30,12 @@
  **/
 
 
+#include "../PrecompiledHeadersServer.h"
 #include "StorePeerCommand.h"
 
+#include "../../Core/Logging.h"
 #include "../../Core/HttpClient.h"
 
-#include <glog/logging.h>
-
 namespace Orthanc
 {
   StorePeerCommand::StorePeerCommand(ServerContext& context,
@@ -71,7 +71,7 @@
 
       try
       {
-        context_.ReadFile(client.AccessPostData(), *it, FileContentType_Dicom);
+        context_.ReadFile(client.GetBody(), *it, FileContentType_Dicom);
 
         std::string answer;
         if (!client.Apply(answer))
--- a/OrthancServer/Scheduler/StoreScuCommand.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Scheduler/StoreScuCommand.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -30,25 +30,28 @@
  **/
 
 
+#include "../PrecompiledHeadersServer.h"
 #include "StoreScuCommand.h"
 
-#include <glog/logging.h>
+#include "../../Core/Logging.h"
 
 namespace Orthanc
 {
   StoreScuCommand::StoreScuCommand(ServerContext& context,
+                                   const std::string& localAet,
                                    const RemoteModalityParameters& modality,
                                    bool ignoreExceptions) : 
     context_(context),
     modality_(modality),
-    ignoreExceptions_(ignoreExceptions)
+    ignoreExceptions_(ignoreExceptions),
+    localAet_(localAet)
   {
   }
 
   bool StoreScuCommand::Apply(ListOfStrings& outputs,
                              const ListOfStrings& inputs)
   {
-    ReusableDicomUserConnection::Locker locker(context_.GetReusableDicomUserConnection(), modality_);
+    ReusableDicomUserConnection::Locker locker(context_.GetReusableDicomUserConnection(), localAet_, modality_);
 
     for (ListOfStrings::const_iterator
            it = inputs.begin(); it != inputs.end(); ++it)
--- a/OrthancServer/Scheduler/StoreScuCommand.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/Scheduler/StoreScuCommand.h	Wed Sep 30 13:23:31 2015 +0200
@@ -43,9 +43,11 @@
     ServerContext& context_;
     RemoteModalityParameters modality_;
     bool ignoreExceptions_;
+    std::string localAet_;
 
   public:
     StoreScuCommand(ServerContext& context,
+                    const std::string& localAet,
                     const RemoteModalityParameters& modality,
                     bool ignoreExceptions);
 
--- a/OrthancServer/ServerContext.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/ServerContext.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,13 +33,14 @@
 #include "PrecompiledHeadersServer.h"
 #include "ServerContext.h"
 
+#include "../Core/FileStorage/StorageAccessor.h"
 #include "../Core/HttpServer/FilesystemHttpSender.h"
-#include "../Core/Lua/LuaFunctionCall.h"
+#include "../Core/HttpServer/HttpStreamTranscoder.h"
+#include "../Core/Logging.h"
 #include "FromDcmtkBridge.h"
 #include "ServerToolbox.h"
 #include "OrthancInitialization.h"
 
-#include <glog/logging.h>
 #include <EmbeddedResources.h>
 #include <dcmtk/dcmdata/dcfilefo.h>
 
@@ -55,9 +56,6 @@
 
 #define ENABLE_DICOM_CACHE  1
 
-static const char* RECEIVED_INSTANCE_FILTER = "ReceivedInstanceFilter";
-static const char* ON_STORED_INSTANCE = "OnStoredInstance";
-
 static const size_t DICOM_CACHE_SIZE = 2;
 
 /**
@@ -71,24 +69,97 @@
 
 namespace Orthanc
 {
-  ServerContext::ServerContext(IDatabaseWrapper& database) :
+  void ServerContext::ChangeThread(ServerContext* that)
+  {
+    while (!that->done_)
+    {
+      std::auto_ptr<IDynamicObject> obj(that->pendingChanges_.Dequeue(100));
+        
+      if (obj.get() != NULL)
+      {
+        const ServerIndexChange& change = dynamic_cast<const ServerIndexChange&>(*obj.get());
+
+        boost::recursive_mutex::scoped_lock lock(that->listenersMutex_);
+        for (ServerListeners::iterator it = that->listeners_.begin(); 
+             it != that->listeners_.end(); ++it)
+        {
+          try
+          {
+            it->GetListener().SignalChange(change);
+          }
+          catch (OrthancException& e)
+          {
+            LOG(ERROR) << "Error in the " << it->GetDescription() 
+                       << " callback while signaling a change: " << e.What();
+          }
+        }
+      }
+    }
+  }
+
+
+  ServerContext::ServerContext(IDatabaseWrapper& database,
+                               IStorageArea& area) :
     index_(*this, database),
+    area_(area),
     compressionEnabled_(false),
+    storeMD5_(true),
     provider_(*this),
     dicomCache_(provider_, DICOM_CACHE_SIZE),
     scheduler_(Configuration::GetGlobalIntegerParameter("LimitJobs", 10)),
+    lua_(*this),
+#if ORTHANC_PLUGINS_ENABLED == 1
     plugins_(NULL),
-    pluginsManager_(NULL)
+#endif
+    done_(false),
+    queryRetrieveArchive_(Configuration::GetGlobalIntegerParameter("QueryRetrieveSize", 10)),
+    defaultLocalAet_(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC"))
   {
-    scu_.SetLocalApplicationEntityTitle(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC"));
-
     uint64_t s = Configuration::GetGlobalIntegerParameter("DicomAssociationCloseDelay", 5);  // In seconds
     scu_.SetMillisecondsBeforeClose(s * 1000);  // Milliseconds are expected here
 
-    lua_.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX);
-    lua_.SetHttpProxy(Configuration::GetGlobalStringParameter("HttpProxy", ""));
+    listeners_.push_back(ServerListener(lua_, "Lua"));
+
+    changeThread_ = boost::thread(ChangeThread, this);
+  }
+
+
+  
+  ServerContext::~ServerContext()
+  {
+    if (!done_)
+    {
+      LOG(ERROR) << "INTERNAL ERROR: ServerContext::Stop() should be invoked manually to avoid mess in the destruction order!";
+      Stop();
+    }
   }
 
+
+  void ServerContext::Stop()
+  {
+    if (!done_)
+    {
+      {
+        boost::recursive_mutex::scoped_lock lock(listenersMutex_);
+        listeners_.clear();
+      }
+
+      done_ = true;
+
+      if (changeThread_.joinable())
+      {
+        changeThread_.join();
+      }
+
+      scu_.Finalize();
+
+      // Do not change the order below!
+      scheduler_.Stop();
+      index_.Stop();
+    }
+  }
+
+
   void ServerContext::SetCompressionEnabled(bool enabled)
   {
     if (enabled)
@@ -99,182 +170,11 @@
     compressionEnabled_ = enabled;
   }
 
+
   void ServerContext::RemoveFile(const std::string& fileUuid,
                                  FileContentType type)
   {
-    accessor_.Remove(fileUuid, type);
-  }
-
-
-  bool ServerContext::ApplyReceivedInstanceFilter(const Json::Value& simplified,
-                                                  const std::string& remoteAet)
-  {
-    LuaContextLocker locker(*this);
-
-    if (locker.GetLua().IsExistingFunction(RECEIVED_INSTANCE_FILTER))
-    {
-      LuaFunctionCall call(locker.GetLua(), RECEIVED_INSTANCE_FILTER);
-      call.PushJson(simplified);
-      call.PushString(remoteAet);
-
-      if (!call.ExecutePredicate())
-      {
-        return false;
-      }
-    }
-
-    return true;
-  }
-
-
-  static IServerCommand* ParseOperation(ServerContext& context,
-                                        const std::string& operation,
-                                        const Json::Value& parameters)
-  {
-    if (operation == "delete")
-    {
-      LOG(INFO) << "Lua script to delete instance " << parameters["Instance"].asString();
-      return new DeleteInstanceCommand(context);
-    }
-
-    if (operation == "store-scu")
-    {
-      std::string modality = parameters["Modality"].asString();
-      LOG(INFO) << "Lua script to send instance " << parameters["Instance"].asString()
-                << " to modality " << modality << " using Store-SCU";
-      return new StoreScuCommand(context, Configuration::GetModalityUsingSymbolicName(modality), true);
-    }
-
-    if (operation == "store-peer")
-    {
-      std::string peer = parameters["Peer"].asString();
-      LOG(INFO) << "Lua script to send instance " << parameters["Instance"].asString()
-                << " to peer " << peer << " using HTTP";
-
-      OrthancPeerParameters parameters;
-      Configuration::GetOrthancPeer(parameters, peer);
-      return new StorePeerCommand(context, parameters, true);
-    }
-
-    if (operation == "modify")
-    {
-      LOG(INFO) << "Lua script to modify instance " << parameters["Instance"].asString();
-      std::auto_ptr<ModifyInstanceCommand> command(new ModifyInstanceCommand(context));
-      OrthancRestApi::ParseModifyRequest(command->GetModification(), parameters);
-      return command.release();
-    }
-
-    if (operation == "call-system")
-    {
-      LOG(INFO) << "Lua script to call system command on " << parameters["Instance"].asString();
-
-      const Json::Value& argsIn = parameters["Arguments"];
-      if (argsIn.type() != Json::arrayValue)
-      {
-        throw OrthancException(ErrorCode_BadParameterType);
-      }
-
-      std::vector<std::string> args;
-      args.reserve(argsIn.size());
-      for (Json::Value::ArrayIndex i = 0; i < argsIn.size(); ++i)
-      {
-        // http://jsoncpp.sourceforge.net/namespace_json.html#7d654b75c16a57007925868e38212b4e
-        switch (argsIn[i].type())
-        {
-          case Json::stringValue:
-            args.push_back(argsIn[i].asString());
-            break;
-
-          case Json::intValue:
-            args.push_back(boost::lexical_cast<std::string>(argsIn[i].asInt()));
-            break;
-
-          case Json::uintValue:
-            args.push_back(boost::lexical_cast<std::string>(argsIn[i].asUInt()));
-            break;
-
-          case Json::realValue:
-            args.push_back(boost::lexical_cast<std::string>(argsIn[i].asFloat()));
-            break;
-
-          default:
-            throw OrthancException(ErrorCode_BadParameterType);
-        }
-      }
-
-      return new CallSystemCommand(context, parameters["Command"].asString(), args);
-    }
-
-    throw OrthancException(ErrorCode_ParameterOutOfRange);
-  }
-
-
-  void ServerContext::ApplyLuaOnStoredInstance(const std::string& instanceId,
-                                               const Json::Value& simplifiedDicom,
-                                               const Json::Value& metadata,
-                                               const std::string& remoteAet,
-                                               const std::string& calledAet)
-  {
-    LuaContextLocker locker(*this);
-
-    if (locker.GetLua().IsExistingFunction(ON_STORED_INSTANCE))
-    {
-      locker.GetLua().Execute("_InitializeJob()");
-
-      LuaFunctionCall call(locker.GetLua(), ON_STORED_INSTANCE);
-      call.PushString(instanceId);
-      call.PushJson(simplifiedDicom);
-      call.PushJson(metadata);
-      call.PushJson(remoteAet);
-      call.PushJson(calledAet);
-      call.Execute();
-
-      Json::Value operations;
-      LuaFunctionCall call2(locker.GetLua(), "_AccessJob");
-      call2.ExecuteToJson(operations);
-     
-      if (operations.type() != Json::arrayValue)
-      {
-        throw OrthancException(ErrorCode_InternalError);
-      }
-
-      ServerJob job;
-      ServerCommandInstance* previousCommand = NULL;
-
-      for (Json::Value::ArrayIndex i = 0; i < operations.size(); ++i)
-      {
-        if (operations[i].type() != Json::objectValue ||
-            !operations[i].isMember("Operation"))
-        {
-          throw OrthancException(ErrorCode_InternalError);
-        }
-
-        const Json::Value& parameters = operations[i];
-        std::string operation = parameters["Operation"].asString();
-
-        ServerCommandInstance& command = job.AddCommand(ParseOperation(*this, operation, operations[i]));
-        
-        if (!parameters.isMember("Instance"))
-        {
-          throw OrthancException(ErrorCode_InternalError);
-        }
-
-        std::string instance = parameters["Instance"].asString();
-        if (instance.empty())
-        {
-          previousCommand->ConnectOutput(command);
-        }
-        else 
-        {
-          command.AddInput(instance);
-        }
-
-        previousCommand = &command;
-      }
-
-      job.SetDescription(std::string("Lua script: ") + ON_STORED_INSTANCE);
-      scheduler_.Submit(job);
-    }
+    area_.Remove(fileUuid, type);
   }
 
 
@@ -283,30 +183,52 @@
   {
     try
     {
+      StorageAccessor accessor(area_);
+
       DicomInstanceHasher hasher(dicom.GetSummary());
       resultPublicId = hasher.HashInstance();
 
-      Json::Value simplified;
-      SimplifyTags(simplified, dicom.GetJson());
+      Json::Value simplifiedTags;
+      SimplifyTags(simplifiedTags, dicom.GetJson());
 
       // Test if the instance must be filtered out
-      if (!ApplyReceivedInstanceFilter(simplified, dicom.GetRemoteAet()))
+      bool accepted = true;
+
+      {
+        boost::recursive_mutex::scoped_lock lock(listenersMutex_);
+
+        for (ServerListeners::iterator it = listeners_.begin(); it != listeners_.end(); ++it)
+        {
+          try
+          {
+            if (!it->GetListener().FilterIncomingInstance(dicom, simplifiedTags))
+            {
+              accepted = false;
+              break;
+            }
+          }
+          catch (OrthancException& e)
+          {
+            LOG(ERROR) << "Error in the " << it->GetDescription() 
+                       << " callback while receiving an instance: " << e.What();
+            throw;
+          }
+        }
+      }
+
+      if (!accepted)
       {
         LOG(INFO) << "An incoming instance has been discarded by the filter";
         return StoreStatus_FilteredOut;
       }
 
-      if (compressionEnabled_)
-      {
-        accessor_.SetCompressionForNextOperations(CompressionType_Zlib);
-      }
-      else
-      {
-        accessor_.SetCompressionForNextOperations(CompressionType_None);
-      }      
+      // TODO Should we use "gzip" instead?
+      CompressionType compression = (compressionEnabled_ ? CompressionType_ZlibWithSize : CompressionType_None);
 
-      FileInfo dicomInfo = accessor_.Write(dicom.GetBufferData(), dicom.GetBufferSize(), FileContentType_Dicom);
-      FileInfo jsonInfo = accessor_.Write(dicom.GetJson().toStyledString(), FileContentType_DicomAsJson);
+      FileInfo dicomInfo = accessor.Write(dicom.GetBufferData(), dicom.GetBufferSize(), 
+                                          FileContentType_Dicom, compression, storeMD5_);
+      FileInfo jsonInfo = accessor.Write(dicom.GetJson().toStyledString(), 
+                                         FileContentType_DicomAsJson, compression, storeMD5_);
 
       ServerIndex::Attachments attachments;
       attachments.push_back(dicomInfo);
@@ -317,6 +239,7 @@
       StoreStatus status = index_.Store(instanceMetadata, dicom.GetSummary(), attachments, 
                                         dicom.GetRemoteAet(), dicom.GetMetadata());
 
+      // Only keep the metadata for the "instance" level
       dicom.GetMetadata().clear();
 
       for (InstanceMetadata::const_iterator it = instanceMetadata.begin();
@@ -328,8 +251,8 @@
             
       if (status != StoreStatus_Success)
       {
-        accessor_.Remove(dicomInfo.GetUuid(), FileContentType_Dicom);
-        accessor_.Remove(jsonInfo.GetUuid(), FileContentType_DicomAsJson);
+        accessor.Remove(dicomInfo);
+        accessor.Remove(jsonInfo);
       }
 
       switch (status)
@@ -354,33 +277,18 @@
       if (status == StoreStatus_Success ||
           status == StoreStatus_AlreadyStored)
       {
-        Json::Value metadata = Json::objectValue;
-        for (std::map<MetadataType, std::string>::const_iterator 
-               it = instanceMetadata.begin(); 
-             it != instanceMetadata.end(); ++it)
-        {
-          metadata[EnumerationToString(it->first)] = it->second;
-        }
+        boost::recursive_mutex::scoped_lock lock(listenersMutex_);
 
-        try
-        {
-          ApplyLuaOnStoredInstance(resultPublicId, simplified, metadata, 
-                                   dicom.GetRemoteAet(), dicom.GetCalledAet());
-        }
-        catch (OrthancException& e)
-        {
-          LOG(ERROR) << "Error in " << ON_STORED_INSTANCE << " callback (Lua): " << e.What();
-        }
-
-        if (plugins_ != NULL)
+        for (ServerListeners::iterator it = listeners_.begin(); it != listeners_.end(); ++it)
         {
           try
           {
-            plugins_->SignalStoredInstance(dicom, resultPublicId);
+            it->GetListener().SignalStoredInstance(resultPublicId, dicom, simplifiedTags);
           }
           catch (OrthancException& e)
           {
-            LOG(ERROR) << "Error in " << ON_STORED_INSTANCE << " callback (plugins): " << e.What();
+            LOG(ERROR) << "Error in the " << it->GetDescription() 
+                       << " callback while receiving an instance: " << e.What();
           }
         }
       }
@@ -410,12 +318,8 @@
       throw OrthancException(ErrorCode_InternalError);
     }
 
-    accessor_.SetCompressionForNextOperations(attachment.GetCompressionType());
-
-    std::auto_ptr<HttpFileSender> sender(accessor_.ConstructHttpFileSender(attachment.GetUuid(), attachment.GetContentType()));
-    sender->SetContentType(GetMimeType(content));
-    sender->SetDownloadFilename(instancePublicId + ".dcm");
-    output.AnswerFile(*sender);
+    StorageAccessor accessor(area_);
+    accessor.AnswerFile(output, attachment);
   }
 
 
@@ -428,7 +332,7 @@
     Json::Reader reader;
     if (!reader.parse(s, result))
     {
-      throw OrthancException("Corrupted JSON file");
+      throw OrthancException(ErrorCode_CorruptedFile);
     }
   }
 
@@ -446,14 +350,15 @@
 
     if (uncompressIfNeeded)
     {
-      accessor_.SetCompressionForNextOperations(attachment.GetCompressionType());
+      StorageAccessor accessor(area_);
+      accessor.Read(result, attachment);
     }
     else
     {
-      accessor_.SetCompressionForNextOperations(CompressionType_None);
+      // Do not interpret the content of the storage area, return the
+      // raw data
+      area_.Read(result, attachment.GetUuid(), content);
     }
-
-    accessor_.Read(result, attachment.GetUuid(), attachment.GetContentType());
   }
 
 
@@ -488,7 +393,7 @@
   void ServerContext::SetStoreMD5ForAttachments(bool storeMD5)
   {
     LOG(INFO) << "Storing MD5 for attachments: " << (storeMD5 ? "yes" : "no");
-    accessor_.SetStoreMD5(storeMD5);
+    storeMD5_ = storeMD5;
   }
 
 
@@ -499,21 +404,16 @@
   {
     LOG(INFO) << "Adding attachment " << EnumerationToString(attachmentType) << " to resource " << resourceId;
     
-    if (compressionEnabled_)
-    {
-      accessor_.SetCompressionForNextOperations(CompressionType_Zlib);
-    }
-    else
-    {
-      accessor_.SetCompressionForNextOperations(CompressionType_None);
-    }      
+    // TODO Should we use "gzip" instead?
+    CompressionType compression = (compressionEnabled_ ? CompressionType_ZlibWithSize : CompressionType_None);
 
-    FileInfo info = accessor_.Write(data, size, attachmentType);
-    StoreStatus status = index_.AddAttachment(info, resourceId);
+    StorageAccessor accessor(area_);
+    FileInfo attachment = accessor.Write(data, size, attachmentType, compression, storeMD5_);
 
+    StoreStatus status = index_.AddAttachment(attachment, resourceId);
     if (status != StoreStatus_Success)
     {
-      accessor_.Remove(info.GetUuid(), info.GetContentType());
+      accessor.Remove(attachment);
       return false;
     }
     else
@@ -533,40 +433,37 @@
 
   void ServerContext::SignalChange(const ServerIndexChange& change)
   {
-    if (plugins_ != NULL)
-    {
-      try
-      {
-        plugins_->SignalChange(change);
-      }
-      catch (OrthancException& e)
-      {
-        LOG(ERROR) << "Error in OnChangeCallback (plugins): " << e.What();
-      }
-    }
+    pendingChanges_.Enqueue(change.Clone());
   }
 
 
-  bool ServerContext::HasPlugins() const
+#if ORTHANC_PLUGINS_ENABLED == 1
+  void ServerContext::SetPlugins(OrthancPlugins& plugins)
   {
-    return (pluginsManager_ && plugins_);
+    boost::recursive_mutex::scoped_lock lock(listenersMutex_);
+
+    plugins_ = &plugins;
+
+    // TODO REFACTOR THIS
+    listeners_.clear();
+    listeners_.push_back(ServerListener(lua_, "Lua"));
+    listeners_.push_back(ServerListener(plugins, "plugin"));
   }
 
 
-  const PluginsManager& ServerContext::GetPluginsManager() const
+  void ServerContext::ResetPlugins()
   {
-    if (HasPlugins())
-    {
-      return *pluginsManager_;
-    }
-    else
-    {
-      throw OrthancException(ErrorCode_InternalError);
-    }
+    boost::recursive_mutex::scoped_lock lock(listenersMutex_);
+
+    plugins_ = NULL;
+
+    // TODO REFACTOR THIS
+    listeners_.clear();
+    listeners_.push_back(ServerListener(lua_, "Lua"));
   }
 
 
-  const OrthancPlugins& ServerContext::GetOrthancPlugins() const
+  const OrthancPlugins& ServerContext::GetPlugins() const
   {
     if (HasPlugins())
     {
@@ -577,4 +474,16 @@
       throw OrthancException(ErrorCode_InternalError);
     }
   }
+
+#endif
+
+
+  bool ServerContext::HasPlugins() const
+  {
+#if ORTHANC_PLUGINS_ENABLED == 1
+    return (plugins_ != NULL);
+#else
+    return false;
+#endif
+  }
 }
--- a/OrthancServer/ServerContext.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/ServerContext.h	Wed Sep 30 13:23:31 2015 +0200
@@ -32,25 +32,28 @@
 
 #pragma once
 
+#include "../Core/MultiThreading/SharedMessageQueue.h"
 #include "../Core/Cache/MemoryCache.h"
-#include "../Core/FileStorage/CompressedFileStorageAccessor.h"
+#include "../Core/Cache/SharedArchive.h"
 #include "../Core/FileStorage/IStorageArea.h"
+#include "../Core/Lua/LuaContext.h"
 #include "../Core/RestApi/RestApiOutput.h"
-#include "../Core/Lua/LuaContext.h"
+#include "../Plugins/Engine/OrthancPlugins.h"
+#include "DicomInstanceToStore.h"
+#include "DicomProtocol/ReusableDicomUserConnection.h"
+#include "IServerListener.h"
+#include "LuaScripting.h"
+#include "ParsedDicomFile.h"
+#include "Scheduler/ServerScheduler.h"
 #include "ServerIndex.h"
-#include "ParsedDicomFile.h"
-#include "DicomProtocol/ReusableDicomUserConnection.h"
-#include "Scheduler/ServerScheduler.h"
-#include "DicomInstanceToStore.h"
-#include "ServerIndexChange.h"
+#include "OrthancHttpHandler.h"
 
 #include <boost/filesystem.hpp>
+#include <boost/thread.hpp>
+
 
 namespace Orthanc
 {
-  class OrthancPlugins;
-  class PluginsManager;
-
   /**
    * This class is responsible for maintaining the storage area on the
    * filesystem (including compression), as well as the index of the
@@ -72,18 +75,42 @@
       virtual IDynamicObject* Provide(const std::string& id);
     };
 
-    bool ApplyReceivedInstanceFilter(const Json::Value& simplified,
-                                     const std::string& remoteAet);
+    class ServerListener
+    {
+    private:
+      IServerListener *listener_;
+      std::string      description_;
+
+    public:
+      ServerListener(IServerListener& listener,
+                     const std::string& description) :
+        listener_(&listener),
+        description_(description)
+      {
+      }
 
-    void ApplyLuaOnStoredInstance(const std::string& instanceId,
-                                  const Json::Value& simplifiedDicom,
-                                  const Json::Value& metadata,
-                                  const std::string& remoteAet,
-                                  const std::string& calledAet);
+      IServerListener& GetListener()
+      {
+        return *listener_;
+      }
+
+      const std::string& GetDescription()
+      {
+        return description_;
+      }
+    };
+
+    typedef std::list<ServerListener>  ServerListeners;
+
+
+    static void ChangeThread(ServerContext* that);
+
 
     ServerIndex index_;
-    CompressedFileStorageAccessor accessor_;
+    IStorageArea& area_;
+
     bool compressionEnabled_;
+    bool storeMD5_;
     
     DicomCacheProvider provider_;
     boost::mutex dicomCacheMutex_;
@@ -91,10 +118,22 @@
     ReusableDicomUserConnection scu_;
     ServerScheduler scheduler_;
 
-    boost::mutex luaMutex_;
-    LuaContext lua_;
-    OrthancPlugins* plugins_;  // TODO Turn it into a listener pattern (idem for Lua callbacks)
-    const PluginsManager* pluginsManager_;
+    LuaScripting lua_;
+
+#if ORTHANC_PLUGINS_ENABLED == 1
+    OrthancPlugins* plugins_;
+#endif
+
+    ServerListeners listeners_;
+    boost::recursive_mutex listenersMutex_;
+
+    bool done_;
+    SharedMessageQueue  pendingChanges_;
+    boost::thread  changeThread_;
+        
+    SharedArchive  queryRetrieveArchive_;
+    std::string defaultLocalAet_;
+    OrthancHttpHandler  httpHandler_;
 
   public:
     class DicomCacheLocker : public boost::noncopyable
@@ -116,35 +155,10 @@
       }
     };
 
-    class LuaContextLocker : public boost::noncopyable
-    {
-    private:
-      ServerContext& that_;
-
-    public:
-      LuaContextLocker(ServerContext& that) : that_(that)
-      {
-        that.luaMutex_.lock();
-      }
+    ServerContext(IDatabaseWrapper& database,
+                  IStorageArea& area);
 
-      ~LuaContextLocker()
-      {
-        that_.luaMutex_.unlock();
-      }
-
-      LuaContext& GetLua()
-      {
-        return that_.lua_;
-      }
-    };
-
-
-    ServerContext(IDatabaseWrapper& database);
-
-    void SetStorageArea(IStorageArea& storage)
-    {
-      accessor_.SetStorageArea(storage);
-    }
+    ~ServerContext();
 
     ServerIndex& GetIndex()
     {
@@ -186,7 +200,7 @@
 
     bool IsStoreMD5ForAttachments() const
     {
-      return accessor_.IsStoreMD5();
+      return storeMD5_;
     }
 
     ReusableDicomUserConnection& GetReusableDicomUserConnection()
@@ -199,29 +213,48 @@
       return scheduler_;
     }
 
-    void SetOrthancPlugins(const PluginsManager& manager,
-                           OrthancPlugins& plugins)
-    {
-      pluginsManager_ = &manager;
-      plugins_ = &plugins;
-    }
-
-    void ResetOrthancPlugins()
-    {
-      pluginsManager_ = NULL;
-      plugins_ = NULL;
-    }
-
     bool DeleteResource(Json::Value& target,
                         const std::string& uuid,
                         ResourceType expectedType);
 
     void SignalChange(const ServerIndexChange& change);
 
+    SharedArchive& GetQueryRetrieveArchive()
+    {
+      return queryRetrieveArchive_;
+    }
+
+    const std::string& GetDefaultLocalApplicationEntityTitle() const
+    {
+      return defaultLocalAet_;
+    }
+
+    LuaScripting& GetLua()
+    {
+      return lua_;
+    }
+
+    OrthancHttpHandler& GetHttpHandler()
+    {
+      return httpHandler_;
+    }
+
+    void Stop();
+
+
+    /**
+     * Management of the plugins
+     **/
+
+#if ORTHANC_PLUGINS_ENABLED == 1
+    void SetPlugins(OrthancPlugins& plugins);
+
+    void ResetPlugins();
+
+    const OrthancPlugins& GetPlugins() const;
+#endif
+
     bool HasPlugins() const;
 
-    const PluginsManager& GetPluginsManager() const;
-
-    const OrthancPlugins& GetOrthancPlugins() const;
   };
 }
--- a/OrthancServer/ServerEnumerations.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/ServerEnumerations.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -243,44 +243,6 @@
   }
 
 
-  ResourceType GetParentResourceType(ResourceType type)
-  {
-    switch (type)
-    {
-      case ResourceType_Study:
-        return ResourceType_Patient;
-
-      case ResourceType_Series:
-        return ResourceType_Study;
-
-      case ResourceType_Instance:
-        return ResourceType_Series;
-      
-      default:
-        throw OrthancException(ErrorCode_ParameterOutOfRange);
-    }
-  }
-
-
-  ResourceType GetChildResourceType(ResourceType type)
-  {
-    switch (type)
-    {
-      case ResourceType_Patient:
-        return ResourceType_Study;
-
-      case ResourceType_Study:
-        return ResourceType_Series;
-
-      case ResourceType_Series:
-        return ResourceType_Instance;
-      
-      default:
-        throw OrthancException(ErrorCode_ParameterOutOfRange);
-    }
-  }
-
-
   const char* EnumerationToString(ModalityManufacturer manufacturer)
   {
     switch (manufacturer)
@@ -300,6 +262,9 @@
       case ModalityManufacturer_Dcm4Chee:
         return "Dcm4Chee";
       
+      case ModalityManufacturer_SyngoVia:
+        return "SyngoVia";
+      
       default:
         throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
@@ -359,6 +324,10 @@
     {
       return ModalityManufacturer_Dcm4Chee;
     }
+    else if (manufacturer == "SyngoVia")
+    {
+      return ModalityManufacturer_SyngoVia;
+    }
     else
     {
       throw OrthancException(ErrorCode_ParameterOutOfRange);
--- a/OrthancServer/ServerEnumerations.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/ServerEnumerations.h	Wed Sep 30 13:23:31 2015 +0200
@@ -60,7 +60,8 @@
     ModalityManufacturer_StoreScp,
     ModalityManufacturer_ClearCanvas,
     ModalityManufacturer_MedInria,
-    ModalityManufacturer_Dcm4Chee
+    ModalityManufacturer_Dcm4Chee,
+    ModalityManufacturer_SyngoVia
   };
 
   enum DicomRequestType
@@ -90,6 +91,15 @@
     TransferSyntax_Rle
   };
 
+  enum ValueRepresentation
+  {
+    ValueRepresentation_Other,
+    ValueRepresentation_PatientName,
+    ValueRepresentation_Date,
+    ValueRepresentation_DateTime,
+    ValueRepresentation_Time
+  };
+
 
   /**
    * WARNING: Do not change the explicit values in the enumerations
@@ -177,8 +187,4 @@
   const char* EnumerationToString(TransferSyntax syntax);
 
   ModalityManufacturer StringToModalityManufacturer(const std::string& manufacturer);
-
-  ResourceType GetParentResourceType(ResourceType type);
-
-  ResourceType GetChildResourceType(ResourceType type);
 }
--- a/OrthancServer/ServerIndex.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/ServerIndex.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -41,172 +41,170 @@
 #include "EmbeddedResources.h"
 #include "OrthancInitialization.h"
 #include "../Core/Toolbox.h"
+#include "../Core/Logging.h"
 #include "../Core/Uuid.h"
 #include "../Core/DicomFormat/DicomArray.h"
+
 #include "FromDcmtkBridge.h"
 #include "ServerContext.h"
 
 #include <boost/lexical_cast.hpp>
 #include <stdio.h>
-#include <glog/logging.h>
 
 static const uint64_t MEGA_BYTES = 1024 * 1024;
 
 namespace Orthanc
 {
-  namespace Internals
+  class ServerIndex::Listener : public IDatabaseListener
   {
-    class ServerIndexListener : public IServerIndexListener
+  private:
+    struct FileToRemove
     {
     private:
-      struct FileToRemove
-      {
-      private:
-        std::string  uuid_;
-        FileContentType  type_;
-
-      public:
-        FileToRemove(const FileInfo& info) : uuid_(info.GetUuid()), 
-                                             type_(info.GetContentType())
-        {
-        }
-
-        const std::string& GetUuid() const
-        {
-          return uuid_;
-        }
+      std::string  uuid_;
+      FileContentType  type_;
 
-        FileContentType GetContentType() const 
-        {
-          return type_;
-        }
-      };
-
-      ServerContext& context_;
-      bool hasRemainingLevel_;
-      ResourceType remainingType_;
-      std::string remainingPublicId_;
-      std::list<FileToRemove> pendingFilesToRemove_;
-      std::list<ServerIndexChange> pendingChanges_;
-      uint64_t sizeOfFilesToRemove_;
-      bool insideTransaction_;
-
-      void Reset()
+    public:
+      FileToRemove(const FileInfo& info) : uuid_(info.GetUuid()), 
+                                           type_(info.GetContentType())
       {
-        sizeOfFilesToRemove_ = 0;
-        hasRemainingLevel_ = false;
-        pendingFilesToRemove_.clear();
-        pendingChanges_.clear();
       }
 
-    public:
-      ServerIndexListener(ServerContext& context) : context_(context),
-                                                    insideTransaction_(false)      
-      {
-        Reset();
-        assert(ResourceType_Patient < ResourceType_Study &&
-               ResourceType_Study < ResourceType_Series &&
-               ResourceType_Series < ResourceType_Instance);
-      }
-
-      void StartTransaction()
+      const std::string& GetUuid() const
       {
-        Reset();
-        insideTransaction_ = true;
-      }
-
-      void EndTransaction()
-      {
-        insideTransaction_ = false;
-      }
-
-      uint64_t GetSizeOfFilesToRemove()
-      {
-        return sizeOfFilesToRemove_;
+        return uuid_;
       }
 
-      void CommitFilesToRemove()
+      FileContentType GetContentType() const 
       {
-        for (std::list<FileToRemove>::const_iterator 
-               it = pendingFilesToRemove_.begin();
-             it != pendingFilesToRemove_.end(); ++it)
-        {
-          context_.RemoveFile(it->GetUuid(), it->GetContentType());
-        }
+        return type_;
       }
+    };
 
-      void CommitChanges()
+    ServerContext& context_;
+    bool hasRemainingLevel_;
+    ResourceType remainingType_;
+    std::string remainingPublicId_;
+    std::list<FileToRemove> pendingFilesToRemove_;
+    std::list<ServerIndexChange> pendingChanges_;
+    uint64_t sizeOfFilesToRemove_;
+    bool insideTransaction_;
+
+    void Reset()
+    {
+      sizeOfFilesToRemove_ = 0;
+      hasRemainingLevel_ = false;
+      pendingFilesToRemove_.clear();
+      pendingChanges_.clear();
+    }
+
+  public:
+    Listener(ServerContext& context) : context_(context),
+                                       insideTransaction_(false)      
+    {
+      Reset();
+      assert(ResourceType_Patient < ResourceType_Study &&
+             ResourceType_Study < ResourceType_Series &&
+             ResourceType_Series < ResourceType_Instance);
+    }
+
+    void StartTransaction()
+    {
+      Reset();
+      insideTransaction_ = true;
+    }
+
+    void EndTransaction()
+    {
+      insideTransaction_ = false;
+    }
+
+    uint64_t GetSizeOfFilesToRemove()
+    {
+      return sizeOfFilesToRemove_;
+    }
+
+    void CommitFilesToRemove()
+    {
+      for (std::list<FileToRemove>::const_iterator 
+             it = pendingFilesToRemove_.begin();
+           it != pendingFilesToRemove_.end(); ++it)
       {
-        for (std::list<ServerIndexChange>::const_iterator 
-               it = pendingChanges_.begin(); 
-             it != pendingChanges_.end(); ++it)
+        context_.RemoveFile(it->GetUuid(), it->GetContentType());
+      }
+    }
+
+    void CommitChanges()
+    {
+      for (std::list<ServerIndexChange>::const_iterator 
+             it = pendingChanges_.begin(); 
+           it != pendingChanges_.end(); ++it)
+      {
+        context_.SignalChange(*it);
+      }
+    }
+
+    virtual void SignalRemainingAncestor(ResourceType parentType,
+                                         const std::string& publicId)
+    {
+      VLOG(1) << "Remaining ancestor \"" << publicId << "\" (" << parentType << ")";
+
+      if (hasRemainingLevel_)
+      {
+        if (parentType < remainingType_)
         {
-          context_.SignalChange(*it);
-        }
-      }
-
-      virtual void SignalRemainingAncestor(ResourceType parentType,
-                                           const std::string& publicId)
-      {
-        LOG(INFO) << "Remaining ancestor \"" << publicId << "\" (" << parentType << ")";
-
-        if (hasRemainingLevel_)
-        {
-          if (parentType < remainingType_)
-          {
-            remainingType_ = parentType;
-            remainingPublicId_ = publicId;
-          }
-        }
-        else
-        {
-          hasRemainingLevel_ = true;
           remainingType_ = parentType;
           remainingPublicId_ = publicId;
-        }        
-      }
-
-      virtual void SignalFileDeleted(const FileInfo& info)
-      {
-        assert(Toolbox::IsUuid(info.GetUuid()));
-        pendingFilesToRemove_.push_back(FileToRemove(info));
-        sizeOfFilesToRemove_ += info.GetCompressedSize();
-      }
-
-      virtual void SignalChange(const ServerIndexChange& change)
-      {
-        LOG(INFO) << "Change related to resource " << change.GetPublicId() << " of type " 
-                  << EnumerationToString(change.GetResourceType()) << ": " 
-                  << EnumerationToString(change.GetChangeType());
-
-        if (insideTransaction_)
-        {
-          pendingChanges_.push_back(change);
-        }
-        else
-        {
-          context_.SignalChange(change);
         }
       }
-
-      bool HasRemainingLevel() const
+      else
       {
-        return hasRemainingLevel_;
-      }
+        hasRemainingLevel_ = true;
+        remainingType_ = parentType;
+        remainingPublicId_ = publicId;
+      }        
+    }
 
-      ResourceType GetRemainingType() const
+    virtual void SignalFileDeleted(const FileInfo& info)
+    {
+      assert(Toolbox::IsUuid(info.GetUuid()));
+      pendingFilesToRemove_.push_back(FileToRemove(info));
+      sizeOfFilesToRemove_ += info.GetCompressedSize();
+    }
+
+    virtual void SignalChange(const ServerIndexChange& change)
+    {
+      VLOG(1) << "Change related to resource " << change.GetPublicId() << " of type " 
+              << EnumerationToString(change.GetResourceType()) << ": " 
+              << EnumerationToString(change.GetChangeType());
+
+      if (insideTransaction_)
       {
-        assert(HasRemainingLevel());
-        return remainingType_;
+        pendingChanges_.push_back(change);
       }
-
-      const std::string& GetRemainingPublicId() const
+      else
       {
-        assert(HasRemainingLevel());
-        return remainingPublicId_;
-      }                                 
-    };
-  }
+        context_.SignalChange(change);
+      }
+    }
+
+    bool HasRemainingLevel() const
+    {
+      return hasRemainingLevel_;
+    }
+
+    ResourceType GetRemainingType() const
+    {
+      assert(HasRemainingLevel());
+      return remainingType_;
+    }
+
+    const std::string& GetRemainingPublicId() const
+    {
+      assert(HasRemainingLevel());
+      return remainingPublicId_;
+    }                                 
+  };
 
 
   class ServerIndex::Transaction
@@ -549,7 +547,7 @@
     maximumStorageSize_(0),
     maximumPatients_(0)
   {
-    listener_.reset(new Internals::ServerIndexListener(context));
+    listener_.reset(new Listener(context));
     db_.SetListener(*listener_);
 
     currentStorageSize_ = db_.GetTotalCompressedSize();
@@ -567,23 +565,39 @@
   }
 
 
+
   ServerIndex::~ServerIndex()
   {
-    done_ = true;
-
-    if (db_.HasFlushToDisk() &&
-        flushThread_.joinable())
+    if (!done_)
     {
-      flushThread_.join();
-    }
-
-    if (unstableResourcesMonitorThread_.joinable())
-    {
-      unstableResourcesMonitorThread_.join();
+      LOG(ERROR) << "INTERNAL ERROR: ServerIndex::Stop() should be invoked manually to avoid mess in the destruction order!";
+      Stop();
     }
   }
 
 
+
+  void ServerIndex::Stop()
+  {
+    if (!done_)
+    {
+      done_ = true;
+
+      if (db_.HasFlushToDisk() &&
+          flushThread_.joinable())
+      {
+        flushThread_.join();
+      }
+
+      if (unstableResourcesMonitorThread_.joinable())
+      {
+        unstableResourcesMonitorThread_.join();
+      }
+    }
+  }
+
+
+
   StoreStatus ServerIndex::Store(std::map<MetadataType, std::string>& instanceMetadata,
                                  const DicomMap& dicomSummary,
                                  const Attachments& attachments,
@@ -883,7 +897,7 @@
     DicomMap tags;
     db_.GetMainDicomTags(tags, resourceId);
     target["MainDicomTags"] = Json::objectValue;
-    FromDcmtkBridge::ToJson(target["MainDicomTags"], tags);
+    FromDcmtkBridge::ToJson(target["MainDicomTags"], tags, true);
   }
 
   bool ServerIndex::LookupResource(Json::Value& result,
@@ -1075,22 +1089,27 @@
 
 
 
-  void ServerIndex::GetAllUuids(Json::Value& target,
+  void ServerIndex::GetAllUuids(std::list<std::string>& target,
                                 ResourceType resourceType)
   {
-    std::list<std::string> lst;
+    boost::mutex::scoped_lock lock(mutex_);
+    db_.GetAllPublicIds(target, resourceType);
+  }
+
 
+  void ServerIndex::GetAllUuids(std::list<std::string>& target,
+                                ResourceType resourceType,
+                                size_t since,
+                                size_t limit)
+  {
+    if (limit == 0)
     {
-      boost::mutex::scoped_lock lock(mutex_);
-      db_.GetAllPublicIds(lst, resourceType);
+      target.clear();
+      return;
     }
 
-    target = Json::arrayValue;
-    for (std::list<std::string>::const_iterator
-           it = lst.begin(); it != lst.end(); ++it)
-    {
-      target.append(*it);
-    }
+    boost::mutex::scoped_lock lock(mutex_);
+    db_.GetAllPublicIds(target, resourceType, since, limit);
   }
 
 
@@ -1316,7 +1335,7 @@
         throw OrthancException(ErrorCode_FullStorage);
       }
       
-      LOG(INFO) << "Recycling one patient";
+      VLOG(1) << "Recycling one patient";
       db_.DeleteResource(patientToRecycle);
 
       if (!IsRecyclingNeeded(instanceSize))
@@ -2055,4 +2074,38 @@
     }
   }
 
+
+  bool ServerIndex::GetMainDicomTags(DicomMap& result,
+                                     const std::string& publicId,
+                                     ResourceType expectedType)
+  {
+    result.Clear();
+
+    boost::mutex::scoped_lock lock(mutex_);
+
+    // Lookup for the requested resource
+    int64_t id;
+    ResourceType type;
+    if (!db_.LookupResource(id, type, publicId) ||
+        type != expectedType)
+    {
+      return false;
+    }
+    else
+    {
+      db_.GetMainDicomTags(result, id);
+      return true;
+    }    
+  }
+
+
+  bool ServerIndex::LookupResourceType(ResourceType& type,
+                                       const std::string& publicId)
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+
+    int64_t id;
+    return db_.LookupResource(id, type, publicId);
+  }
+
 }
--- a/OrthancServer/ServerIndex.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/ServerIndex.h	Wed Sep 30 13:23:31 2015 +0200
@@ -47,11 +47,6 @@
 {
   class ServerContext;
 
-  namespace Internals
-  {
-    class ServerIndexListener;
-  }
-
   class ServerIndex : public boost::noncopyable
   {
   public:
@@ -59,6 +54,7 @@
     typedef std::map< std::pair<ResourceType, MetadataType>, std::string>  MetadataMap;
 
   private:
+    class Listener;
     class Transaction;
     class UnstableResourcePayload;
 
@@ -67,7 +63,7 @@
     boost::thread flushThread_;
     boost::thread unstableResourcesMonitorThread_;
 
-    std::auto_ptr<Internals::ServerIndexListener> listener_;
+    std::auto_ptr<Listener> listener_;
     IDatabaseWrapper& db_;
     LeastRecentlyUsedIndex<int64_t, UnstableResourcePayload>  unstableResources_;
 
@@ -126,6 +122,8 @@
 
     ~ServerIndex();
 
+    void Stop();
+
     uint64_t GetMaximumStorageSize() const
     {
       return maximumStorageSize_;
@@ -158,9 +156,14 @@
                           const std::string& instanceUuid,
                           FileContentType contentType);
 
-    void GetAllUuids(Json::Value& target,
+    void GetAllUuids(std::list<std::string>& target,
                      ResourceType resourceType);
 
+    void GetAllUuids(std::list<std::string>& target,
+                     ResourceType resourceType,
+                     size_t since,
+                     size_t limit);
+
     bool DeleteResource(Json::Value& target /* out */,
                         const std::string& uuid,
                         ResourceType expectedType);
@@ -257,5 +260,12 @@
 
     std::string GetGlobalProperty(GlobalProperty property,
                                   const std::string& defaultValue);
+
+    bool GetMainDicomTags(DicomMap& result,
+                          const std::string& publicId,
+                          ResourceType expectedType);
+
+    bool LookupResourceType(ResourceType& type,
+                            const std::string& publicId);
   };
 }
--- a/OrthancServer/ServerIndexChange.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/ServerIndexChange.h	Wed Sep 30 13:23:31 2015 +0200
@@ -40,7 +40,7 @@
 
 namespace Orthanc
 {
-  struct ServerIndexChange
+  struct ServerIndexChange : public IDynamicObject
   {
   private:
     int64_t      seq_;
@@ -74,6 +74,20 @@
     {
     }
 
+    ServerIndexChange(const ServerIndexChange& other) 
+    : seq_(other.seq_),
+      changeType_(other.changeType_),
+      resourceType_(other.resourceType_),
+      publicId_(other.publicId_),
+      date_(other.date_)
+    {
+    }
+
+    ServerIndexChange* Clone() const
+    {
+      return new ServerIndexChange(*this);
+    }
+
     int64_t  GetSeq() const
     {
       return seq_;
--- a/OrthancServer/ServerToolbox.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/ServerToolbox.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,10 +33,10 @@
 #include "PrecompiledHeadersServer.h"
 #include "ServerToolbox.h"
 
+#include "../Core/Logging.h"
 #include "../Core/OrthancException.h"
 
 #include <cassert>
-#include <glog/logging.h>
 
 namespace Orthanc
 {
--- a/OrthancServer/main.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/OrthancServer/main.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -34,9 +34,9 @@
 #include "OrthancRestApi/OrthancRestApi.h"
 
 #include <fstream>
-#include <glog/logging.h>
 #include <boost/algorithm/string/predicate.hpp>
 
+#include "../Core/Logging.h"
 #include "../Core/Uuid.h"
 #include "../Core/HttpServer/EmbeddedResourceHttpHandler.h"
 #include "../Core/HttpServer/FilesystemHttpHandler.h"
@@ -49,15 +49,12 @@
 #include "OrthancFindRequestHandler.h"
 #include "OrthancMoveRequestHandler.h"
 #include "ServerToolbox.h"
-#include "../Plugins/Engine/PluginsManager.h"
 #include "../Plugins/Engine/OrthancPlugins.h"
+#include "FromDcmtkBridge.h"
 
 using namespace Orthanc;
 
 
-#define ENABLE_PLUGINS  1
-
-
 class OrthancStoreRequestHandler : public IStoreRequestHandler
 {
 private:
@@ -69,20 +66,21 @@
   {
   }
 
+
   virtual void Handle(const std::string& dicomFile,
                       const DicomMap& dicomSummary,
                       const Json::Value& dicomJson,
+                      const std::string& remoteIp,
                       const std::string& remoteAet,
-                      const std::string& calledAet)
+                      const std::string& calledAet) 
   {
     if (dicomFile.size() > 0)
     {
       DicomInstanceToStore toStore;
+      toStore.SetDicomProtocolOrigin(remoteIp.c_str(), remoteAet.c_str(), calledAet.c_str());
       toStore.SetBuffer(dicomFile);
       toStore.SetSummary(dicomSummary);
       toStore.SetJson(dicomJson);
-      toStore.SetRemoteAet(remoteAet);
-      toStore.SetCalledAet(calledAet);
 
       std::string id;
       server_.Store(id, toStore);
@@ -231,7 +229,7 @@
     {
       std::string lua = "Is" + configuration;
 
-      ServerContext::LuaContextLocker locker(context_);
+      LuaScripting::Locker locker(context_.GetLua());
       
       if (locker.GetLua().IsExistingFunction(lua.c_str()))
       {
@@ -264,7 +262,7 @@
   {
     static const char* HTTP_FILTER = "IncomingHttpRequestFilter";
 
-    ServerContext::LuaContextLocker locker(context_);
+    LuaScripting::Locker locker(context_.GetLua());
 
     // Test if the instance must be filtered out
     if (locker.GetLua().IsExistingFunction(HTTP_FILTER))
@@ -309,6 +307,86 @@
 };
 
 
+
+class MyHttpExceptionFormatter : public IHttpExceptionFormatter
+{
+private:
+  bool             describeErrors_;
+  OrthancPlugins*  plugins_;
+
+public:
+  MyHttpExceptionFormatter(bool describeErrors,
+                           OrthancPlugins* plugins) :
+    describeErrors_(describeErrors),
+    plugins_(plugins)
+  {
+  }
+
+  virtual void Format(HttpOutput& output,
+                      const OrthancException& exception,
+                      HttpMethod method,
+                      const char* uri)
+  {
+    {
+      bool isPlugin = false;
+
+#if ORTHANC_PLUGINS_ENABLED == 1
+      if (plugins_ != NULL)
+      {
+        plugins_->GetErrorDictionary().LogError(exception.GetErrorCode(), true);
+        isPlugin = true;
+      }
+#endif
+
+      if (!isPlugin)
+      {
+        LOG(ERROR) << "Exception in the HTTP handler: " << exception.What();
+      }
+    }      
+
+    Json::Value message = Json::objectValue;
+    ErrorCode errorCode = exception.GetErrorCode();
+    HttpStatus httpStatus = exception.GetHttpStatus();
+
+    {
+      bool isPlugin = false;
+
+#if ORTHANC_PLUGINS_ENABLED == 1
+      if (plugins_ != NULL &&
+          plugins_->GetErrorDictionary().Format(message, httpStatus, exception))
+      {
+        errorCode = ErrorCode_Plugin;
+        isPlugin = true;
+      }
+#endif
+
+      if (!isPlugin)
+      {
+        message["Message"] = exception.What();
+      }
+    }
+
+    if (!describeErrors_)
+    {
+      output.SendStatus(httpStatus);
+    }
+    else
+    {
+      message["Method"] = EnumerationToString(method);
+      message["Uri"] = uri;
+      message["HttpError"] = EnumerationToString(httpStatus);
+      message["HttpStatus"] = httpStatus;
+      message["OrthancError"] = EnumerationToString(errorCode);
+      message["OrthancStatus"] = errorCode;
+
+      std::string info = message.toStyledString();
+      output.SendStatus(httpStatus, info);
+    }
+  }
+};
+
+
+
 static void PrintHelp(char* path)
 {
   std::cout 
@@ -317,21 +395,23 @@
     << std::endl
     << "If no configuration file is given on the command line, a set of default " << std::endl
     << "parameters is used. Please refer to the Orthanc homepage for the full " << std::endl
-    << "instructions about how to use Orthanc " << std::endl
-    << "<https://code.google.com/p/orthanc/wiki/OrthancCookbook>." << std::endl
+    << "instructions about how to use Orthanc <http://www.orthanc-server.com/>." << std::endl
     << std::endl
     << "Command-line options:" << std::endl
     << "  --help\t\tdisplay this help and exit" << std::endl
     << "  --logdir=[dir]\tdirectory where to store the log files" << std::endl
     << "\t\t\t(if not used, the logs are dumped to stderr)" << std::endl
     << "  --config=[file]\tcreate a sample configuration file and exit" << std::endl
+    << "  --verbose\t\tbe verbose in logs" << std::endl
     << "  --trace\t\thighest verbosity in logs (for debug)" << std::endl
-    << "  --verbose\t\tbe verbose in logs" << std::endl
+    << "  --upgrade\t\tallow Orthanc to upgrade the version of the" << std::endl
+    << "\t\t\tdatabase (beware that the database will become" << std::endl
+    << "\t\t\tincompatible with former versions of Orthanc)" << std::endl
     << "  --version\t\toutput version information and exit" << std::endl
     << std::endl
     << "Exit status:" << std::endl
-    << " 0  if OK," << std::endl
-    << " -1  if error (have a look at the logs)." << std::endl
+    << "   0 if success," << std::endl
+    << "  -1 if error (have a look at the logs)." << std::endl
     << std::endl;
 }
 
@@ -362,210 +442,334 @@
     std::string script;
     Toolbox::ReadFile(script, path);
 
-    ServerContext::LuaContextLocker locker(context);
+    LuaScripting::Locker locker(context.GetLua());
     locker.GetLua().Execute(script);
   }
 }
 
 
-static void LoadPlugins(PluginsManager& pluginsManager)
+
+#if ORTHANC_PLUGINS_ENABLED == 1
+static void LoadPlugins(OrthancPlugins& plugins)
 {
-  std::list<std::string> plugins;
-  Configuration::GetGlobalListOfStringsParameter(plugins, "Plugins");
+  std::list<std::string> path;
+  Configuration::GetGlobalListOfStringsParameter(path, "Plugins");
   for (std::list<std::string>::const_iterator
-         it = plugins.begin(); it != plugins.end(); ++it)
+         it = path.begin(); it != path.end(); ++it)
   {
     std::string path = Configuration::InterpretStringParameterAsPath(*it);
-    LOG(WARNING) << "Registering a plugin from: " << path;
-    pluginsManager.RegisterPlugin(path);
+    LOG(WARNING) << "Loading plugin(s) from: " << path;
+    plugins.GetManager().RegisterPlugin(path);
   }  
 }
+#endif
+
+
+
+// Returns "true" if restart is required
+static bool WaitForExit(ServerContext& context,
+                        OrthancRestApi& restApi)
+{
+  LOG(WARNING) << "Orthanc has started";
+
+  context.GetLua().Execute("Initialize");
+
+  Toolbox::ServerBarrier(restApi.ResetRequestReceivedFlag());
+  bool restart = restApi.ResetRequestReceivedFlag();
+
+  context.GetLua().Execute("Finalize");
+
+  if (restart)
+  {
+    LOG(WARNING) << "Reset request received, restarting Orthanc";
+  }
+
+  // We're done
+  LOG(WARNING) << "Orthanc is stopping";
+
+  return restart;
+}
 
 
 
-static bool StartOrthanc(int argc, char *argv[])
+static bool StartHttpServer(ServerContext& context,
+                            OrthancRestApi& restApi,
+                            OrthancPlugins* plugins)
 {
-#if ENABLE_PLUGINS == 1
-  OrthancPlugins orthancPlugins;
-  orthancPlugins.SetCommandLineArguments(argc, argv);
-  PluginsManager pluginsManager;
-  pluginsManager.RegisterServiceProvider(orthancPlugins);
-  LoadPlugins(pluginsManager);
-#endif
+  if (!Configuration::GetGlobalBoolParameter("HttpServerEnabled", true))
+  {
+    LOG(WARNING) << "The HTTP server is disabled";
+    return WaitForExit(context, restApi);
+  }
+
+  MyHttpExceptionFormatter exceptionFormatter(Configuration::GetGlobalBoolParameter("HttpDescribeErrors", true), plugins);
+  
 
-  // "storage" and "database" must be declared BEFORE "ServerContext
-  // context", to avoid mess in the invokation order of the destructors.
-  std::auto_ptr<IDatabaseWrapper> database;
-  std::auto_ptr<IStorageArea>  storage;
-  std::auto_ptr<ServerContext> context;
+  // HTTP server
+  MyIncomingHttpRequestFilter httpFilter(context);
+  MongooseServer httpServer;
+  httpServer.SetPortNumber(Configuration::GetGlobalIntegerParameter("HttpPort", 8042));
+  httpServer.SetRemoteAccessAllowed(Configuration::GetGlobalBoolParameter("RemoteAccessAllowed", false));
+  httpServer.SetKeepAliveEnabled(Configuration::GetGlobalBoolParameter("KeepAlive", false));
+  httpServer.SetHttpCompressionEnabled(Configuration::GetGlobalBoolParameter("HttpCompressionEnabled", true));
+  httpServer.SetIncomingHttpRequestFilter(httpFilter);
+  httpServer.SetHttpExceptionFormatter(exceptionFormatter);
 
-  if (orthancPlugins.HasDatabase())
+  httpServer.SetAuthenticationEnabled(Configuration::GetGlobalBoolParameter("AuthenticationEnabled", false));
+  Configuration::SetupRegisteredUsers(httpServer);
+
+  if (Configuration::GetGlobalBoolParameter("SslEnabled", false))
   {
-    context.reset(new ServerContext(orthancPlugins.GetDatabase()));
+    std::string certificate = Configuration::InterpretStringParameterAsPath(
+      Configuration::GetGlobalStringParameter("SslCertificate", "certificate.pem"));
+    httpServer.SetSslEnabled(true);
+    httpServer.SetSslCertificate(certificate.c_str());
   }
   else
   {
-    database.reset(Configuration::CreateDatabaseWrapper());
-    context.reset(new ServerContext(*database));
+    httpServer.SetSslEnabled(false);
+  }
+
+  httpServer.Register(context.GetHttpHandler());
+
+  httpServer.Start();
+  LOG(WARNING) << "HTTP server listening on port: " << httpServer.GetPortNumber();
+  
+  bool restart = WaitForExit(context, restApi);
+
+  httpServer.Stop();
+  LOG(WARNING) << "    HTTP server has stopped";
+
+  return restart;
+}
+
+
+static bool StartDicomServer(ServerContext& context,
+                             OrthancRestApi& restApi,
+                             OrthancPlugins* plugins)
+{
+  if (!Configuration::GetGlobalBoolParameter("DicomServerEnabled", true))
+  {
+    LOG(WARNING) << "The DICOM server is disabled";
+    return StartHttpServer(context, restApi, plugins);
   }
 
-  context->SetCompressionEnabled(Configuration::GetGlobalBoolParameter("StorageCompression", false));
-  context->SetStoreMD5ForAttachments(Configuration::GetGlobalBoolParameter("StoreMD5ForAttachments", true));
+  MyDicomServerFactory serverFactory(context);
+
+  // DICOM server
+  DicomServer dicomServer;
+  OrthancApplicationEntityFilter dicomFilter(context);
+  dicomServer.SetCalledApplicationEntityTitleCheck(Configuration::GetGlobalBoolParameter("DicomCheckCalledAet", false));
+  dicomServer.SetStoreRequestHandlerFactory(serverFactory);
+  dicomServer.SetMoveRequestHandlerFactory(serverFactory);
+  dicomServer.SetFindRequestHandlerFactory(serverFactory);
+  dicomServer.SetPortNumber(Configuration::GetGlobalIntegerParameter("DicomPort", 4242));
+  dicomServer.SetApplicationEntityTitle(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC"));
+  dicomServer.SetApplicationEntityFilter(dicomFilter);
+
+  dicomServer.Start();
+  LOG(WARNING) << "DICOM server listening on port: " << dicomServer.GetPortNumber();
+
+  bool restart = StartHttpServer(context, restApi, plugins);
+
+  dicomServer.Stop();
+  LOG(WARNING) << "    DICOM server has stopped";
+
+  serverFactory.Done();
+
+  return restart;
+}
+
+
+static bool ConfigureHttpHandler(ServerContext& context,
+                                 OrthancPlugins *plugins)
+{
+#if ORTHANC_PLUGINS_ENABLED == 1
+  // By order of priority, first apply the "plugins" layer, so that
+  // plugins can overwrite the built-in REST API of Orthanc
+  if (plugins)
+  {
+    assert(context.HasPlugins());
+    context.GetHttpHandler().Register(*plugins, false);
+  }
+#endif
+
+  // Secondly, apply the "static resources" layer
+#if ORTHANC_STANDALONE == 1
+  EmbeddedResourceHttpHandler staticResources("/app", EmbeddedResources::ORTHANC_EXPLORER);
+#else
+  FilesystemHttpHandler staticResources("/app", ORTHANC_PATH "/OrthancExplorer");
+#endif
+
+  context.GetHttpHandler().Register(staticResources, false);
+
+  // Thirdly, consider the built-in REST API of Orthanc
+  OrthancRestApi restApi(context);
+  context.GetHttpHandler().Register(restApi, true);
 
-  LoadLuaScripts(*context);
+  return StartDicomServer(context, restApi, plugins);
+}
+
+
+static bool UpgradeDatabase(IDatabaseWrapper& database,
+                            IStorageArea& storageArea,
+                            bool allowDatabaseUpgrade)
+{
+  // Upgrade the database, if needed
+  unsigned int currentVersion = database.GetDatabaseVersion();
+  if (currentVersion == ORTHANC_DATABASE_VERSION)
+  {
+    return true;
+  }
+
+  if (!allowDatabaseUpgrade)
+  {
+    LOG(ERROR) << "The database must be upgraded from version "
+               << currentVersion << " to " << ORTHANC_DATABASE_VERSION 
+               << ": Please run Orthanc with the \"--upgrade\" command-line option";
+    return false;
+  }
+
+  LOG(WARNING) << "Upgrading the database from version "
+               << currentVersion << " to " << ORTHANC_DATABASE_VERSION;
+  database.Upgrade(ORTHANC_DATABASE_VERSION, storageArea);
+    
+  // Sanity check
+  currentVersion = database.GetDatabaseVersion();
+  if (ORTHANC_DATABASE_VERSION != currentVersion)
+  {
+    LOG(ERROR) << "The database was not properly updated, it is still at version " << currentVersion;
+    throw OrthancException(ErrorCode_InternalError);
+  }
+
+  return true;
+}
+
+
+static bool ConfigureServerContext(IDatabaseWrapper& database,
+                                   IStorageArea& storageArea,
+                                   OrthancPlugins *plugins,
+                                   bool allowDatabaseUpgrade)
+{
+  if (!UpgradeDatabase(database, storageArea, allowDatabaseUpgrade))
+  {
+    return false;
+  }
+
+  ServerContext context(database, storageArea);
+
+  HttpClient::SetDefaultTimeout(Configuration::GetGlobalIntegerParameter("HttpTimeout", 0));
+  context.SetCompressionEnabled(Configuration::GetGlobalBoolParameter("StorageCompression", false));
+  context.SetStoreMD5ForAttachments(Configuration::GetGlobalBoolParameter("StoreMD5ForAttachments", true));
 
   try
   {
-    context->GetIndex().SetMaximumPatientCount(Configuration::GetGlobalIntegerParameter("MaximumPatientCount", 0));
+    context.GetIndex().SetMaximumPatientCount(Configuration::GetGlobalIntegerParameter("MaximumPatientCount", 0));
   }
   catch (...)
   {
-    context->GetIndex().SetMaximumPatientCount(0);
+    context.GetIndex().SetMaximumPatientCount(0);
   }
 
   try
   {
     uint64_t size = Configuration::GetGlobalIntegerParameter("MaximumStorageSize", 0);
-    context->GetIndex().SetMaximumStorageSize(size * 1024 * 1024);
+    context.GetIndex().SetMaximumStorageSize(size * 1024 * 1024);
   }
   catch (...)
   {
-    context->GetIndex().SetMaximumStorageSize(0);
+    context.GetIndex().SetMaximumStorageSize(0);
   }
 
-  MyDicomServerFactory serverFactory(*context);
-  bool isReset = false;
-    
-  {
-    // DICOM server
-    DicomServer dicomServer;
-    OrthancApplicationEntityFilter dicomFilter(*context);
-    dicomServer.SetCalledApplicationEntityTitleCheck(Configuration::GetGlobalBoolParameter("DicomCheckCalledAet", false));
-    dicomServer.SetStoreRequestHandlerFactory(serverFactory);
-    dicomServer.SetMoveRequestHandlerFactory(serverFactory);
-    dicomServer.SetFindRequestHandlerFactory(serverFactory);
-    dicomServer.SetPortNumber(Configuration::GetGlobalIntegerParameter("DicomPort", 4242));
-    dicomServer.SetApplicationEntityTitle(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC"));
-    dicomServer.SetApplicationEntityFilter(dicomFilter);
-
-    // HTTP server
-    MyIncomingHttpRequestFilter httpFilter(*context);
-    MongooseServer httpServer;
-    httpServer.SetPortNumber(Configuration::GetGlobalIntegerParameter("HttpPort", 8042));
-    httpServer.SetRemoteAccessAllowed(Configuration::GetGlobalBoolParameter("RemoteAccessAllowed", false));
-    httpServer.SetKeepAliveEnabled(Configuration::GetGlobalBoolParameter("KeepAlive", false));
-    httpServer.SetIncomingHttpRequestFilter(httpFilter);
-
-    httpServer.SetAuthenticationEnabled(Configuration::GetGlobalBoolParameter("AuthenticationEnabled", false));
-    Configuration::SetupRegisteredUsers(httpServer);
+  LoadLuaScripts(context);
 
-    if (Configuration::GetGlobalBoolParameter("SslEnabled", false))
-    {
-      std::string certificate = Configuration::InterpretStringParameterAsPath(
-        Configuration::GetGlobalStringParameter("SslCertificate", "certificate.pem"));
-      httpServer.SetSslEnabled(true);
-      httpServer.SetSslCertificate(certificate.c_str());
-    }
-    else
-    {
-      httpServer.SetSslEnabled(false);
-    }
-
-    OrthancRestApi restApi(*context);
-
-#if ORTHANC_STANDALONE == 1
-    EmbeddedResourceHttpHandler staticResources("/app", EmbeddedResources::ORTHANC_EXPLORER);
-#else
-    FilesystemHttpHandler staticResources("/app", ORTHANC_PATH "/OrthancExplorer");
-#endif
-
-#if ENABLE_PLUGINS == 1
-    orthancPlugins.SetServerContext(*context);
-    orthancPlugins.SetOrthancRestApi(restApi);
-    httpServer.RegisterHandler(orthancPlugins);
-    context->SetOrthancPlugins(pluginsManager, orthancPlugins);
+#if ORTHANC_PLUGINS_ENABLED == 1
+  if (plugins)
+  {
+    plugins->SetServerContext(context);
+    context.SetPlugins(*plugins);
+  }
 #endif
 
-    httpServer.RegisterHandler(staticResources);
-    httpServer.RegisterHandler(restApi);
-
-
-#if ENABLE_PLUGINS == 1
-    // Prepare the storage area
-    if (orthancPlugins.HasStorageArea())
-    {
-      LOG(WARNING) << "Using a custom storage area from plugins";
-      storage.reset(orthancPlugins.GetStorageArea());
-    }
-    else
-#endif
-    {
-      storage.reset(Configuration::CreateStorageArea());
-    }
-    
-    context->SetStorageArea(*storage);
-
+  bool restart = ConfigureHttpHandler(context, plugins);
+  context.Stop();
 
-    // GO !!! Start the requested servers
-    if (Configuration::GetGlobalBoolParameter("HttpServerEnabled", true))
-    {
-      httpServer.Start();
-      LOG(WARNING) << "HTTP server listening on port: " << httpServer.GetPortNumber();
-    }
-    else
-    {
-      LOG(WARNING) << "The HTTP server is disabled";
-    }
-
-    if (Configuration::GetGlobalBoolParameter("DicomServerEnabled", true))
-    {
-      dicomServer.Start();
-      LOG(WARNING) << "DICOM server listening on port: " << dicomServer.GetPortNumber();
-    }
-    else
-    {
-      LOG(WARNING) << "The DICOM server is disabled";
-    }
-
-    LOG(WARNING) << "Orthanc has started";
-    Toolbox::ServerBarrier(restApi.ResetRequestReceivedFlag());
-    isReset = restApi.ResetRequestReceivedFlag();
-
-    if (isReset)
-    {
-      LOG(WARNING) << "Reset request received, restarting Orthanc";
-    }
-
-    // We're done
-    LOG(WARNING) << "Orthanc is stopping";
-
-#if ENABLE_PLUGINS == 1
-    context->ResetOrthancPlugins();
-    orthancPlugins.Stop();
-    LOG(WARNING) << "    Plugins have stopped";
+#if ORTHANC_PLUGINS_ENABLED == 1
+  if (plugins)
+  {
+    context.ResetPlugins();
+  }
 #endif
 
-    dicomServer.Stop();
-    LOG(WARNING) << "    DICOM server has stopped";
-
-    httpServer.Stop();
-    LOG(WARNING) << "    HTTP server has stopped";
-  }
-
-  serverFactory.Done();
-
-  return isReset;
+  return restart;
 }
 
 
+static bool ConfigurePlugins(int argc, 
+                             char* argv[],
+                             bool allowDatabaseUpgrade)
+{
+  std::auto_ptr<IDatabaseWrapper>  databasePtr;
+  std::auto_ptr<IStorageArea>  storage;
+
+#if ORTHANC_PLUGINS_ENABLED == 1
+  OrthancPlugins plugins;
+  plugins.SetCommandLineArguments(argc, argv);
+  LoadPlugins(plugins);
+
+  IDatabaseWrapper* database = NULL;
+  if (plugins.HasDatabaseBackend())
+  {
+    LOG(WARNING) << "Using a custom database from plugins";
+    database = &plugins.GetDatabaseBackend();
+  }
+  else
+  {
+    databasePtr.reset(Configuration::CreateDatabaseWrapper());
+    database = databasePtr.get();
+  }
+
+  if (plugins.HasStorageArea())
+  {
+    LOG(WARNING) << "Using a custom storage area from plugins";
+    storage.reset(plugins.CreateStorageArea());
+  }
+  else
+  {
+    storage.reset(Configuration::CreateStorageArea());
+  }
+
+  assert(database != NULL);
+  assert(storage.get() != NULL);
+
+  return ConfigureServerContext(*database, *storage, &plugins, allowDatabaseUpgrade);
+
+#elif ORTHANC_PLUGINS_ENABLED == 0
+  // The plugins are disabled
+  databasePtr.reset(Configuration::CreateDatabaseWrapper());
+  storage.reset(Configuration::CreateStorageArea());
+
+  return ConfigureServerContext(*databasePtr, *storage, NULL, allowDatabaseUpgrade);
+
+#else
+#  error The macro ORTHANC_PLUGINS_ENABLED must be set to 0 or 1
+#endif
+}
+
+
+static bool StartOrthanc(int argc, 
+                         char* argv[],
+                         bool allowDatabaseUpgrade)
+{
+  return ConfigurePlugins(argc, argv, allowDatabaseUpgrade);
+}
 
 
 int main(int argc, char* argv[]) 
 {
-  // Initialize Google's logging library.
-  FLAGS_logtostderr = true;
-  FLAGS_minloglevel = 1;
-  FLAGS_v = 0;
+  Logging::Initialize();
+
+  bool allowDatabaseUpgrade = false;
 
   for (int i = 1; i < argc; i++)
   {
@@ -583,19 +787,32 @@
 
     if (std::string(argv[i]) == "--verbose")
     {
-      FLAGS_minloglevel = 0;
+      Logging::EnableInfoLevel(true);
     }
 
     if (std::string(argv[i]) == "--trace")
     {
-      FLAGS_minloglevel = 0;
-      FLAGS_v = 1;
+      Logging::EnableTraceLevel(true);
     }
 
     if (boost::starts_with(argv[i], "--logdir="))
     {
-      FLAGS_logtostderr = false;
-      FLAGS_log_dir = std::string(argv[i]).substr(9);
+      std::string directory = std::string(argv[i]).substr(9);
+
+      try
+      {
+        Logging::SetTargetFolder(directory);
+      }
+      catch (OrthancException&)
+      {
+        fprintf(stderr, "The directory where to store the log files (%s) is inexistent, aborting.\n", directory.c_str());
+        return -1;
+      }
+    }
+
+    if (std::string(argv[i]) == "--upgrade")
+    {
+      allowDatabaseUpgrade = true;
     }
 
     if (boost::starts_with(argv[i], "--config="))
@@ -616,8 +833,6 @@
     }
   }
 
-  google::InitGoogleLogging("Orthanc");
-
   const char* configurationFile = NULL;
   for (int i = 1; i < argc; i++)
   {
@@ -638,8 +853,8 @@
     {
       OrthancInitialize(configurationFile);
 
-      bool reset = StartOrthanc(argc, argv);
-      if (reset)
+      bool restart = StartOrthanc(argc, argv, allowDatabaseUpgrade);
+      if (restart)
       {
         OrthancFinalize();
       }
@@ -674,5 +889,7 @@
 
   LOG(WARNING) << "Orthanc has stopped";
 
+  Logging::Finalize();
+
   return status;
 }
--- a/Plugins/Engine/IPluginServiceProvider.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Engine/IPluginServiceProvider.h	Wed Sep 30 13:23:31 2015 +0200
@@ -32,9 +32,11 @@
 
 #pragma once
 
-#include "../Include/OrthancCPlugin.h"
+#if ORTHANC_PLUGINS_ENABLED == 1
 
-#include <boost/noncopyable.hpp>
+#include "../Include/orthanc/OrthancCPlugin.h"
+
+#include "SharedLibrary.h"
 
 namespace Orthanc
 {
@@ -45,7 +47,10 @@
     {
     }
 
-    virtual bool InvokeService(_OrthancPluginService service,
+    virtual bool InvokeService(SharedLibrary& plugin,
+                               _OrthancPluginService service,
                                const void* parameters) = 0;
   };
 }
+
+#endif
--- a/Plugins/Engine/OrthancPluginDatabase.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Engine/OrthancPluginDatabase.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -30,12 +30,19 @@
  **/
 
 
+#include "../../OrthancServer/PrecompiledHeadersServer.h"
 #include "OrthancPluginDatabase.h"
 
+#if ORTHANC_PLUGINS_ENABLED != 1
+#error The plugin support is disabled
+#endif
+
+
 #include "../../Core/OrthancException.h"
+#include "../../Core/Logging.h"
+#include "PluginsEnumerations.h"
 
 #include <cassert>
-#include <glog/logging.h>
 
 namespace Orthanc
 {
@@ -111,7 +118,7 @@
     if (type_ != _OrthancPluginDatabaseAnswerType_None &&
         type_ != _OrthancPluginDatabaseAnswerType_Int64)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
 
     target.clear();
@@ -119,7 +126,7 @@
     if (type_ == _OrthancPluginDatabaseAnswerType_Int64)
     {
       for (std::list<int64_t>::const_iterator 
-             it = answerInt64_.begin(); it != answerInt64_.end(); it++)
+             it = answerInt64_.begin(); it != answerInt64_.end(); ++it)
       {
         target.push_back(*it);
       }
@@ -132,7 +139,7 @@
     if (type_ != _OrthancPluginDatabaseAnswerType_None &&
         type_ != _OrthancPluginDatabaseAnswerType_String)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
 
     target.clear();
@@ -140,7 +147,7 @@
     if (type_ == _OrthancPluginDatabaseAnswerType_String)
     {
       for (std::list<std::string>::const_iterator 
-             it = answerStrings_.begin(); it != answerStrings_.end(); it++)
+             it = answerStrings_.begin(); it != answerStrings_.end(); ++it)
       {
         target.push_back(*it);
       }
@@ -162,7 +169,7 @@
     }
     else
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
   }
 
@@ -181,11 +188,40 @@
     }
     else
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
   }
 
 
+  OrthancPluginDatabase::OrthancPluginDatabase(SharedLibrary& library,
+                                               PluginsErrorDictionary&  errorDictionary,
+                                               const OrthancPluginDatabaseBackend& backend,
+                                               const OrthancPluginDatabaseExtensions* extensions,
+                                               size_t extensionsSize,
+                                               void *payload) : 
+    library_(library),
+    errorDictionary_(errorDictionary),
+    type_(_OrthancPluginDatabaseAnswerType_None),
+    backend_(backend),
+    payload_(payload),
+    listener_(NULL),
+    answerDicomMap_(NULL),
+    answerChanges_(NULL),
+    answerExportedResources_(NULL),
+    answerDone_(NULL)
+  {
+    memset(&extensions_, 0, sizeof(extensions_));
+
+    size_t size = sizeof(extensions_);
+    if (extensionsSize < size)
+    {
+      size = extensionsSize;  // Not all the extensions are available
+    }
+
+    memcpy(&extensions_, extensions, size);
+  }
+
+
   void OrthancPluginDatabase::AddAttachment(int64_t id,
                                             const FileInfo& attachment)
   {
@@ -198,9 +234,12 @@
     tmp.compressedSize = attachment.GetCompressedSize();
     tmp.compressedHash = attachment.GetCompressedMD5().c_str();
 
-    if (backend_.addAttachment(payload_, id, &tmp) != 0)
+    OrthancPluginErrorCode error = backend_.addAttachment(payload_, id, &tmp);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
@@ -208,27 +247,36 @@
   void OrthancPluginDatabase::AttachChild(int64_t parent,
                                           int64_t child)
   {
-    if (backend_.attachChild(payload_, parent, child) != 0)
+    OrthancPluginErrorCode error = backend_.attachChild(payload_, parent, child);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
 
   void OrthancPluginDatabase::ClearChanges()
   {
-    if (backend_.clearChanges(payload_) != 0)
+    OrthancPluginErrorCode error = backend_.clearChanges(payload_);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
 
   void OrthancPluginDatabase::ClearExportedResources()
   {
-    if (backend_.clearExportedResources(payload_) != 0)
+    OrthancPluginErrorCode error = backend_.clearExportedResources(payload_);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
@@ -238,9 +286,12 @@
   {
     int64_t id;
 
-    if (backend_.createResource(&id, payload_, publicId.c_str(), Convert(type)) != 0)
+    OrthancPluginErrorCode error = backend_.createResource(&id, payload_, publicId.c_str(), Convert(type));
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     return id;
@@ -250,9 +301,12 @@
   void OrthancPluginDatabase::DeleteAttachment(int64_t id,
                                                FileContentType attachment)
   {
-    if (backend_.deleteAttachment(payload_, id, static_cast<int32_t>(attachment)) != 0)
+    OrthancPluginErrorCode error = backend_.deleteAttachment(payload_, id, static_cast<int32_t>(attachment));
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
@@ -260,18 +314,24 @@
   void OrthancPluginDatabase::DeleteMetadata(int64_t id,
                                              MetadataType type)
   {
-    if (backend_.deleteMetadata(payload_, id, static_cast<int32_t>(type)) != 0)
+    OrthancPluginErrorCode error = backend_.deleteMetadata(payload_, id, static_cast<int32_t>(type));
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
 
   void OrthancPluginDatabase::DeleteResource(int64_t id)
   {
-    if (backend_.deleteResource(payload_, id) != 0)
+    OrthancPluginErrorCode error = backend_.deleteResource(payload_, id);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
@@ -285,12 +345,12 @@
     target.clear();
 
     for (std::list<MetadataType>::const_iterator
-           it = metadata.begin(); it != metadata.end(); it++)
+           it = metadata.begin(); it != metadata.end(); ++it)
     {
       std::string value;
       if (!LookupMetadata(value, id, *it))
       {
-        throw OrthancException(ErrorCode_Plugin);
+        throw OrthancException(ErrorCode_DatabasePlugin);
       }
 
       target[*it] = value;
@@ -303,15 +363,72 @@
   {
     ResetAnswers();
 
-    if (backend_.getAllPublicIds(GetContext(), payload_, Convert(resourceType)) != 0)
+    OrthancPluginErrorCode error = backend_.getAllPublicIds(GetContext(), payload_, Convert(resourceType));
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     ForwardAnswers(target);
   }
 
 
+  void OrthancPluginDatabase::GetAllPublicIds(std::list<std::string>& target,
+                                              ResourceType resourceType,
+                                              size_t since,
+                                              size_t limit)
+  {
+    if (extensions_.getAllPublicIdsWithLimit != NULL)
+    {
+      // This extension is available since Orthanc 0.9.4
+      ResetAnswers();
+
+      OrthancPluginErrorCode error = extensions_.getAllPublicIdsWithLimit
+        (GetContext(), payload_, Convert(resourceType), since, limit);
+
+      if (error != OrthancPluginErrorCode_Success)
+      {
+        errorDictionary_.LogError(error, true);
+        throw OrthancException(static_cast<ErrorCode>(error));
+      }
+
+      ForwardAnswers(target);
+    }
+    else
+    {
+      // The extension is not available in the database plugin, use a
+      // fallback implementation
+      target.clear();
+
+      if (limit == 0)
+      {
+        return;
+      }
+
+      std::list<std::string> tmp;
+      GetAllPublicIds(tmp, resourceType);
+    
+      if (tmp.size() <= since)
+      {
+        // Not enough results => empty answer
+        return;
+      }
+
+      std::list<std::string>::iterator current = tmp.begin();
+      std::advance(current, since);
+
+      while (limit > 0 && current != tmp.end())
+      {
+        target.push_back(*current);
+        --limit;
+        ++current;
+      }
+    }
+  }
+
+
 
   void OrthancPluginDatabase::GetChanges(std::list<ServerIndexChange>& target /*out*/,
                                          bool& done /*out*/,
@@ -323,9 +440,12 @@
     answerDone_ = &done;
     done = false;
 
-    if (backend_.getChanges(GetContext(), payload_, since, maxResults) != 0)
+    OrthancPluginErrorCode error = backend_.getChanges(GetContext(), payload_, since, maxResults);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
@@ -335,9 +455,12 @@
   {
     ResetAnswers();
 
-    if (backend_.getChildrenInternalId(GetContext(), payload_, id) != 0)
+    OrthancPluginErrorCode error = backend_.getChildrenInternalId(GetContext(), payload_, id);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     ForwardAnswers(target);
@@ -349,9 +472,12 @@
   {
     ResetAnswers();
 
-    if (backend_.getChildrenPublicId(GetContext(), payload_, id) != 0)
+    OrthancPluginErrorCode error = backend_.getChildrenPublicId(GetContext(), payload_, id);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     ForwardAnswers(target);
@@ -368,9 +494,12 @@
     answerDone_ = &done;
     done = false;
 
-    if (backend_.getExportedResources(GetContext(), payload_, since, maxResults) != 0)
+    OrthancPluginErrorCode error = backend_.getExportedResources(GetContext(), payload_, since, maxResults);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
@@ -383,9 +512,12 @@
     answerChanges_ = &target;
     answerDone_ = &ignored;
 
-    if (backend_.getLastChange(GetContext(), payload_) != 0)
+    OrthancPluginErrorCode error = backend_.getLastChange(GetContext(), payload_);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
@@ -398,9 +530,12 @@
     answerExportedResources_ = &target;
     answerDone_ = &ignored;
 
-    if (backend_.getLastExportedResource(GetContext(), payload_) != 0)
+    OrthancPluginErrorCode error = backend_.getLastExportedResource(GetContext(), payload_);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
@@ -411,9 +546,12 @@
     ResetAnswers();
     answerDicomMap_ = &map;
 
-    if (backend_.getMainDicomTags(GetContext(), payload_, id) != 0)
+    OrthancPluginErrorCode error = backend_.getMainDicomTags(GetContext(), payload_, id);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
@@ -423,10 +561,17 @@
     ResetAnswers();
     std::string s;
 
-    if (backend_.getPublicId(GetContext(), payload_, resourceId) != 0 ||
-        !ForwardSingleAnswer(s))
+    OrthancPluginErrorCode error = backend_.getPublicId(GetContext(), payload_, resourceId);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
+    }
+    
+    if (!ForwardSingleAnswer(s))
+    {
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
 
     return s;
@@ -437,9 +582,12 @@
   {
     uint64_t count;
 
-    if (backend_.getResourceCount(&count, payload_, Convert(resourceType)) != 0)
+    OrthancPluginErrorCode error = backend_.getResourceCount(&count, payload_, Convert(resourceType));
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     return count;
@@ -450,9 +598,12 @@
   {
     OrthancPluginResourceType type;
 
-    if (backend_.getResourceType(&type, payload_, resourceId) != 0)
+    OrthancPluginErrorCode error = backend_.getResourceType(&type, payload_, resourceId);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     return Convert(type);
@@ -463,9 +614,12 @@
   {
     uint64_t size;
 
-    if (backend_.getTotalCompressedSize(&size, payload_) != 0)
+    OrthancPluginErrorCode error = backend_.getTotalCompressedSize(&size, payload_);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     return size;
@@ -476,9 +630,12 @@
   {
     uint64_t size;
 
-    if (backend_.getTotalUncompressedSize(&size, payload_) != 0)
+    OrthancPluginErrorCode error = backend_.getTotalUncompressedSize(&size, payload_);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     return size;
@@ -489,12 +646,15 @@
   {
     int32_t existing;
 
-    if (backend_.isExistingResource(&existing, payload_, internalId) != 0)
+    OrthancPluginErrorCode error = backend_.isExistingResource(&existing, payload_, internalId);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
-    return existing;
+    return (existing != 0);
   }
 
 
@@ -502,12 +662,15 @@
   {
     int32_t isProtected;
 
-    if (backend_.isProtectedPatient(&isProtected, payload_, internalId) != 0)
+    OrthancPluginErrorCode error = backend_.isProtectedPatient(&isProtected, payload_, internalId);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
-    return isProtected;
+    return (isProtected != 0);
   }
 
 
@@ -516,15 +679,18 @@
   {
     ResetAnswers();
 
-    if (backend_.listAvailableMetadata(GetContext(), payload_, id) != 0)
+    OrthancPluginErrorCode error = backend_.listAvailableMetadata(GetContext(), payload_, id);
+ 
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     if (type_ != _OrthancPluginDatabaseAnswerType_None &&
         type_ != _OrthancPluginDatabaseAnswerType_Int32)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
 
     target.clear();
@@ -532,7 +698,7 @@
     if (type_ == _OrthancPluginDatabaseAnswerType_Int32)
     {
       for (std::list<int32_t>::const_iterator 
-             it = answerInt32_.begin(); it != answerInt32_.end(); it++)
+             it = answerInt32_.begin(); it != answerInt32_.end(); ++it)
       {
         target.push_back(static_cast<MetadataType>(*it));
       }
@@ -545,15 +711,18 @@
   {
     ResetAnswers();
 
-    if (backend_.listAvailableAttachments(GetContext(), payload_, id) != 0)
+    OrthancPluginErrorCode error = backend_.listAvailableAttachments(GetContext(), payload_, id);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     if (type_ != _OrthancPluginDatabaseAnswerType_None &&
         type_ != _OrthancPluginDatabaseAnswerType_Int32)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
 
     target.clear();
@@ -561,7 +730,7 @@
     if (type_ == _OrthancPluginDatabaseAnswerType_Int32)
     {
       for (std::list<int32_t>::const_iterator 
-             it = answerInt32_.begin(); it != answerInt32_.end(); it++)
+             it = answerInt32_.begin(); it != answerInt32_.end(); ++it)
       {
         target.push_back(static_cast<FileContentType>(*it));
       }
@@ -579,9 +748,12 @@
     tmp.publicId = change.GetPublicId().c_str();
     tmp.date = change.GetDate().c_str();
 
-    if (backend_.logChange(payload_, &tmp) != 0)
+    OrthancPluginErrorCode error = backend_.logChange(payload_, &tmp);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
@@ -599,9 +771,12 @@
     tmp.seriesInstanceUid = resource.GetSeriesInstanceUid().c_str();
     tmp.sopInstanceUid = resource.GetSopInstanceUid().c_str();
 
-    if (backend_.logExportedResource(payload_, &tmp) != 0)
+    OrthancPluginErrorCode error = backend_.logExportedResource(payload_, &tmp);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
@@ -612,9 +787,13 @@
   {
     ResetAnswers();
 
-    if (backend_.lookupAttachment(GetContext(), payload_, id, static_cast<int32_t>(contentType)))
+    OrthancPluginErrorCode error = backend_.lookupAttachment
+      (GetContext(), payload_, id, static_cast<int32_t>(contentType));
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     if (type_ == _OrthancPluginDatabaseAnswerType_None)
@@ -629,7 +808,7 @@
     }
     else
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
   }
 
@@ -639,10 +818,13 @@
   {
     ResetAnswers();
 
-    if (backend_.lookupGlobalProperty(GetContext(), payload_, 
-                                      static_cast<int32_t>(property)))
+    OrthancPluginErrorCode error = backend_.lookupGlobalProperty
+      (GetContext(), payload_, static_cast<int32_t>(property));
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     return ForwardSingleAnswer(target);
@@ -660,9 +842,12 @@
     tmp.element = tag.GetElement();
     tmp.value = value.c_str();
 
-    if (backend_.lookupIdentifier(GetContext(), payload_, &tmp) != 0)
+    OrthancPluginErrorCode error = backend_.lookupIdentifier(GetContext(), payload_, &tmp);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     ForwardAnswers(target);
@@ -674,9 +859,12 @@
   {
     ResetAnswers();
 
-    if (backend_.lookupIdentifier2(GetContext(), payload_, value.c_str()) != 0)
+    OrthancPluginErrorCode error = backend_.lookupIdentifier2(GetContext(), payload_, value.c_str());
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     ForwardAnswers(target);
@@ -689,9 +877,12 @@
   {
     ResetAnswers();
 
-    if (backend_.lookupMetadata(GetContext(), payload_, id, static_cast<int32_t>(type)))
+    OrthancPluginErrorCode error = backend_.lookupMetadata(GetContext(), payload_, id, static_cast<int32_t>(type));
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     return ForwardSingleAnswer(target);
@@ -703,9 +894,12 @@
   {
     ResetAnswers();
 
-    if (backend_.lookupParent(GetContext(), payload_, resourceId))
+    OrthancPluginErrorCode error = backend_.lookupParent(GetContext(), payload_, resourceId);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     return ForwardSingleAnswer(parentId);
@@ -718,9 +912,12 @@
   {
     ResetAnswers();
 
-    if (backend_.lookupResource(GetContext(), payload_, publicId.c_str()))
+    OrthancPluginErrorCode error = backend_.lookupResource(GetContext(), payload_, publicId.c_str());
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     if (type_ == _OrthancPluginDatabaseAnswerType_None)
@@ -736,7 +933,7 @@
     }
     else
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
   }
 
@@ -745,9 +942,12 @@
   {
     ResetAnswers();
 
-    if (backend_.selectPatientToRecycle(GetContext(), payload_))
+    OrthancPluginErrorCode error = backend_.selectPatientToRecycle(GetContext(), payload_);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     return ForwardSingleAnswer(internalId);
@@ -759,9 +959,12 @@
   {
     ResetAnswers();
 
-    if (backend_.selectPatientToRecycle2(GetContext(), payload_, patientIdToAvoid))
+    OrthancPluginErrorCode error = backend_.selectPatientToRecycle2(GetContext(), payload_, patientIdToAvoid);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
 
     return ForwardSingleAnswer(internalId);
@@ -771,10 +974,13 @@
   void OrthancPluginDatabase::SetGlobalProperty(GlobalProperty property,
                                                 const std::string& value)
   {
-    if (backend_.setGlobalProperty(payload_, static_cast<int32_t>(property), 
-                                   value.c_str()) != 0)
+    OrthancPluginErrorCode error = backend_.setGlobalProperty
+      (payload_, static_cast<int32_t>(property), value.c_str());
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
@@ -783,24 +989,26 @@
                                               const DicomTag& tag,
                                               const std::string& value)
   {
-    int32_t status;
     OrthancPluginDicomTag tmp;
     tmp.group = tag.GetGroup();
     tmp.element = tag.GetElement();
     tmp.value = value.c_str();
 
+    OrthancPluginErrorCode error;
+
     if (tag.IsIdentifier())
     {
-      status = backend_.setIdentifierTag(payload_, id, &tmp);
+      error = backend_.setIdentifierTag(payload_, id, &tmp);
     }
     else
     {
-      status = backend_.setMainDicomTag(payload_, id, &tmp);
+      error = backend_.setMainDicomTag(payload_, id, &tmp);
     }
 
-    if (status != 0)
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
@@ -809,10 +1017,13 @@
                                           MetadataType type,
                                           const std::string& value)
   {
-    if (backend_.setMetadata(payload_, id, static_cast<int32_t>(type), 
-                             value.c_str()) != 0)
+    OrthancPluginErrorCode error = backend_.setMetadata
+      (payload_, id, static_cast<int32_t>(type), value.c_str());
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
@@ -820,9 +1031,12 @@
   void OrthancPluginDatabase::SetProtectedPatient(int64_t internalId, 
                                                   bool isProtected)
   {
-    if (backend_.setProtectedPatient(payload_, internalId, isProtected) != 0)
+    OrthancPluginErrorCode error = backend_.setProtectedPatient(payload_, internalId, isProtected);
+
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      errorDictionary_.LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
@@ -832,36 +1046,48 @@
   private:
     const OrthancPluginDatabaseBackend& backend_;
     void* payload_;
+    PluginsErrorDictionary&  errorDictionary_;
 
   public:
     Transaction(const OrthancPluginDatabaseBackend& backend,
-                void* payload) :
+                void* payload,
+                PluginsErrorDictionary&  errorDictionary) :
       backend_(backend),
-      payload_(payload)
+      payload_(payload),
+      errorDictionary_(errorDictionary)
     {
     }
 
     virtual void Begin()
     {
-      if (backend_.startTransaction(payload_) != 0)
+      OrthancPluginErrorCode error = backend_.startTransaction(payload_);
+
+      if (error != OrthancPluginErrorCode_Success)
       {
-        throw OrthancException(ErrorCode_Plugin);
+        errorDictionary_.LogError(error, true);
+        throw OrthancException(static_cast<ErrorCode>(error));
       }
     }
 
     virtual void Rollback()
     {
-      if (backend_.rollbackTransaction(payload_) != 0)
+      OrthancPluginErrorCode error = backend_.rollbackTransaction(payload_);
+
+      if (error != OrthancPluginErrorCode_Success)
       {
-        throw OrthancException(ErrorCode_Plugin);
+        errorDictionary_.LogError(error, true);
+        throw OrthancException(static_cast<ErrorCode>(error));
       }
     }
 
     virtual void Commit()
     {
-      if (backend_.commitTransaction(payload_) != 0)
+      OrthancPluginErrorCode error = backend_.commitTransaction(payload_);
+
+      if (error != OrthancPluginErrorCode_Success)
       {
-        throw OrthancException(ErrorCode_Plugin);
+        errorDictionary_.LogError(error, true);
+        throw OrthancException(static_cast<ErrorCode>(error));
       }
     }
   };
@@ -869,11 +1095,11 @@
 
   SQLite::ITransaction* OrthancPluginDatabase::StartTransaction()
   {
-    return new Transaction(backend_, payload_);
+    return new Transaction(backend_, payload_, errorDictionary_);
   }
 
 
-  static void ProcessEvent(IServerIndexListener& listener,
+  static void ProcessEvent(IDatabaseListener& listener,
                            const _OrthancPluginDatabaseAnswer& answer)
   {
     switch (answer.type)
@@ -902,7 +1128,50 @@
       }
 
       default:
-        throw OrthancException(ErrorCode_Plugin);
+        throw OrthancException(ErrorCode_DatabasePlugin);
+    }
+  }
+
+
+  unsigned int OrthancPluginDatabase::GetDatabaseVersion()
+  {
+    if (extensions_.getDatabaseVersion != NULL)
+    {
+      uint32_t version;
+      OrthancPluginErrorCode error = extensions_.getDatabaseVersion(&version, payload_);
+
+      if (error != OrthancPluginErrorCode_Success)
+      {
+        errorDictionary_.LogError(error, true);
+        throw OrthancException(static_cast<ErrorCode>(error));
+      }
+
+      return version;
+    }
+    else
+    {
+      // Before adding the "GetDatabaseVersion()" extension in plugins
+      // (OrthancPostgreSQL <= 1.2), the only supported DB schema was
+      // version 5.
+      return 5;
+    }
+  }
+
+
+  void OrthancPluginDatabase::Upgrade(unsigned int targetVersion,
+                                      IStorageArea& storageArea)
+  {
+    if (extensions_.upgradeDatabase != NULL)
+    {
+      OrthancPluginErrorCode error = extensions_.upgradeDatabase(
+        payload_, targetVersion, 
+        reinterpret_cast<OrthancPluginStorageArea*>(&storageArea));
+
+      if (error != OrthancPluginErrorCode_Success)
+      {
+        errorDictionary_.LogError(error, true);
+        throw OrthancException(static_cast<ErrorCode>(error));
+      }
     }
   }
 
@@ -911,7 +1180,7 @@
   {
     if (answer.type == _OrthancPluginDatabaseAnswerType_None)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
 
     if (answer.type == _OrthancPluginDatabaseAnswerType_DeletedAttachment ||
@@ -966,13 +1235,13 @@
 
         default:
           LOG(ERROR) << "Unhandled type of answer for custom index plugin: " << answer.type;
-          throw OrthancException(ErrorCode_Plugin);
+          throw OrthancException(ErrorCode_DatabasePlugin);
       }
     }
     else if (type_ != answer.type)
     {
       LOG(ERROR) << "Error in the plugin protocol: Cannot change the answer type";
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
 
     switch (answer.type)
@@ -1017,7 +1286,7 @@
       {
         if (answer.valueString == NULL)
         {
-          throw OrthancException(ErrorCode_Plugin);
+          throw OrthancException(ErrorCode_DatabasePlugin);
         }
 
         if (type_ == _OrthancPluginDatabaseAnswerType_None)
@@ -1027,7 +1296,7 @@
         }
         else if (type_ != _OrthancPluginDatabaseAnswerType_String)
         {
-          throw OrthancException(ErrorCode_Plugin);
+          throw OrthancException(ErrorCode_DatabasePlugin);
         }
 
         answerStrings_.push_back(std::string(answer.valueString));
@@ -1043,7 +1312,7 @@
         }
         else if (*answerDone_)
         {
-          throw OrthancException(ErrorCode_Plugin);
+          throw OrthancException(ErrorCode_DatabasePlugin);
         }
         else
         {
@@ -1069,7 +1338,7 @@
         }
         else if (*answerDone_)
         {
-          throw OrthancException(ErrorCode_Plugin);
+          throw OrthancException(ErrorCode_DatabasePlugin);
         }
         else
         {
@@ -1093,7 +1362,7 @@
 
       default:
         LOG(ERROR) << "Unhandled type of answer for custom index plugin: " << answer.type;
-        throw OrthancException(ErrorCode_Plugin);
+        throw OrthancException(ErrorCode_DatabasePlugin);
     }
   }
 }
--- a/Plugins/Engine/OrthancPluginDatabase.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Engine/OrthancPluginDatabase.h	Wed Sep 30 13:23:31 2015 +0200
@@ -32,8 +32,12 @@
 
 #pragma once
 
+#if ORTHANC_PLUGINS_ENABLED == 1
+
 #include "../../OrthancServer/IDatabaseWrapper.h"
-#include "../Include/OrthancCDatabasePlugin.h"
+#include "../Include/orthanc/OrthancCDatabasePlugin.h"
+#include "PluginsErrorDictionary.h"
+#include "SharedLibrary.h"
 
 namespace Orthanc
 {
@@ -44,10 +48,13 @@
 
     typedef std::pair<int64_t, ResourceType>  AnswerResource;
 
+    SharedLibrary&  library_;
+    PluginsErrorDictionary&  errorDictionary_;
     _OrthancPluginDatabaseAnswerType type_;
     OrthancPluginDatabaseBackend backend_;
+    OrthancPluginDatabaseExtensions extensions_;
     void* payload_;
-    IServerIndexListener* listener_;
+    IDatabaseListener* listener_;
 
     std::list<std::string>         answerStrings_;
     std::list<int32_t>             answerInt32_;
@@ -76,13 +83,16 @@
     bool ForwardSingleAnswer(int64_t& target);
 
   public:
-    OrthancPluginDatabase(const OrthancPluginDatabaseBackend& backend,
-                          void *payload) : 
-    type_(_OrthancPluginDatabaseAnswerType_None),
-    backend_(backend),
-    payload_(payload),
-    listener_(NULL)
+    OrthancPluginDatabase(SharedLibrary& library,
+                          PluginsErrorDictionary&  errorDictionary,
+                          const OrthancPluginDatabaseBackend& backend,
+                          const OrthancPluginDatabaseExtensions* extensions,
+                          size_t extensionsSize,
+                          void *payload);
+
+    const SharedLibrary& GetSharedLibrary() const
     {
+      return library_;
     }
 
     virtual void AddAttachment(int64_t id,
@@ -121,6 +131,10 @@
     virtual void GetAllPublicIds(std::list<std::string>& target,
                                  ResourceType resourceType);
 
+    virtual void GetAllPublicIds(std::list<std::string>& target,
+                                 ResourceType resourceType,
+                                 size_t since,
+                                 size_t limit);
 
     virtual void GetChanges(std::list<ServerIndexChange>& target /*out*/,
                             bool& done /*out*/,
@@ -216,11 +230,18 @@
 
     virtual SQLite::ITransaction* StartTransaction();
 
-    virtual void SetListener(IServerIndexListener& listener)
+    virtual void SetListener(IDatabaseListener& listener)
     {
       listener_ = &listener;
     }
 
+    virtual unsigned int GetDatabaseVersion();
+
+    virtual void Upgrade(unsigned int targetVersion,
+                         IStorageArea& storageArea);
+
     void AnswerReceived(const _OrthancPluginDatabaseAnswer& answer);
   };
 }
+
+#endif
--- a/Plugins/Engine/OrthancPlugins.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Engine/OrthancPlugins.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -30,197 +30,236 @@
  **/
 
 
+#include "../../OrthancServer/PrecompiledHeadersServer.h"
 #include "OrthancPlugins.h"
 
+#if ORTHANC_PLUGINS_ENABLED != 1
+#error The plugin support is disabled
+#endif
+
+
 #include "../../Core/ChunkedBuffer.h"
+#include "../../Core/HttpServer/HttpToolbox.h"
+#include "../../Core/Logging.h"
 #include "../../Core/OrthancException.h"
 #include "../../Core/Toolbox.h"
-#include "../../Core/HttpServer/HttpOutput.h"
-#include "../../Core/ImageFormats/PngWriter.h"
-#include "../../OrthancServer/ServerToolbox.h"
+#include "../../OrthancServer/FromDcmtkBridge.h"
 #include "../../OrthancServer/OrthancInitialization.h"
-#include "../../Core/MultiThreading/SharedMessageQueue.h"
+#include "../../OrthancServer/ServerContext.h"
+#include "../../OrthancServer/ServerToolbox.h"
+#include "../../Core/Compression/ZlibCompressor.h"
+#include "../../Core/Compression/GzipCompressor.h"
+#include "../../Core/Images/Image.h"
+#include "../../Core/Images/PngReader.h"
+#include "../../Core/Images/PngWriter.h"
+#include "../../Core/Images/JpegReader.h"
+#include "../../Core/Images/JpegWriter.h"
+#include "../../Core/Images/ImageProcessing.h"
+#include "PluginsEnumerations.h"
 
-#include <boost/thread.hpp>
 #include <boost/regex.hpp> 
-#include <glog/logging.h>
 
 namespace Orthanc
 {
-  static OrthancPluginResourceType Convert(ResourceType type)
+  namespace
   {
-    switch (type)
+    class PluginStorageArea : public IStorageArea
     {
-      case ResourceType_Patient:
-        return OrthancPluginResourceType_Patient;
-
-      case ResourceType_Study:
-        return OrthancPluginResourceType_Study;
+    private:
+      _OrthancPluginRegisterStorageArea callbacks_;
+      PluginsErrorDictionary&  errorDictionary_;
 
-      case ResourceType_Series:
-        return OrthancPluginResourceType_Series;
+      void Free(void* buffer) const
+      {
+        if (buffer != NULL)
+        {
+          callbacks_.free(buffer);
+        }
+      }
 
-      case ResourceType_Instance:
-        return OrthancPluginResourceType_Instance;
-
-      default:
-        throw OrthancException(ErrorCode_ParameterOutOfRange);
-    }
-  }
+    public:
+      PluginStorageArea(const _OrthancPluginRegisterStorageArea& callbacks,
+                        PluginsErrorDictionary&  errorDictionary) : 
+        callbacks_(callbacks),
+        errorDictionary_(errorDictionary)
+      {
+      }
 
 
-  static OrthancPluginChangeType Convert(ChangeType type)
-  {
-    switch (type)
-    {
-      case ChangeType_CompletedSeries:
-        return OrthancPluginChangeType_CompletedSeries;
-
-      case ChangeType_Deleted:
-        return OrthancPluginChangeType_Deleted;
-
-      case ChangeType_NewChildInstance:
-        return OrthancPluginChangeType_NewChildInstance;
-
-      case ChangeType_NewInstance:
-        return OrthancPluginChangeType_NewInstance;
-
-      case ChangeType_NewPatient:
-        return OrthancPluginChangeType_NewPatient;
-
-      case ChangeType_NewSeries:
-        return OrthancPluginChangeType_NewSeries;
-
-      case ChangeType_NewStudy:
-        return OrthancPluginChangeType_NewStudy;
-
-      case ChangeType_StablePatient:
-        return OrthancPluginChangeType_StablePatient;
+      virtual void Create(const std::string& uuid,
+                          const void* content, 
+                          size_t size,
+                          FileContentType type)
+      {
+        OrthancPluginErrorCode error = callbacks_.create
+          (uuid.c_str(), content, size, Plugins::Convert(type));
 
-      case ChangeType_StableSeries:
-        return OrthancPluginChangeType_StableSeries;
-
-      case ChangeType_StableStudy:
-        return OrthancPluginChangeType_StableStudy;
-
-      default:
-        throw OrthancException(ErrorCode_ParameterOutOfRange);
-    }
-  }
-
-
-  namespace
-  {
-    // Anonymous namespace to avoid clashes between compilation modules
-    class StringHttpOutput : public IHttpOutputStream
-    {
-    private:
-      ChunkedBuffer buffer_;
-
-    public:
-      void GetOutput(std::string& output)
-      {
-        buffer_.Flatten(output);
-      }
-
-      virtual void OnHttpStatusReceived(HttpStatus status)
-      {
-        if (status != HttpStatus_200_Ok)
+        if (error != OrthancPluginErrorCode_Success)
         {
-          throw OrthancException(ErrorCode_BadRequest);
+          errorDictionary_.LogError(error, true);
+          throw OrthancException(static_cast<ErrorCode>(error));
         }
       }
 
-      virtual void Send(bool isHeader, const void* buffer, size_t length)
+
+      virtual void Read(std::string& content,
+                        const std::string& uuid,
+                        FileContentType type)
       {
-        if (!isHeader)
+        void* buffer = NULL;
+        int64_t size = 0;
+
+        OrthancPluginErrorCode error = callbacks_.read
+          (&buffer, &size, uuid.c_str(), Plugins::Convert(type));
+
+        if (error != OrthancPluginErrorCode_Success)
+        {
+          errorDictionary_.LogError(error, true);
+          throw OrthancException(static_cast<ErrorCode>(error));
+        }
+
+        try
+        {
+          content.resize(static_cast<size_t>(size));
+        }
+        catch (...)
         {
-          buffer_.AddChunk(reinterpret_cast<const char*>(buffer), length);
+          Free(buffer);
+          throw OrthancException(ErrorCode_NotEnoughMemory);
+        }
+
+        if (size > 0)
+        {
+          memcpy(&content[0], buffer, static_cast<size_t>(size));
+        }
+
+        Free(buffer);
+      }
+
+
+      virtual void Remove(const std::string& uuid,
+                          FileContentType type) 
+      {
+        OrthancPluginErrorCode error = callbacks_.remove
+          (uuid.c_str(), Plugins::Convert(type));
+
+        if (error != OrthancPluginErrorCode_Success)
+        {
+          errorDictionary_.LogError(error, true);
+          throw OrthancException(static_cast<ErrorCode>(error));
         }
       }
     };
 
 
-    class PendingChange : public IDynamicObject
+    class StorageAreaFactory : public boost::noncopyable
     {
     private:
-      OrthancPluginChangeType changeType_;
-      OrthancPluginResourceType resourceType_;
-      std::string publicId_;
+      SharedLibrary&   sharedLibrary_;
+      _OrthancPluginRegisterStorageArea  callbacks_;
+      PluginsErrorDictionary&  errorDictionary_;
 
     public:
-      PendingChange(const ServerIndexChange& change)
+      StorageAreaFactory(SharedLibrary& sharedLibrary,
+                         const _OrthancPluginRegisterStorageArea& callbacks,
+                         PluginsErrorDictionary&  errorDictionary) :
+        sharedLibrary_(sharedLibrary),
+        callbacks_(callbacks),
+        errorDictionary_(errorDictionary)
       {
-        changeType_ = Convert(change.GetChangeType());
-        resourceType_ = Convert(change.GetResourceType());
-        publicId_ = change.GetPublicId();
       }
 
-      void Submit(std::list<OrthancPluginOnChangeCallback>& callbacks)
+      SharedLibrary&  GetSharedLibrary()
       {
-        for (std::list<OrthancPluginOnChangeCallback>::const_iterator 
-               callback = callbacks.begin(); 
-             callback != callbacks.end(); ++callback)
-        {
-          (*callback) (changeType_, resourceType_, publicId_.c_str());
-        }
+        return sharedLibrary_;
+      }
+
+      IStorageArea* Create() const
+      {
+        return new PluginStorageArea(callbacks_, errorDictionary_);
       }
     };
   }
 
 
-
   struct OrthancPlugins::PImpl
   {
-    typedef std::pair<std::string, _OrthancPluginProperty>  Property;
+    class RestCallback : public boost::noncopyable
+    {
+    private:
+      boost::regex              regex_;
+      OrthancPluginRestCallback callback_;
+      bool                      lock_;
+
+      OrthancPluginErrorCode InvokeInternal(HttpOutput& output,
+                                            const std::string& flatUri,
+                                            const OrthancPluginHttpRequest& request)
+      {
+        return callback_(reinterpret_cast<OrthancPluginRestOutput*>(&output), 
+                         flatUri.c_str(), 
+                         &request);
+      }
+
+    public:
+      RestCallback(const char* regex,
+                   OrthancPluginRestCallback callback,
+                   bool lockRestCallbacks) :
+        regex_(regex),
+        callback_(callback),
+        lock_(lockRestCallbacks)
+      {
+      }
 
-    typedef std::pair<boost::regex*, OrthancPluginRestCallback> RestCallback;
-    typedef std::list<RestCallback>  RestCallbacks;
+      const boost::regex& GetRegularExpression() const
+      {
+        return regex_;
+      }
+
+      OrthancPluginErrorCode Invoke(boost::recursive_mutex& restCallbackMutex,
+                                    HttpOutput& output,
+                                    const std::string& flatUri,
+                                    const OrthancPluginHttpRequest& request)
+      {
+        if (lock_)
+        {
+          boost::recursive_mutex::scoped_lock lock(restCallbackMutex);
+          return InvokeInternal(output, flatUri, request);
+        }
+        else
+        {
+          return InvokeInternal(output, flatUri, request);
+        }
+      }
+    };
+
+
+    typedef std::pair<std::string, _OrthancPluginProperty>  Property;
+    typedef std::list<RestCallback*>  RestCallbacks;
     typedef std::list<OrthancPluginOnStoredInstanceCallback>  OnStoredCallbacks;
     typedef std::list<OrthancPluginOnChangeCallback>  OnChangeCallbacks;
     typedef std::map<Property, std::string>  Properties;
 
+    PluginsManager manager_;
     ServerContext* context_;
     RestCallbacks restCallbacks_;
-    OrthancRestApi* restApi_;
     OnStoredCallbacks  onStoredCallbacks_;
     OnChangeCallbacks  onChangeCallbacks_;
-    bool hasStorageArea_;
-    _OrthancPluginRegisterStorageArea storageArea_;
-    boost::recursive_mutex callbackMutex_;
-    SharedMessageQueue  pendingChanges_;
-    boost::thread  changeThread_;
-    bool done_;
+    std::auto_ptr<StorageAreaFactory>  storageArea_;
+    boost::recursive_mutex restCallbackMutex_;
+    boost::recursive_mutex storedCallbackMutex_;
+    boost::recursive_mutex changeCallbackMutex_;
+    boost::recursive_mutex invokeServiceMutex_;
     Properties properties_;
     int argc_;
     char** argv_;
     std::auto_ptr<OrthancPluginDatabase>  database_;
+    PluginsErrorDictionary  dictionary_;
 
     PImpl() : 
       context_(NULL), 
-      restApi_(NULL),
-      hasStorageArea_(false),
-      done_(false),
       argc_(1),
       argv_(NULL)
     {
-      memset(&storageArea_, 0, sizeof(storageArea_));
-    }
-
-
-    static void ChangeThread(PImpl* that)
-    {
-      while (!that->done_)
-      {
-        std::auto_ptr<IDynamicObject> obj(that->pendingChanges_.Dequeue(500));
-        
-        if (obj.get() != NULL)
-        {
-          boost::recursive_mutex::scoped_lock lock(that->callbackMutex_);
-          PendingChange& change = *dynamic_cast<PendingChange*>(obj.get());
-          change.Submit(that->onChangeCallbacks_);
-        }
-      }
     }
   };
 
@@ -249,8 +288,25 @@
 
   OrthancPlugins::OrthancPlugins()
   {
+    if (sizeof(int32_t) != sizeof(OrthancPluginErrorCode) ||
+        sizeof(int32_t) != sizeof(OrthancPluginHttpMethod) ||
+        sizeof(int32_t) != sizeof(_OrthancPluginService) ||
+        sizeof(int32_t) != sizeof(_OrthancPluginProperty) ||
+        sizeof(int32_t) != sizeof(OrthancPluginPixelFormat) ||
+        sizeof(int32_t) != sizeof(OrthancPluginContentType) ||
+        sizeof(int32_t) != sizeof(OrthancPluginResourceType) ||
+        sizeof(int32_t) != sizeof(OrthancPluginChangeType) ||
+        sizeof(int32_t) != sizeof(OrthancPluginImageFormat) ||
+        sizeof(int32_t) != sizeof(OrthancPluginCompressionType) ||
+        sizeof(int32_t) != sizeof(OrthancPluginValueRepresentation) ||
+        sizeof(int32_t) != sizeof(_OrthancPluginDatabaseAnswerType))
+    {
+      /* Sanity check of the compiler */
+      throw OrthancException(ErrorCode_Plugin);
+    }
+
     pimpl_.reset(new PImpl());
-    pimpl_->changeThread_ = boost::thread(PImpl::ChangeThread, pimpl_.get());
+    pimpl_->manager_.RegisterServiceProvider(*this);
   }
 
   
@@ -263,37 +319,23 @@
   
   OrthancPlugins::~OrthancPlugins()
   {
-    Stop();
-
     for (PImpl::RestCallbacks::iterator it = pimpl_->restCallbacks_.begin(); 
          it != pimpl_->restCallbacks_.end(); ++it)
     {
-      // Delete the regular expression associated with this callback
-      delete it->first;
+      delete *it;
     }
   }
 
 
-  void OrthancPlugins::Stop()
-  {
-    if (!pimpl_->done_)
-    {
-      pimpl_->done_ = true;
-      pimpl_->changeThread_.join();
-    }
-  }
-
-
-
   static void ArgumentsToPlugin(std::vector<const char*>& keys,
                                 std::vector<const char*>& values,
-                                const HttpHandler::Arguments& arguments)
+                                const IHttpHandler::Arguments& arguments)
   {
     keys.resize(arguments.size());
     values.resize(arguments.size());
 
     size_t pos = 0;
-    for (HttpHandler::Arguments::const_iterator 
+    for (IHttpHandler::Arguments::const_iterator 
            it = arguments.begin(); it != arguments.end(); ++it)
     {
       keys[pos] = it->first.c_str();
@@ -303,15 +345,34 @@
   }
 
 
+  static void ArgumentsToPlugin(std::vector<const char*>& keys,
+                                std::vector<const char*>& values,
+                                const IHttpHandler::GetArguments& arguments)
+  {
+    keys.resize(arguments.size());
+    values.resize(arguments.size());
+
+    for (size_t i = 0; i < arguments.size(); i++)
+    {
+      keys[i] = arguments[i].first.c_str();
+      values[i] = arguments[i].second.c_str();
+    }
+  }
+
+
   bool OrthancPlugins::Handle(HttpOutput& output,
-                                  HttpMethod method,
-                                  const UriComponents& uri,
-                                  const Arguments& headers,
-                                  const Arguments& getArguments,
-                                  const std::string& postData)
+                              RequestOrigin /*origin*/,
+                              const char* /*remoteIp*/,
+                              const char* /*username*/,
+                              HttpMethod method,
+                              const UriComponents& uri,
+                              const Arguments& headers,
+                              const GetArguments& getArguments,
+                              const char* bodyData,
+                              size_t bodySize)
   {
     std::string flatUri = Toolbox::FlattenUri(uri);
-    OrthancPluginRestCallback callback = NULL;
+    PImpl::RestCallback* callback = NULL;
 
     std::vector<std::string> groups;
     std::vector<const char*> cgroups;
@@ -324,9 +385,9 @@
       // Check whether the regular expression associated to this
       // callback matches the URI
       boost::cmatch what;
-      if (boost::regex_match(flatUri.c_str(), what, *(it->first)))
+      if (boost::regex_match(flatUri.c_str(), what, (*it)->GetRegularExpression()))
       {
-        callback = it->second;
+        callback = *it;
 
         // Extract the value of the free parameters of the regular expression
         if (what.size() > 1)
@@ -339,13 +400,12 @@
             cgroups[i - 1] = groups[i - 1].c_str();
           }
         }
-
-        found = true;
       }
     }
 
-    if (!found)
+    if (callback == NULL)
     {
+      // Callback not found
       return false;
     }
 
@@ -385,8 +445,8 @@
     request.groups = (cgroups.size() ? &cgroups[0] : NULL);
     request.groupsCount = cgroups.size();
     request.getCount = getArguments.size();
-    request.body = (postData.size() ? &postData[0] : NULL);
-    request.bodySize = postData.size();
+    request.body = bodyData;
+    request.bodySize = bodySize;
     request.headersCount = headers.size();
     
     if (getArguments.size() > 0)
@@ -402,43 +462,45 @@
     }
 
     assert(callback != NULL);
-    int32_t error;
+    OrthancPluginErrorCode error = callback->Invoke(pimpl_->restCallbackMutex_, output, flatUri, request);
 
+    if (error == OrthancPluginErrorCode_Success && 
+        output.IsWritingMultipart())
     {
-      boost::recursive_mutex::scoped_lock lock(pimpl_->callbackMutex_);
-      error = callback(reinterpret_cast<OrthancPluginRestOutput*>(&output), 
-                       flatUri.c_str(), 
-                       &request);
+      output.CloseMultipart();
     }
 
-    if (error < 0)
+    if (error == OrthancPluginErrorCode_Success)
     {
-      LOG(ERROR) << "Plugin callback failed with error code " << error;
-      return false;
+      return true;
     }
     else
     {
-      if (error > 0)
-      {
-        LOG(WARNING) << "Plugin callback finished with warning code " << error;
-      }
-
-      return true;
+      GetErrorDictionary().LogError(error, true);
+      throw OrthancException(static_cast<ErrorCode>(error));
     }
   }
 
 
-  void OrthancPlugins::SignalStoredInstance(DicomInstanceToStore& instance,
-                                            const std::string& instanceId)                                                  
+  void OrthancPlugins::SignalStoredInstance(const std::string& instanceId,
+                                            DicomInstanceToStore& instance,
+                                            const Json::Value& simplifiedTags)
   {
-    boost::recursive_mutex::scoped_lock lock(pimpl_->callbackMutex_);
+    boost::recursive_mutex::scoped_lock lock(pimpl_->storedCallbackMutex_);
 
     for (PImpl::OnStoredCallbacks::const_iterator
            callback = pimpl_->onStoredCallbacks_.begin(); 
          callback != pimpl_->onStoredCallbacks_.end(); ++callback)
     {
-      (*callback) (reinterpret_cast<OrthancPluginDicomInstance*>(&instance),
-                   instanceId.c_str());
+      OrthancPluginErrorCode error = (*callback) 
+        (reinterpret_cast<OrthancPluginDicomInstance*>(&instance),
+         instanceId.c_str());
+
+      if (error != OrthancPluginErrorCode_Success)
+      {
+        GetErrorDictionary().LogError(error, true);
+        throw OrthancException(static_cast<ErrorCode>(error));
+      }
     }
   }
 
@@ -446,14 +508,22 @@
 
   void OrthancPlugins::SignalChange(const ServerIndexChange& change)
   {
-    try
+    boost::recursive_mutex::scoped_lock lock(pimpl_->changeCallbackMutex_);
+
+    for (std::list<OrthancPluginOnChangeCallback>::const_iterator 
+           callback = pimpl_->onChangeCallbacks_.begin(); 
+         callback != pimpl_->onChangeCallbacks_.end(); ++callback)
     {
-      pimpl_->pendingChanges_.Enqueue(new PendingChange(change));
-    }
-    catch (OrthancException&)
-    {
-      // This change type or resource type is not supported by the plugin SDK
-      return;
+      OrthancPluginErrorCode error = (*callback)
+        (Plugins::Convert(change.GetChangeType()),
+         Plugins::Convert(change.GetResourceType()),
+         change.GetPublicId().c_str());
+
+      if (error != OrthancPluginErrorCode_Success)
+      {
+        GetErrorDictionary().LogError(error, true);
+        throw OrthancException(static_cast<ErrorCode>(error));
+      }
     }
   }
 
@@ -499,13 +569,18 @@
   }
 
 
-  void OrthancPlugins::RegisterRestCallback(const void* parameters)
+  void OrthancPlugins::RegisterRestCallback(const void* parameters,
+                                            bool lock)
   {
     const _OrthancPluginRestCallback& p = 
       *reinterpret_cast<const _OrthancPluginRestCallback*>(parameters);
 
-    LOG(INFO) << "Plugin has registered a REST callback on: " << p.pathRegularExpression;
-    pimpl_->restCallbacks_.push_back(std::make_pair(new boost::regex(p.pathRegularExpression), p.callback));
+    LOG(INFO) << "Plugin has registered a REST callback "
+              << (lock ? "with" : "witout")
+              << " mutual exclusion on: " 
+              << p.pathRegularExpression;
+
+    pimpl_->restCallbacks_.push_back(new PImpl::RestCallback(p.pathRegularExpression, p.callback, lock));
   }
 
 
@@ -538,7 +613,7 @@
 
     HttpOutput* translatedOutput = reinterpret_cast<HttpOutput*>(p.output);
     translatedOutput->SetContentType(p.mimeType);
-    translatedOutput->SendBody(p.answer, p.answerSize);
+    translatedOutput->Answer(p.answer, p.answerSize);
   }
 
 
@@ -562,6 +637,25 @@
   }
 
 
+  void OrthancPlugins::SendHttpStatus(const void* parameters)
+  {
+    const _OrthancPluginSendHttpStatus& p = 
+      *reinterpret_cast<const _OrthancPluginSendHttpStatus*>(parameters);
+
+    HttpOutput* translatedOutput = reinterpret_cast<HttpOutput*>(p.output);
+    HttpStatus status = static_cast<HttpStatus>(p.status);
+
+    if (p.bodySize > 0 && p.body != NULL)
+    {
+      translatedOutput->SendStatus(status, p.body, p.bodySize);
+    }
+    else
+    {
+      translatedOutput->SendStatus(status);
+    }
+  }
+
+
   void OrthancPlugins::SendUnauthorized(const void* parameters)
   {
     const _OrthancPluginOutputPlusArgument& p = 
@@ -604,59 +698,82 @@
 
   void OrthancPlugins::CompressAndAnswerPngImage(const void* parameters)
   {
+    // Bridge for backward compatibility with Orthanc <= 0.9.3
     const _OrthancPluginCompressAndAnswerPngImage& p = 
       *reinterpret_cast<const _OrthancPluginCompressAndAnswerPngImage*>(parameters);
 
+    _OrthancPluginCompressAndAnswerImage p2;
+    p2.output = p.output;
+    p2.imageFormat = OrthancPluginImageFormat_Png;
+    p2.pixelFormat = p.format;
+    p2.width = p.width;
+    p2.height = p.height;
+    p2.pitch = p.height;
+    p2.buffer = p.buffer;
+    p2.quality = 0;
+
+    CompressAndAnswerImage(&p2);
+  }
+
+
+  void OrthancPlugins::CompressAndAnswerImage(const void* parameters)
+  {
+    const _OrthancPluginCompressAndAnswerImage& p = 
+      *reinterpret_cast<const _OrthancPluginCompressAndAnswerImage*>(parameters);
+
     HttpOutput* translatedOutput = reinterpret_cast<HttpOutput*>(p.output);
 
-    PixelFormat format;
-    switch (p.format)
-    {
-      case OrthancPluginPixelFormat_Grayscale8:  
-        format = PixelFormat_Grayscale8;
-        break;
+    ImageAccessor accessor;
+    accessor.AssignReadOnly(Plugins::Convert(p.pixelFormat), p.width, p.height, p.pitch, p.buffer);
 
-      case OrthancPluginPixelFormat_Grayscale16:  
-        format = PixelFormat_Grayscale16;
-        break;
+    std::string compressed;
 
-      case OrthancPluginPixelFormat_SignedGrayscale16:  
-        format = PixelFormat_SignedGrayscale16;
+    switch (p.imageFormat)
+    {
+      case OrthancPluginImageFormat_Png:
+      {
+        PngWriter writer;
+        writer.WriteToMemory(compressed, accessor);
+        translatedOutput->SetContentType("image/png");
         break;
+      }
 
-      case OrthancPluginPixelFormat_RGB24:  
-        format = PixelFormat_RGB24;
+      case OrthancPluginImageFormat_Jpeg:
+      {
+        JpegWriter writer;
+        writer.SetQuality(p.quality);
+        writer.WriteToMemory(compressed, accessor);
+        translatedOutput->SetContentType("image/jpeg");
         break;
-
-      case OrthancPluginPixelFormat_RGBA32:  
-        format = PixelFormat_RGBA32;
-        break;
+      }
 
       default:
         throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
 
-    ImageAccessor accessor;
-    accessor.AssignReadOnly(format, p.width, p.height, p.pitch, p.buffer);
+    translatedOutput->Answer(compressed);
+  }
+
 
-    PngWriter writer;
-    std::string png;
-    writer.WriteToMemory(png, accessor);
-
-    translatedOutput->SetContentType("image/png");
-    translatedOutput->SendBody(png);
+  void OrthancPlugins::CheckContextAvailable()
+  {
+    if (!pimpl_->context_)
+    {
+      throw OrthancException(ErrorCode_DatabaseNotInitialized);
+    }
   }
 
 
   void OrthancPlugins::GetDicomForInstance(const void* parameters)
   {
-    assert(pimpl_->context_ != NULL);
-
     const _OrthancPluginGetDicomForInstance& p = 
       *reinterpret_cast<const _OrthancPluginGetDicomForInstance*>(parameters);
 
     std::string dicom;
+
+    CheckContextAvailable();
     pimpl_->context_->ReadFile(dicom, p.instanceId, FileContentType_Dicom);
+
     CopyToMemoryBuffer(*p.target, dicom);
   }
 
@@ -667,36 +784,15 @@
     const _OrthancPluginRestApiGet& p = 
       *reinterpret_cast<const _OrthancPluginRestApiGet*>(parameters);
         
-    HttpHandler::Arguments headers;  // No HTTP header
-    std::string body;  // No body for a GET request
-
-    UriComponents uri;
-    HttpHandler::Arguments getArguments;
-    HttpHandler::ParseGetQuery(uri, getArguments, p.uri);
-
-    StringHttpOutput stream;
-    HttpOutput http(stream, false /* no keep alive */);
-
     LOG(INFO) << "Plugin making REST GET call on URI " << p.uri
               << (afterPlugins ? " (after plugins)" : " (built-in API)");
 
-    bool ok = false;
-    std::string result;
-
-    if (afterPlugins)
-    {
-      ok = Handle(http, HttpMethod_Get, uri, headers, getArguments, body);
-    }
+    CheckContextAvailable();
+    IHttpHandler& handler = pimpl_->context_->GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
 
-    if (!ok)
+    std::string result;
+    if (HttpToolbox::SimpleGet(result, handler, RequestOrigin_Plugins, p.uri))
     {
-      ok = (pimpl_->restApi_ != NULL &&
-            pimpl_->restApi_->Handle(http, HttpMethod_Get, uri, headers, getArguments, body));
-    }
-
-    if (ok)
-    {
-      stream.GetOutput(result);
       CopyToMemoryBuffer(*p.target, result);
     }
     else
@@ -713,39 +809,17 @@
     const _OrthancPluginRestApiPostPut& p = 
       *reinterpret_cast<const _OrthancPluginRestApiPostPut*>(parameters);
 
-    HttpHandler::Arguments headers;  // No HTTP header
-    HttpHandler::Arguments getArguments;  // No GET argument for POST/PUT
-
-    UriComponents uri;
-    Toolbox::SplitUriComponents(uri, p.uri);
+    LOG(INFO) << "Plugin making REST " << EnumerationToString(isPost ? HttpMethod_Post : HttpMethod_Put)
+              << " call on URI " << p.uri << (afterPlugins ? " (after plugins)" : " (built-in API)");
 
-    // TODO Avoid unecessary memcpy
-    std::string body(p.body, p.bodySize);
-
-    StringHttpOutput stream;
-    HttpOutput http(stream, false /* no keep alive */);
-
-    HttpMethod method = (isPost ? HttpMethod_Post : HttpMethod_Put);
-    LOG(INFO) << "Plugin making REST " << EnumerationToString(method) << " call on URI " << p.uri
-              << (afterPlugins ? " (after plugins)" : " (built-in API)");
+    CheckContextAvailable();
+    IHttpHandler& handler = pimpl_->context_->GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
 
-    bool ok = false;
     std::string result;
-
-    if (afterPlugins)
+    if (isPost ? 
+        HttpToolbox::SimplePost(result, handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize) :
+        HttpToolbox::SimplePut (result, handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize))
     {
-      ok = Handle(http, method, uri, headers, getArguments, body);
-    }
-    
-    if (!ok)
-    {
-      ok = (pimpl_->restApi_ != NULL &&
-            pimpl_->restApi_->Handle(http, method, uri, headers, getArguments, body));
-    }
-
-    if (ok)
-    {
-      stream.GetOutput(result);
       CopyToMemoryBuffer(*p.target, result);
     }
     else
@@ -758,35 +832,14 @@
   void OrthancPlugins::RestApiDelete(const void* parameters,
                                      bool afterPlugins)
   {
-    // The "parameters" point to the URI
-    UriComponents uri;
-    Toolbox::SplitUriComponents(uri, reinterpret_cast<const char*>(parameters));
-
-    HttpHandler::Arguments headers;  // No HTTP header
-    HttpHandler::Arguments getArguments;  // No GET argument for POST/PUT
-    std::string body;  // No body for DELETE
-
-    StringHttpOutput stream;
-    HttpOutput http(stream, false /* no keep alive */);
-
-    LOG(INFO) << "Plugin making REST DELETE call on URI " 
-              << reinterpret_cast<const char*>(parameters)
+    const char* uri = reinterpret_cast<const char*>(parameters);
+    LOG(INFO) << "Plugin making REST DELETE call on URI " << uri
               << (afterPlugins ? " (after plugins)" : " (built-in API)");
 
-    bool ok = false;
-
-    if (afterPlugins)
-    {
-      ok = Handle(http, HttpMethod_Delete, uri, headers, getArguments, body);
-    }
+    CheckContextAvailable();
+    IHttpHandler& handler = pimpl_->context_->GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
 
-    if (!ok)
-    {
-      ok = (pimpl_->restApi_ != NULL &&
-            pimpl_->restApi_->Handle(http, HttpMethod_Delete, uri, headers, getArguments, body));
-    }
-
-    if (!ok)
+    if (!HttpToolbox::SimpleDelete(handler, RequestOrigin_Plugins, uri))
     {
       throw OrthancException(ErrorCode_BadRequest);
     }
@@ -839,7 +892,7 @@
         throw OrthancException(ErrorCode_InternalError);
     }
 
-    assert(pimpl_->context_ != NULL);
+    CheckContextAvailable();
 
     std::list<std::string> result;
     pimpl_->context_->GetIndex().LookupIdentifier(result, tag, p.argument, level);
@@ -921,7 +974,7 @@
     switch (service)
     {
       case _OrthancPluginService_GetInstanceRemoteAet:
-        *p.resultString = instance.GetRemoteAet().c_str();
+        *p.resultString = instance.GetRemoteAet();
         return;
 
       case _OrthancPluginService_GetInstanceSize:
@@ -967,9 +1020,225 @@
   }
 
 
-  bool OrthancPlugins::InvokeService(_OrthancPluginService service,
+  void OrthancPlugins::BufferCompression(const void* parameters)
+  {
+    const _OrthancPluginBufferCompression& p = 
+      *reinterpret_cast<const _OrthancPluginBufferCompression*>(parameters);
+
+    std::string result;
+
+    {
+      std::auto_ptr<DeflateBaseCompressor> compressor;
+
+      switch (p.compression)
+      {
+        case OrthancPluginCompressionType_Zlib:
+        {
+          compressor.reset(new ZlibCompressor);
+          compressor->SetPrefixWithUncompressedSize(false);
+          break;
+        }
+
+        case OrthancPluginCompressionType_ZlibWithSize:
+        {
+          compressor.reset(new ZlibCompressor);
+          compressor->SetPrefixWithUncompressedSize(true);
+          break;
+        }
+
+        case OrthancPluginCompressionType_Gzip:
+        {
+          compressor.reset(new GzipCompressor);
+          compressor->SetPrefixWithUncompressedSize(false);
+          break;
+        }
+
+        case OrthancPluginCompressionType_GzipWithSize:
+        {
+          compressor.reset(new GzipCompressor);
+          compressor->SetPrefixWithUncompressedSize(true);
+          break;
+        }
+
+        default:
+          throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+
+      if (p.uncompress)
+      {
+        compressor->Uncompress(result, p.source, p.size);
+      }
+      else
+      {
+        compressor->Compress(result, p.source, p.size);
+      }
+    }
+
+    CopyToMemoryBuffer(*p.target, result);
+  }
+
+
+  void OrthancPlugins::UncompressImage(const void* parameters)
+  {
+    const _OrthancPluginUncompressImage& p = *reinterpret_cast<const _OrthancPluginUncompressImage*>(parameters);
+
+    std::auto_ptr<ImageAccessor> image;
+
+    switch (p.format)
+    {
+      case OrthancPluginImageFormat_Png:
+      {
+        image.reset(new PngReader);
+        reinterpret_cast<PngReader&>(*image).ReadFromMemory(p.data, p.size);
+        break;
+      }
+
+      case OrthancPluginImageFormat_Jpeg:
+      {
+        image.reset(new JpegReader);
+        reinterpret_cast<JpegReader&>(*image).ReadFromMemory(p.data, p.size);
+        break;
+      }
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    *(p.target) = reinterpret_cast<OrthancPluginImage*>(image.release());
+  }
+
+
+  void OrthancPlugins::CompressImage(const void* parameters)
+  {
+    const _OrthancPluginCompressImage& p = *reinterpret_cast<const _OrthancPluginCompressImage*>(parameters);
+
+    std::string compressed;
+
+    switch (p.imageFormat)
+    {
+      case OrthancPluginImageFormat_Png:
+      {
+        PngWriter writer;
+        writer.WriteToMemory(compressed, p.width, p.height, p.pitch, Plugins::Convert(p.pixelFormat), p.buffer);
+        break;
+      }
+
+      case OrthancPluginImageFormat_Jpeg:
+      {
+        JpegWriter writer;
+        writer.SetQuality(p.quality);
+        writer.WriteToMemory(compressed, p.width, p.height, p.pitch, Plugins::Convert(p.pixelFormat), p.buffer);
+        break;
+      }
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    CopyToMemoryBuffer(*p.target, compressed.size() > 0 ? compressed.c_str() : NULL, compressed.size());
+  }
+
+
+  void OrthancPlugins::CallHttpClient(const void* parameters)
+  {
+    const _OrthancPluginCallHttpClient& p = *reinterpret_cast<const _OrthancPluginCallHttpClient*>(parameters);
+
+    HttpClient client;
+    client.SetUrl(p.url);
+
+    if (p.username != NULL && 
+        p.password != NULL)
+    {
+      client.SetCredentials(p.username, p.password);
+    }
+
+    switch (p.method)
+    {
+      case OrthancPluginHttpMethod_Get:
+        client.SetMethod(HttpMethod_Get);
+        break;
+
+      case OrthancPluginHttpMethod_Post:
+        client.SetMethod(HttpMethod_Post);
+        client.GetBody().assign(p.body, p.bodySize);
+        break;
+
+      case OrthancPluginHttpMethod_Put:
+        client.SetMethod(HttpMethod_Put);
+        client.GetBody().assign(p.body, p.bodySize);
+        break;
+
+      case OrthancPluginHttpMethod_Delete:
+        client.SetMethod(HttpMethod_Delete);
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    std::string s;
+    client.ApplyAndThrowException(s);
+
+    if (p.method != OrthancPluginHttpMethod_Delete)
+    {
+      CopyToMemoryBuffer(*p.target, s);
+    }
+  }
+
+
+  void OrthancPlugins::ConvertPixelFormat(const void* parameters)
+  {
+    const _OrthancPluginConvertPixelFormat& p = *reinterpret_cast<const _OrthancPluginConvertPixelFormat*>(parameters);
+    const ImageAccessor& source = *reinterpret_cast<const ImageAccessor*>(p.source);
+
+    std::auto_ptr<ImageAccessor> target(new Image(Plugins::Convert(p.targetFormat), source.GetWidth(), source.GetHeight()));
+    ImageProcessing::Convert(*target, source);
+
+    *(p.target) = reinterpret_cast<OrthancPluginImage*>(target.release());
+  }
+
+
+
+  void OrthancPlugins::GetFontInfo(const void* parameters)
+  {
+    const _OrthancPluginGetFontInfo& p = *reinterpret_cast<const _OrthancPluginGetFontInfo*>(parameters);
+
+    const Font& font = Configuration::GetFontRegistry().GetFont(p.fontIndex);
+
+    if (p.name != NULL)
+    {
+      *(p.name) = font.GetName().c_str();
+    }
+    else if (p.size != NULL)
+    {
+      *(p.size) = font.GetSize();
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+  }
+
+
+  void OrthancPlugins::DrawText(const void* parameters)
+  {
+    const _OrthancPluginDrawText& p = *reinterpret_cast<const _OrthancPluginDrawText*>(parameters);
+
+    ImageAccessor& target = *reinterpret_cast<ImageAccessor*>(p.image);
+    const Font& font = Configuration::GetFontRegistry().GetFont(p.fontIndex);
+
+    font.Draw(target, p.utf8Text, p.x, p.y, p.r, p.g, p.b);
+  }
+        
+
+  bool OrthancPlugins::InvokeService(SharedLibrary& plugin,
+                                     _OrthancPluginService service,
                                      const void* parameters)
   {
+    VLOG(1) << "Calling service " << service << " from plugin " << plugin.GetPath();
+
+    boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
+
     switch (service)
     {
       case _OrthancPluginService_GetOrthancPath:
@@ -993,8 +1262,25 @@
         return true;
       }
 
+      case _OrthancPluginService_GetConfiguration:
+      {
+        std::string s;
+        Configuration::FormatConfiguration(s);
+
+        *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s);
+        return true;
+      }
+
+      case _OrthancPluginService_BufferCompression:
+        BufferCompression(parameters);
+        return true;
+
       case _OrthancPluginService_RegisterRestCallback:
-        RegisterRestCallback(parameters);
+        RegisterRestCallback(parameters, true);
+        return true;
+
+      case _OrthancPluginService_RegisterRestCallbackNoLock:
+        RegisterRestCallback(parameters, false);
         return true;
 
       case _OrthancPluginService_RegisterOnStoredInstanceCallback:
@@ -1013,6 +1299,10 @@
         CompressAndAnswerPngImage(parameters);
         return true;
 
+      case _OrthancPluginService_CompressAndAnswerImage:
+        CompressAndAnswerImage(parameters);
+        return true;
+
       case _OrthancPluginService_GetDicomForInstance:
         GetDicomForInstance(parameters);
         return true;
@@ -1061,6 +1351,10 @@
         SendMethodNotAllowed(parameters);
         return true;
 
+      case _OrthancPluginService_SendHttpStatus:
+        SendHttpStatus(parameters);
+        return true;
+
       case _OrthancPluginService_SendHttpStatusCode:
         SendHttpStatusCode(parameters);
         return true;
@@ -1097,8 +1391,15 @@
         const _OrthancPluginRegisterStorageArea& p = 
           *reinterpret_cast<const _OrthancPluginRegisterStorageArea*>(parameters);
         
-        pimpl_->storageArea_ = p;
-        pimpl_->hasStorageArea_ = true;
+        if (pimpl_->storageArea_.get() == NULL)
+        {
+          pimpl_->storageArea_.reset(new StorageAreaFactory(plugin, p, GetErrorDictionary()));
+        }
+        else
+        {
+          throw OrthancException(ErrorCode_StorageAreaAlreadyRegistered);
+        }
+
         return true;
       }
 
@@ -1120,7 +1421,7 @@
         }
         else
         {
-          assert(pimpl_->context_ != NULL);
+          CheckContextAvailable();
           pimpl_->context_->GetIndex().SetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value);
           return true;
         }
@@ -1128,7 +1429,7 @@
 
       case _OrthancPluginService_GetGlobalProperty:
       {
-        assert(pimpl_->context_ != NULL);
+        CheckContextAvailable();
 
         const _OrthancPluginGlobalProperty& p = 
           *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters);
@@ -1165,10 +1466,43 @@
       case _OrthancPluginService_RegisterDatabaseBackend:
       {
         LOG(INFO) << "Plugin has registered a custom database back-end";
+
         const _OrthancPluginRegisterDatabaseBackend& p =
           *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackend*>(parameters);
 
-        pimpl_->database_.reset(new OrthancPluginDatabase(*p.backend, p.payload));
+        if (pimpl_->database_.get() == NULL)
+        {
+          pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(), 
+                                                            *p.backend, NULL, 0, p.payload));
+        }
+        else
+        {
+          throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
+        }
+
+        *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
+
+        return true;
+      }
+
+      case _OrthancPluginService_RegisterDatabaseBackendV2:
+      {
+        LOG(INFO) << "Plugin has registered a custom database back-end";
+
+        const _OrthancPluginRegisterDatabaseBackendV2& p =
+          *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackendV2*>(parameters);
+
+        if (pimpl_->database_.get() == NULL)
+        {
+          pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(),
+                                                            *p.backend, p.extensions,
+                                                            p.extensionsSize, p.payload));
+        }
+        else
+        {
+          throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
+        }
+
         *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
 
         return true;
@@ -1178,6 +1512,7 @@
       {
         const _OrthancPluginDatabaseAnswer& p =
           *reinterpret_cast<const _OrthancPluginDatabaseAnswer*>(parameters);
+
         if (pimpl_->database_.get() != NULL)
         {
           pimpl_->database_->AnswerReceived(p);
@@ -1190,141 +1525,260 @@
         }
       }
 
+      case _OrthancPluginService_GetExpectedDatabaseVersion:
+      {
+        const _OrthancPluginReturnSingleValue& p =
+          *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters);
+        *(p.resultUint32) = ORTHANC_DATABASE_VERSION;
+        return true;
+      }
+
+      case _OrthancPluginService_StartMultipartAnswer:
+      {
+        const _OrthancPluginStartMultipartAnswer& p =
+          *reinterpret_cast<const _OrthancPluginStartMultipartAnswer*>(parameters);
+        HttpOutput* output = reinterpret_cast<HttpOutput*>(p.output);
+        output->StartMultipart(p.subType, p.contentType);
+        return true;
+      }
+
+      case _OrthancPluginService_SendMultipartItem:
+      {
+        // An exception might be raised in this function if the
+        // connection was closed by the HTTP client.
+        const _OrthancPluginAnswerBuffer& p =
+          *reinterpret_cast<const _OrthancPluginAnswerBuffer*>(parameters);
+        HttpOutput* output = reinterpret_cast<HttpOutput*>(p.output);
+        output->SendMultipartItem(p.answer, p.answerSize);
+        return true;
+      }
+
+      case _OrthancPluginService_ReadFile:
+      {
+        const _OrthancPluginReadFile& p =
+          *reinterpret_cast<const _OrthancPluginReadFile*>(parameters);
+
+        std::string content;
+        Toolbox::ReadFile(content, p.path);
+        CopyToMemoryBuffer(*p.target, content.size() > 0 ? content.c_str() : NULL, content.size());
+
+        return true;
+      }
+
+      case _OrthancPluginService_WriteFile:
+      {
+        const _OrthancPluginWriteFile& p =
+          *reinterpret_cast<const _OrthancPluginWriteFile*>(parameters);
+        Toolbox::WriteFile(p.data, p.size, p.path);
+        return true;
+      }
+
+      case _OrthancPluginService_GetErrorDescription:
+      {
+        const _OrthancPluginGetErrorDescription& p =
+          *reinterpret_cast<const _OrthancPluginGetErrorDescription*>(parameters);
+        *(p.target) = EnumerationToString(static_cast<ErrorCode>(p.error));
+        return true;
+      }
+
+      case _OrthancPluginService_GetImagePixelFormat:
+      {
+        const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters);
+        *(p.resultPixelFormat) = Plugins::Convert(reinterpret_cast<const ImageAccessor*>(p.image)->GetFormat());
+        return true;
+      }
+
+      case _OrthancPluginService_GetImageWidth:
+      {
+        const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters);
+        *(p.resultUint32) = reinterpret_cast<const ImageAccessor*>(p.image)->GetWidth();
+        return true;
+      }
+
+      case _OrthancPluginService_GetImageHeight:
+      {
+        const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters);
+        *(p.resultUint32) = reinterpret_cast<const ImageAccessor*>(p.image)->GetHeight();
+        return true;
+      }
+
+      case _OrthancPluginService_GetImagePitch:
+      {
+        const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters);
+        *(p.resultUint32) = reinterpret_cast<const ImageAccessor*>(p.image)->GetPitch();
+        return true;
+      }
+
+      case _OrthancPluginService_GetImageBuffer:
+      {
+        const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters);
+        *(p.resultBuffer) = reinterpret_cast<const ImageAccessor*>(p.image)->GetConstBuffer();
+        return true;
+      }
+
+      case _OrthancPluginService_FreeImage:
+      {
+        const _OrthancPluginFreeImage& p = *reinterpret_cast<const _OrthancPluginFreeImage*>(parameters);
+        if (p.image == NULL)
+        {
+          throw OrthancException(ErrorCode_ParameterOutOfRange);
+        }
+        else
+        {
+          delete reinterpret_cast<ImageAccessor*>(p.image);
+          return true;
+        }
+      }
+
+      case _OrthancPluginService_UncompressImage:
+        UncompressImage(parameters);
+        return true;
+
+      case _OrthancPluginService_CompressImage:
+        CompressImage(parameters);
+        return true;
+
+      case _OrthancPluginService_CallHttpClient:
+        CallHttpClient(parameters);
+        return true;
+
+      case _OrthancPluginService_ConvertPixelFormat:
+        ConvertPixelFormat(parameters);
+        return true;
+
+      case _OrthancPluginService_GetFontsCount:
+      {
+        const _OrthancPluginReturnSingleValue& p =
+          *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters);
+        *(p.resultUint32) = Configuration::GetFontRegistry().GetSize();
+        return true;
+      }
+
+      case _OrthancPluginService_GetFontInfo:
+        GetFontInfo(parameters);
+        return true;
+
+      case _OrthancPluginService_DrawText:
+        DrawText(parameters);
+        return true;
+
+      case _OrthancPluginService_StorageAreaCreate:
+      {
+        const _OrthancPluginStorageAreaCreate& p =
+          *reinterpret_cast<const _OrthancPluginStorageAreaCreate*>(parameters);
+        IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea);
+        storage.Create(p.uuid, p.content, p.size, Plugins::Convert(p.type));
+        return true;
+      }
+
+      case _OrthancPluginService_StorageAreaRead:
+      {
+        const _OrthancPluginStorageAreaRead& p =
+          *reinterpret_cast<const _OrthancPluginStorageAreaRead*>(parameters);
+        IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea);
+        std::string content;
+        storage.Read(content, p.uuid, Plugins::Convert(p.type));
+        CopyToMemoryBuffer(*p.target, content);
+        return true;
+      }
+
+      case _OrthancPluginService_StorageAreaRemove:
+      {
+        const _OrthancPluginStorageAreaRemove& p =
+          *reinterpret_cast<const _OrthancPluginStorageAreaRemove*>(parameters);
+        IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea);
+        storage.Remove(p.uuid, Plugins::Convert(p.type));
+        return true;
+      }
+
+      case _OrthancPluginService_RegisterErrorCode:
+      {
+        const _OrthancPluginRegisterErrorCode& p =
+          *reinterpret_cast<const _OrthancPluginRegisterErrorCode*>(parameters);
+        *(p.target) = pimpl_->dictionary_.Register(plugin, p.code, p.httpStatus, p.message);
+        return true;
+      }
+
+      case _OrthancPluginService_RegisterDictionaryTag:
+      {
+        const _OrthancPluginRegisterDictionaryTag& p =
+          *reinterpret_cast<const _OrthancPluginRegisterDictionaryTag*>(parameters);
+        FromDcmtkBridge::RegisterDictionaryTag(DicomTag(p.group, p.element),
+                                               Plugins::Convert(p.vr), p.name,
+                                               p.minMultiplicity, p.maxMultiplicity);
+        return true;
+      }
+
       default:
+      {
+        // This service is unknown to the Orthanc plugin engine
         return false;
+      }
     }
   }
 
 
-  void OrthancPlugins::SetOrthancRestApi(OrthancRestApi& restApi)
-  {
-    pimpl_->restApi_ = &restApi;
-  }
-
-  
   bool OrthancPlugins::HasStorageArea() const
   {
-    return pimpl_->hasStorageArea_;
+    return pimpl_->storageArea_.get() != NULL;
   }
   
-  bool OrthancPlugins::HasDatabase() const
+  bool OrthancPlugins::HasDatabaseBackend() const
   {
     return pimpl_->database_.get() != NULL;
   }
 
 
-
-  namespace
+  IStorageArea* OrthancPlugins::CreateStorageArea()
   {
-    class PluginStorageArea : public IStorageArea
+    if (!HasStorageArea())
     {
-    private:
-      _OrthancPluginRegisterStorageArea params_;
-
-      void Free(void* buffer) const
-      {
-        if (buffer != NULL)
-        {
-          params_.free(buffer);
-        }
-      }
-
-      OrthancPluginContentType Convert(FileContentType type) const
-      {
-        switch (type)
-        {
-          case FileContentType_Dicom:
-            return OrthancPluginContentType_Dicom;
-
-          case FileContentType_DicomAsJson:
-            return OrthancPluginContentType_DicomAsJson;
-
-          default:
-            return OrthancPluginContentType_Unknown;
-        }
-      }
-
-    public:
-      PluginStorageArea(const _OrthancPluginRegisterStorageArea& params) : params_(params)
-      {
-      }
-
-      virtual void Create(const std::string& uuid,
-                          const void* content, 
-                          size_t size,
-                          FileContentType type)
-      {
-        if (params_.create(uuid.c_str(), content, size, Convert(type)) != 0)
-        {
-          throw OrthancException(ErrorCode_Plugin);
-        }
-      }
-
-      virtual void Read(std::string& content,
-                        const std::string& uuid,
-                        FileContentType type)
-      {
-        void* buffer = NULL;
-        int64_t size = 0;
-
-        if (params_.read(&buffer, &size, uuid.c_str(), Convert(type)) != 0)
-        {
-          throw OrthancException(ErrorCode_Plugin);
-        }        
-
-        try
-        {
-          content.resize(size);
-        }
-        catch (OrthancException&)
-        {
-          Free(buffer);
-          throw;
-        }
-
-        if (size > 0)
-        {
-          memcpy(&content[0], buffer, size);
-        }
-
-        Free(buffer);
-      }
-
-      virtual void Remove(const std::string& uuid,
-                          FileContentType type) 
-      {
-        if (params_.remove(uuid.c_str(), Convert(type)) != 0)
-        {
-          throw OrthancException(ErrorCode_Plugin);
-        }        
-      }
-    };
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      return pimpl_->storageArea_->Create();
+    }
   }
 
 
-  IStorageArea* OrthancPlugins::GetStorageArea()
+  const SharedLibrary& OrthancPlugins::GetStorageAreaLibrary() const
   {
     if (!HasStorageArea())
     {
       throw OrthancException(ErrorCode_BadSequenceOfCalls);
     }
-
-    return new PluginStorageArea(pimpl_->storageArea_);
+    else
+    {
+      return pimpl_->storageArea_->GetSharedLibrary();
+    }
   }
 
 
-  IDatabaseWrapper& OrthancPlugins::GetDatabase()
+  IDatabaseWrapper& OrthancPlugins::GetDatabaseBackend()
   {
-    if (!HasDatabase())
+    if (!HasDatabaseBackend())
     {
       throw OrthancException(ErrorCode_BadSequenceOfCalls);
     }
-
-    return *pimpl_->database_;
+    else
+    {
+      return *pimpl_->database_;
+    }
   }
 
 
-
+  const SharedLibrary& OrthancPlugins::GetDatabaseBackendLibrary() const
+  {
+    if (!HasDatabaseBackend())
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      return pimpl_->database_->GetSharedLibrary();
+    }
+  }
 
 
   const char* OrthancPlugins::GetProperty(const char* plugin,
@@ -1354,4 +1808,22 @@
     pimpl_->argc_ = argc;
     pimpl_->argv_ = argv;
   }
+
+
+  PluginsManager& OrthancPlugins::GetManager()
+  {
+    return pimpl_->manager_;
+  }
+
+
+  const PluginsManager& OrthancPlugins::GetManager() const
+  {
+    return pimpl_->manager_;
+  }
+
+
+  PluginsErrorDictionary&  OrthancPlugins::GetErrorDictionary()
+  {
+    return pimpl_->dictionary_;
+  }
 }
--- a/Plugins/Engine/OrthancPlugins.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Engine/OrthancPlugins.h	Wed Sep 30 13:23:31 2015 +0200
@@ -32,24 +32,47 @@
 
 #pragma once
 
+#include "PluginsErrorDictionary.h"
+
+#if ORTHANC_PLUGINS_ENABLED != 1
+
+#include <boost/noncopyable.hpp>
+
+namespace Orthanc
+{
+  class OrthancPlugins : public boost::noncopyable
+  {
+  };
+}
+
+#else
+
+#include "../../Core/FileStorage/IStorageArea.h"
+#include "../../Core/HttpServer/IHttpHandler.h"
+#include "../../OrthancServer/IServerListener.h"
+#include "OrthancPluginDatabase.h"
 #include "PluginsManager.h"
-#include "../../Core/HttpServer/HttpHandler.h"
-#include "../../OrthancServer/ServerContext.h"
-#include "../../OrthancServer/OrthancRestApi/OrthancRestApi.h"
-#include "OrthancPluginDatabase.h"
 
 #include <list>
 #include <boost/shared_ptr.hpp>
 
 namespace Orthanc
 {
-  class OrthancPlugins : public HttpHandler, public IPluginServiceProvider
+  class ServerContext;
+
+  class OrthancPlugins : 
+    public IHttpHandler, 
+    public IPluginServiceProvider, 
+    public IServerListener
   {
   private:
     struct PImpl;
     boost::shared_ptr<PImpl> pimpl_;
 
-    void RegisterRestCallback(const void* parameters);
+    void CheckContextAvailable();
+
+    void RegisterRestCallback(const void* parameters,
+                              bool lock);
 
     void RegisterOnStoredInstanceCallback(const void* parameters);
 
@@ -61,6 +84,8 @@
 
     void CompressAndAnswerPngImage(const void* parameters);
 
+    void CompressAndAnswerImage(const void* parameters);
+
     void GetDicomForInstance(const void* parameters);
 
     void RestApiGet(const void* parameters,
@@ -78,6 +103,8 @@
 
     void SendHttpStatusCode(const void* parameters);
 
+    void SendHttpStatus(const void* parameters);
+
     void SendUnauthorized(const void* parameters);
 
     void SendMethodNotAllowed(const void* parameters);
@@ -86,6 +113,20 @@
 
     void SetHttpHeader(const void* parameters);
 
+    void BufferCompression(const void* parameters);
+
+    void UncompressImage(const void* parameters);
+
+    void CompressImage(const void* parameters);
+
+    void ConvertPixelFormat(const void* parameters);
+
+    void CallHttpClient(const void* parameters);
+
+    void GetFontInfo(const void* parameters);
+
+    void DrawText(const void* parameters);
+
   public:
     OrthancPlugins();
 
@@ -94,35 +135,55 @@
     void SetServerContext(ServerContext& context);
 
     virtual bool Handle(HttpOutput& output,
+                        RequestOrigin origin,
+                        const char* remoteIp,
+                        const char* username,
                         HttpMethod method,
                         const UriComponents& uri,
                         const Arguments& headers,
-                        const Arguments& getArguments,
-                        const std::string& postData);
+                        const GetArguments& getArguments,
+                        const char* bodyData,
+                        size_t bodySize);
 
-    virtual bool InvokeService(_OrthancPluginService service,
+    virtual bool InvokeService(SharedLibrary& plugin,
+                               _OrthancPluginService service,
                                const void* parameters);
 
-    void SignalChange(const ServerIndexChange& change);
+    virtual void SignalChange(const ServerIndexChange& change);
+
+    virtual void SignalStoredInstance(const std::string& instanceId,
+                                      DicomInstanceToStore& instance,
+                                      const Json::Value& simplifiedTags);
 
-    void SignalStoredInstance(DicomInstanceToStore& instance,
-                              const std::string& instanceId);
-
-    void SetOrthancRestApi(OrthancRestApi& restApi);
+    virtual bool FilterIncomingInstance(const DicomInstanceToStore& instance,
+                                        const Json::Value& simplified)
+    {
+      return true; // TODO Enable filtering of instances from plugins
+    }
 
     bool HasStorageArea() const;
 
-    IStorageArea* GetStorageArea();  // To be freed after use
+    IStorageArea* CreateStorageArea();  // To be freed after use
 
-    bool HasDatabase() const;
+    const SharedLibrary& GetStorageAreaLibrary() const;
 
-    IDatabaseWrapper& GetDatabase();
+    bool HasDatabaseBackend() const;
 
-    void Stop();
+    IDatabaseWrapper& GetDatabaseBackend();
+
+    const SharedLibrary& GetDatabaseBackendLibrary() const;
 
     const char* GetProperty(const char* plugin,
                             _OrthancPluginProperty property) const;
 
     void SetCommandLineArguments(int argc, char* argv[]);
+
+    PluginsManager& GetManager();
+
+    const PluginsManager& GetManager() const;
+
+    PluginsErrorDictionary& GetErrorDictionary();
   };
 }
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Engine/PluginsEnumerations.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,282 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../../OrthancServer/PrecompiledHeadersServer.h"
+#include "PluginsEnumerations.h"
+
+#if ORTHANC_PLUGINS_ENABLED != 1
+#error The plugin support is disabled
+#endif
+
+
+#include "../../Core/OrthancException.h"
+
+namespace Orthanc
+{
+  namespace Plugins
+  {
+    OrthancPluginResourceType Convert(ResourceType type)
+    {
+      switch (type)
+      {
+        case ResourceType_Patient:
+          return OrthancPluginResourceType_Patient;
+
+        case ResourceType_Study:
+          return OrthancPluginResourceType_Study;
+
+        case ResourceType_Series:
+          return OrthancPluginResourceType_Series;
+
+        case ResourceType_Instance:
+          return OrthancPluginResourceType_Instance;
+
+        default:
+          throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+    }
+
+
+    OrthancPluginChangeType Convert(ChangeType type)
+    {
+      switch (type)
+      {
+        case ChangeType_CompletedSeries:
+          return OrthancPluginChangeType_CompletedSeries;
+
+        case ChangeType_Deleted:
+          return OrthancPluginChangeType_Deleted;
+
+        case ChangeType_NewChildInstance:
+          return OrthancPluginChangeType_NewChildInstance;
+
+        case ChangeType_NewInstance:
+          return OrthancPluginChangeType_NewInstance;
+
+        case ChangeType_NewPatient:
+          return OrthancPluginChangeType_NewPatient;
+
+        case ChangeType_NewSeries:
+          return OrthancPluginChangeType_NewSeries;
+
+        case ChangeType_NewStudy:
+          return OrthancPluginChangeType_NewStudy;
+
+        case ChangeType_StablePatient:
+          return OrthancPluginChangeType_StablePatient;
+
+        case ChangeType_StableSeries:
+          return OrthancPluginChangeType_StableSeries;
+
+        case ChangeType_StableStudy:
+          return OrthancPluginChangeType_StableStudy;
+
+        default:
+          throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+    }
+
+
+    OrthancPluginPixelFormat Convert(PixelFormat format)
+    {
+      switch (format)
+      {
+        case PixelFormat_Grayscale16:
+          return OrthancPluginPixelFormat_Grayscale16;
+
+        case PixelFormat_Grayscale8:
+          return OrthancPluginPixelFormat_Grayscale8;
+
+        case PixelFormat_RGB24:
+          return OrthancPluginPixelFormat_RGB24;
+
+        case PixelFormat_RGBA32:
+          return OrthancPluginPixelFormat_RGBA32;
+
+        case PixelFormat_SignedGrayscale16:
+          return OrthancPluginPixelFormat_SignedGrayscale16;
+
+        default:
+          throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+    }
+
+
+    PixelFormat Convert(OrthancPluginPixelFormat format)
+    {
+      switch (format)
+      {
+        case OrthancPluginPixelFormat_Grayscale16:
+          return PixelFormat_Grayscale16;
+
+        case OrthancPluginPixelFormat_Grayscale8:
+          return PixelFormat_Grayscale8;
+
+        case OrthancPluginPixelFormat_RGB24:
+          return PixelFormat_RGB24;
+
+        case OrthancPluginPixelFormat_RGBA32:
+          return PixelFormat_RGBA32;
+
+        case OrthancPluginPixelFormat_SignedGrayscale16:
+          return PixelFormat_SignedGrayscale16;
+
+        default:
+          throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+    }
+
+
+    OrthancPluginContentType Convert(FileContentType type)
+    {
+      switch (type)
+      {
+        case FileContentType_Dicom:
+          return OrthancPluginContentType_Dicom;
+
+        case FileContentType_DicomAsJson:
+          return OrthancPluginContentType_DicomAsJson;
+
+        default:
+          return OrthancPluginContentType_Unknown;
+      }
+    }
+
+
+    FileContentType Convert(OrthancPluginContentType type)
+    {
+      switch (type)
+      {
+        case OrthancPluginContentType_Dicom:
+          return FileContentType_Dicom;
+
+        case OrthancPluginContentType_DicomAsJson:
+          return FileContentType_DicomAsJson;
+
+        default:
+          return FileContentType_Unknown;
+      }
+    }
+
+
+
+    DcmEVR Convert(OrthancPluginValueRepresentation vr)
+    {
+      switch (vr)
+      {
+        case OrthancPluginValueRepresentation_AE:
+          return EVR_AE;
+
+        case OrthancPluginValueRepresentation_AS:
+          return EVR_AS;
+
+        case OrthancPluginValueRepresentation_AT:
+          return EVR_AT;
+
+        case OrthancPluginValueRepresentation_CS:
+          return EVR_CS;
+
+        case OrthancPluginValueRepresentation_DA:
+          return EVR_DA;
+
+        case OrthancPluginValueRepresentation_DS:
+          return EVR_DS;
+
+        case OrthancPluginValueRepresentation_DT:
+          return EVR_DT;
+
+        case OrthancPluginValueRepresentation_FD:
+          return EVR_FD;
+
+        case OrthancPluginValueRepresentation_FL:
+          return EVR_FL;
+
+        case OrthancPluginValueRepresentation_IS:
+          return EVR_IS;
+
+        case OrthancPluginValueRepresentation_LO:
+          return EVR_LO;
+
+        case OrthancPluginValueRepresentation_LT:
+          return EVR_LT;
+
+        case OrthancPluginValueRepresentation_OB:
+          return EVR_OB;
+
+        case OrthancPluginValueRepresentation_OF:
+          return EVR_OF;
+
+        case OrthancPluginValueRepresentation_OW:
+          return EVR_OW;
+
+        case OrthancPluginValueRepresentation_PN:
+          return EVR_PN;
+
+        case OrthancPluginValueRepresentation_SH:
+          return EVR_SH;
+
+        case OrthancPluginValueRepresentation_SL:
+          return EVR_SL;
+
+        case OrthancPluginValueRepresentation_SQ:
+          return EVR_SQ;
+
+        case OrthancPluginValueRepresentation_SS:
+          return EVR_SS;
+
+        case OrthancPluginValueRepresentation_ST:
+          return EVR_ST;
+
+        case OrthancPluginValueRepresentation_TM:
+          return EVR_TM;
+
+        case OrthancPluginValueRepresentation_UI:
+          return EVR_UI;
+
+        case OrthancPluginValueRepresentation_UL:
+          return EVR_UL;
+
+        case OrthancPluginValueRepresentation_UN:
+          return EVR_UN;
+
+        case OrthancPluginValueRepresentation_US:
+          return EVR_US;
+
+        case OrthancPluginValueRepresentation_UT:
+          return EVR_UT;
+
+        default:
+          throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Engine/PluginsEnumerations.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,62 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#if ORTHANC_PLUGINS_ENABLED == 1
+
+#include "../Include/orthanc/OrthancCPlugin.h"
+#include "../../OrthancServer/ServerEnumerations.h"
+
+#include <dcmtk/dcmdata/dcvr.h>
+
+namespace Orthanc
+{
+  namespace Plugins
+  {
+    OrthancPluginResourceType Convert(ResourceType type);
+
+    OrthancPluginChangeType Convert(ChangeType type);
+
+    OrthancPluginPixelFormat Convert(PixelFormat format);
+
+    PixelFormat Convert(OrthancPluginPixelFormat format);
+
+    OrthancPluginContentType Convert(FileContentType type);
+
+    FileContentType Convert(OrthancPluginContentType type);
+
+    DcmEVR Convert(OrthancPluginValueRepresentation vr);
+  }
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Engine/PluginsErrorDictionary.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,138 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../../OrthancServer/PrecompiledHeadersServer.h"
+#include "PluginsErrorDictionary.h"
+
+#if ORTHANC_PLUGINS_ENABLED != 1
+#error The plugin support is disabled
+#endif
+
+
+
+#include "PluginsEnumerations.h"
+#include "PluginsManager.h"
+#include "../../Core/Logging.h"
+
+#include <memory>
+
+
+namespace Orthanc
+{
+  PluginsErrorDictionary::PluginsErrorDictionary() : 
+    pos_(ErrorCode_START_PLUGINS)
+  {
+  }
+
+
+  PluginsErrorDictionary::~PluginsErrorDictionary()
+  {
+    for (Errors::iterator it = errors_.begin(); it != errors_.end(); ++it)
+    {
+      delete it->second;
+    }
+  }
+
+
+  OrthancPluginErrorCode PluginsErrorDictionary::Register(SharedLibrary& library,
+                                                          int32_t  pluginCode,
+                                                          uint16_t httpStatus,
+                                                          const char* message)
+  {
+    std::auto_ptr<Error> error(new Error);
+
+    error->pluginName_ = PluginsManager::GetPluginName(library);
+    error->pluginCode_ = pluginCode;
+    error->message_ = message;
+    error->httpStatus_ = static_cast<HttpStatus>(httpStatus);
+
+    OrthancPluginErrorCode code;
+
+    {
+      boost::mutex::scoped_lock lock(mutex_);
+      errors_[pos_] = error.release();
+      code = static_cast<OrthancPluginErrorCode>(pos_);
+      pos_ += 1;
+    }
+
+    return code;
+  }
+
+
+  void  PluginsErrorDictionary::LogError(ErrorCode code,
+                                         bool ignoreBuiltinErrors)
+  {
+    if (code >= ErrorCode_START_PLUGINS)
+    {
+      boost::mutex::scoped_lock lock(mutex_);
+      Errors::const_iterator error = errors_.find(static_cast<int32_t>(code));
+      
+      if (error != errors_.end())
+      {
+        LOG(ERROR) << "Error code " << error->second->pluginCode_
+                   << " inside plugin \"" << error->second->pluginName_
+                   << "\": " << error->second->message_;
+        return;
+      }
+    }
+
+    if (!ignoreBuiltinErrors)
+    {
+      LOG(ERROR) << "Exception inside the plugin engine: "
+                 << EnumerationToString(code);
+    }
+  }
+
+
+  bool  PluginsErrorDictionary::Format(Json::Value& message,    /* out */
+                                       HttpStatus& httpStatus,  /* out */
+                                       const OrthancException& exception)
+  {
+    if (exception.GetErrorCode() >= ErrorCode_START_PLUGINS)
+    {
+      boost::mutex::scoped_lock lock(mutex_);
+      Errors::const_iterator error = errors_.find(static_cast<int32_t>(exception.GetErrorCode()));
+      
+      if (error != errors_.end())
+      {
+        httpStatus = error->second->httpStatus_;
+        message["PluginName"] = error->second->pluginName_;
+        message["PluginCode"] = error->second->pluginCode_;
+        message["Message"] = error->second->message_;
+
+        return true;
+      }
+    }
+
+    return false;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Engine/PluginsErrorDictionary.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,92 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#if ORTHANC_PLUGINS_ENABLED == 1
+
+#include "../Include/orthanc/OrthancCPlugin.h"
+#include "../../Core/OrthancException.h"
+#include "SharedLibrary.h"
+
+#include <map>
+#include <string>
+#include <boost/noncopyable.hpp>
+#include <boost/thread/mutex.hpp>
+#include <json/value.h>
+
+
+namespace Orthanc
+{
+  class PluginsErrorDictionary : public boost::noncopyable
+  {
+  private:
+    struct Error
+    {
+      std::string  pluginName_;
+      int32_t      pluginCode_;
+      HttpStatus   httpStatus_;
+      std::string  message_;
+    };
+    
+    typedef std::map<int32_t, Error*>  Errors;
+
+    boost::mutex  mutex_;
+    int32_t  pos_;
+    Errors   errors_;
+
+  public:
+    PluginsErrorDictionary();
+
+    ~PluginsErrorDictionary();
+
+    OrthancPluginErrorCode  Register(SharedLibrary& library,
+                                     int32_t  pluginCode,
+                                     uint16_t httpStatus,
+                                     const char* message);
+
+    void  LogError(ErrorCode code,
+                   bool ignoreBuiltinErrors);
+
+    void  LogError(OrthancPluginErrorCode code,
+                   bool ignoreBuiltinErrors)
+    {
+      LogError(static_cast<ErrorCode>(code), ignoreBuiltinErrors);
+    }
+
+    bool  Format(Json::Value& message,    /* out */
+                 HttpStatus& httpStatus,  /* out */
+                 const OrthancException& exception);
+  };
+}
+
+#endif
--- a/Plugins/Engine/PluginsManager.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Engine/PluginsManager.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -30,19 +30,25 @@
  **/
 
 
+#include "../../OrthancServer/PrecompiledHeadersServer.h"
 #include "PluginsManager.h"
 
+#if ORTHANC_PLUGINS_ENABLED != 1
+#error The plugin support is disabled
+#endif
+
+
 #include "../../Core/Toolbox.h"
 #include "../../Core/HttpServer/HttpOutput.h"
+#include "../../Core/Logging.h"
 
-#include <glog/logging.h>
 #include <cassert>
 #include <memory>
 #include <boost/filesystem.hpp>
 
 #ifdef WIN32
 #define PLUGIN_EXTENSION ".dll"
-#elif defined(__linux) || defined(__FreeBSD_kernel__)
+#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
 #define PLUGIN_EXTENSION ".so"
 #elif defined(__APPLE__) && defined(__MACH__)
 #define PLUGIN_EXTENSION ".dylib"
@@ -53,6 +59,19 @@
 
 namespace Orthanc
 {
+  PluginsManager::Plugin::Plugin(PluginsManager& pluginManager,
+                                 const std::string& path) : 
+    library_(path),
+    pluginManager_(pluginManager)
+  {
+    memset(&context_, 0, sizeof(context_));
+    context_.pluginsManager = this;
+    context_.orthancVersion = ORTHANC_VERSION;
+    context_.Free = ::free;
+    context_.InvokeService = InvokeService;
+  }
+
+
   static void CallInitialize(SharedLibrary& plugin,
                              const OrthancPluginContext& context)
   {
@@ -134,69 +153,56 @@
   }
 
 
-  int32_t PluginsManager::InvokeService(OrthancPluginContext* context,
-                                        _OrthancPluginService service, 
-                                        const void* params)
+  OrthancPluginErrorCode PluginsManager::InvokeService(OrthancPluginContext* context,
+                                                       _OrthancPluginService service, 
+                                                       const void* params)
   {
     switch (service)
     {
       case _OrthancPluginService_LogError:
         LOG(ERROR) << reinterpret_cast<const char*>(params);
-        return 0;
+        return OrthancPluginErrorCode_Success;
 
       case _OrthancPluginService_LogWarning:
         LOG(WARNING) << reinterpret_cast<const char*>(params);
-        return 0;
+        return OrthancPluginErrorCode_Success;
 
       case _OrthancPluginService_LogInfo:
         LOG(INFO) << reinterpret_cast<const char*>(params);
-        return 0;
+        return OrthancPluginErrorCode_Success;
 
       default:
         break;
     }
 
-    PluginsManager* that = reinterpret_cast<PluginsManager*>(context->pluginsManager);
-    bool error = false;
+    Plugin* that = reinterpret_cast<Plugin*>(context->pluginsManager);
 
     for (std::list<IPluginServiceProvider*>::iterator
-           it = that->serviceProviders_.begin(); 
-         it != that->serviceProviders_.end(); ++it)
+           it = that->GetPluginManager().serviceProviders_.begin(); 
+         it != that->GetPluginManager().serviceProviders_.end(); ++it)
     {
       try
       {
-        if ((*it)->InvokeService(service, params))
+        if ((*it)->InvokeService(that->GetSharedLibrary(), service, params))
         {
-          return 0;
+          return OrthancPluginErrorCode_Success;
         }
       }
-      catch (OrthancException&)
+      catch (OrthancException& e)
       {
-        // This service provider has failed, go to the next
-        error = true;
+        // This service provider has failed
+        LOG(ERROR) << "Exception while invoking a plugin service: " << e.What();
+        return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
       }
     }
 
-    if (error)
-    {
-      // LOG(ERROR) << "Exception when dealing with service " << service;
-    }
-    else
-    {
-      LOG(ERROR) << "Plugin invoking unknown service " << service;
-    }
-
-    return -1;
+    LOG(ERROR) << "Plugin invoking unknown service: " << service;
+    return OrthancPluginErrorCode_UnknownPluginService;
   }
 
 
   PluginsManager::PluginsManager()
   {
-    memset(&context_, 0, sizeof(context_));
-    context_.pluginsManager = this;
-    context_.orthancVersion = ORTHANC_VERSION;
-    context_.Free = ::free;
-    context_.InvokeService = InvokeService;
   }
 
   PluginsManager::~PluginsManager()
@@ -208,7 +214,7 @@
         LOG(WARNING) << "Unregistering plugin '" << it->first
                      << "' (version " << it->second->GetVersion() << ")";
 
-        CallFinalize(it->second->GetLibrary());
+        CallFinalize(it->second->GetSharedLibrary());
         delete it->second;
       }
     }
@@ -238,27 +244,27 @@
       return;
     }
 
-    std::auto_ptr<Plugin> plugin(new Plugin(path));
+    std::auto_ptr<Plugin> plugin(new Plugin(*this, path));
 
-    if (!IsOrthancPlugin(plugin->GetLibrary()))
+    if (!IsOrthancPlugin(plugin->GetSharedLibrary()))
     {
-      LOG(ERROR) << "Plugin " << plugin->GetLibrary().GetPath()
+      LOG(ERROR) << "Plugin " << plugin->GetSharedLibrary().GetPath()
                  << " does not declare the proper entry functions";
       throw OrthancException(ErrorCode_SharedLibrary);
     }
 
-    std::string name(CallGetName(plugin->GetLibrary()));
+    std::string name(CallGetName(plugin->GetSharedLibrary()));
     if (plugins_.find(name) != plugins_.end())
     {
       LOG(ERROR) << "Plugin '" << name << "' already registered";
       throw OrthancException(ErrorCode_SharedLibrary);
     }
 
-    plugin->SetVersion(CallGetVersion(plugin->GetLibrary()));
+    plugin->SetVersion(CallGetVersion(plugin->GetSharedLibrary()));
     LOG(WARNING) << "Registering plugin '" << name
                  << "' (version " << plugin->GetVersion() << ")";
 
-    CallInitialize(plugin->GetLibrary(), context_);
+    CallInitialize(plugin->GetSharedLibrary(), plugin->GetContext());
 
     plugins_[name] = plugin.release();
   }
@@ -292,7 +298,10 @@
       }
       else
       {
-        if (boost::filesystem::extension(it->path()) == PLUGIN_EXTENSION)
+        std::string extension = boost::filesystem::extension(it->path());
+        Toolbox::ToLowerCase(extension);
+
+        if (extension == PLUGIN_EXTENSION)
         {
           LOG(INFO) << "Found a shared library: " << it->path();
 
@@ -338,4 +347,9 @@
     }
   }
 
+  
+  std::string PluginsManager::GetPluginName(SharedLibrary& library)
+  {
+    return CallGetName(library);
+  }
 }
--- a/Plugins/Engine/PluginsManager.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Engine/PluginsManager.h	Wed Sep 30 13:23:31 2015 +0200
@@ -32,6 +32,8 @@
 
 #pragma once
 
+#if ORTHANC_PLUGINS_ENABLED == 1
+
 #include "SharedLibrary.h"
 #include "IPluginServiceProvider.h"
 
@@ -40,21 +42,22 @@
 
 namespace Orthanc
 {
-  class PluginsManager : boost::noncopyable
+  class PluginsManager : public boost::noncopyable
   {
   private:
-    class Plugin
+    class Plugin : public boost::noncopyable
     {
     private:
-      SharedLibrary library_;
-      std::string  version_;
+      OrthancPluginContext  context_;
+      SharedLibrary         library_;
+      std::string           version_;
+      PluginsManager&       pluginManager_;
 
     public:
-      Plugin(const std::string& path) : library_(path)
-      {
-      }
+      Plugin(PluginsManager& pluginManager,
+             const std::string& path);
 
-      SharedLibrary& GetLibrary()
+      SharedLibrary& GetSharedLibrary()
       {
         return library_;
       }
@@ -68,17 +71,26 @@
       {
         return version_;
       }
+
+      PluginsManager& GetPluginManager()
+      {
+        return pluginManager_;
+      }
+
+      OrthancPluginContext& GetContext()
+      {
+        return context_;
+      }
     };
 
     typedef std::map<std::string, Plugin*>  Plugins;
 
-    OrthancPluginContext  context_;
     Plugins  plugins_;
     std::list<IPluginServiceProvider*> serviceProviders_;
 
-    static int32_t InvokeService(OrthancPluginContext* context,
-                                 _OrthancPluginService service,
-                                 const void* parameters);
+    static OrthancPluginErrorCode InvokeService(OrthancPluginContext* context,
+                                                _OrthancPluginService service,
+                                                const void* parameters);
 
   public:
     PluginsManager();
@@ -100,5 +112,9 @@
     bool HasPlugin(const std::string& name) const;
 
     const std::string& GetPluginVersion(const std::string& name) const;
+
+    static std::string GetPluginName(SharedLibrary& library);
   };
 }
+
+#endif
--- a/Plugins/Engine/SharedLibrary.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Engine/SharedLibrary.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -30,36 +30,43 @@
  **/
 
 
+#include "../../OrthancServer/PrecompiledHeadersServer.h"
 #include "SharedLibrary.h"
 
+#if ORTHANC_PLUGINS_ENABLED != 1
+#error The plugin support is disabled
+#endif
+
+
+#include "../../Core/Logging.h"
 #include "../../Core/Toolbox.h"
 
+#include <boost/filesystem.hpp>
+
 #if defined(_WIN32)
 #include <windows.h>
-#elif defined(__linux) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__)
+#elif defined(__linux) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
 #include <dlfcn.h>
 #else
 #error Support your platform here
 #endif
 
-#include <glog/logging.h>
-
 namespace Orthanc
 {
   SharedLibrary::SharedLibrary(const std::string& path) : 
-    path_(path),
+    path_(path), 
     handle_(NULL)
   {
 #if defined(_WIN32)
-    handle_ = ::LoadLibraryA(path.c_str());
+    handle_ = ::LoadLibraryA(path_.c_str());
     if (handle_ == NULL)
     {
-      LOG(ERROR) << "LoadLibrary(" << path << ") failed: Error " << ::GetLastError();
+      LOG(ERROR) << "LoadLibrary(" << path_ << ") failed: Error " << ::GetLastError();
       throw OrthancException(ErrorCode_SharedLibrary);
     }
 
-#elif defined(__linux) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__)
-    handle_ = ::dlopen(path.c_str(), RTLD_NOW);
+#elif defined(__linux) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
+    handle_ = ::dlopen(path_.c_str(), RTLD_NOW);
     if (handle_ == NULL) 
     {
       std::string explanation;
@@ -69,7 +76,7 @@
         explanation = ": Error " + std::string(tmp);
       }
 
-      LOG(ERROR) << "dlopen(" << path << ") failed" << explanation;
+      LOG(ERROR) << "dlopen(" << path_ << ") failed" << explanation;
       throw OrthancException(ErrorCode_SharedLibrary);
     }
 
@@ -84,7 +91,7 @@
     {
 #if defined(_WIN32)
       ::FreeLibrary((HMODULE)handle_);
-#elif defined(__linux) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__)
+#elif defined(__linux) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
       ::dlclose(handle_);
 #else
 #error Support your platform here
@@ -102,7 +109,7 @@
 
 #if defined(_WIN32)
     return ::GetProcAddress((HMODULE)handle_, name.c_str());
-#elif defined(__linux) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__)
+#elif defined(__linux) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
     return ::dlsym(handle_, name.c_str());
 #else
 #error Support your platform here
--- a/Plugins/Engine/SharedLibrary.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Engine/SharedLibrary.h	Wed Sep 30 13:23:31 2015 +0200
@@ -32,13 +32,19 @@
 
 #pragma once
 
+#if ORTHANC_PLUGINS_ENABLED == 1
+
 #include "../../Core/OrthancException.h"
 
 #include <boost/noncopyable.hpp>
 
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
 namespace Orthanc
 {
-  class SharedLibrary : boost::noncopyable
+  class SharedLibrary : public boost::noncopyable
   {
   public:
 #if defined(_WIN32)
@@ -68,3 +74,5 @@
     FunctionPointer GetFunction(const std::string& name);
   };
 }
+
+#endif
--- a/Plugins/Include/OrthancCDatabasePlugin.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,664 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-
-#pragma once
-
-#include "OrthancCPlugin.h"
-
-
-/** @{ */
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-  typedef struct _OrthancPluginDatabaseContext_t OrthancPluginDatabaseContext;
-
-
-  typedef enum
-  {
-    _OrthancPluginDatabaseAnswerType_None = 0,
-
-    /* Events */
-    _OrthancPluginDatabaseAnswerType_DeletedAttachment = 1,
-    _OrthancPluginDatabaseAnswerType_DeletedResource = 2,
-    _OrthancPluginDatabaseAnswerType_RemainingAncestor = 3,
-
-    /* Return value */
-    _OrthancPluginDatabaseAnswerType_Attachment = 10,
-    _OrthancPluginDatabaseAnswerType_Change = 11,
-    _OrthancPluginDatabaseAnswerType_DicomTag = 12,
-    _OrthancPluginDatabaseAnswerType_ExportedResource = 13,
-    _OrthancPluginDatabaseAnswerType_Int32 = 14,
-    _OrthancPluginDatabaseAnswerType_Int64 = 15,
-    _OrthancPluginDatabaseAnswerType_Resource = 16,
-    _OrthancPluginDatabaseAnswerType_String = 17
-  } _OrthancPluginDatabaseAnswerType;
-
-
-  typedef struct
-  {
-    const char* uuid;
-    int32_t     contentType;
-    uint64_t    uncompressedSize;
-    const char* uncompressedHash;
-    int32_t     compressionType;
-    uint64_t    compressedSize;
-    const char* compressedHash;
-  } OrthancPluginAttachment;
-
-  typedef struct
-  {
-    uint16_t     group;
-    uint16_t     element;
-    const char*  value;
-  } OrthancPluginDicomTag;
-
-  typedef struct
-  {
-    int64_t                    seq;
-    int32_t                    changeType;
-    OrthancPluginResourceType  resourceType;
-    const char*                publicId;
-    const char*                date;
-  } OrthancPluginChange;
-
-  typedef struct
-  {
-    int64_t                    seq;
-    OrthancPluginResourceType  resourceType;
-    const char*                publicId;
-    const char*                modality;
-    const char*                date;
-    const char*                patientId;
-    const char*                studyInstanceUid;
-    const char*                seriesInstanceUid;
-    const char*                sopInstanceUid;
-  } OrthancPluginExportedResource;
-
-
-  typedef struct
-  {
-    OrthancPluginDatabaseContext* database;
-    _OrthancPluginDatabaseAnswerType  type;
-    int32_t      valueInt32;
-    uint32_t     valueUint32;
-    int64_t      valueInt64;
-    const char  *valueString;
-    const void  *valueGeneric;
-  } _OrthancPluginDatabaseAnswer;
-
-  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerString(
-    OrthancPluginContext*          context,
-    OrthancPluginDatabaseContext*  database,
-    const char*                    value)
-  {
-    _OrthancPluginDatabaseAnswer params;
-    memset(&params, 0, sizeof(params));
-    params.database = database;
-    params.type = _OrthancPluginDatabaseAnswerType_String;
-    params.valueString = value;
-    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
-  }
-
-  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerChange(
-    OrthancPluginContext*          context,
-    OrthancPluginDatabaseContext*  database,
-    const OrthancPluginChange*     change)
-  {
-    _OrthancPluginDatabaseAnswer params;
-    memset(&params, 0, sizeof(params));
-
-    params.database = database;
-    params.type = _OrthancPluginDatabaseAnswerType_Change;
-    params.valueUint32 = 0;
-    params.valueGeneric = change;
-
-    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
-  }
-
-  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerChangesDone(
-    OrthancPluginContext*          context,
-    OrthancPluginDatabaseContext*  database)
-  {
-    _OrthancPluginDatabaseAnswer params;
-    memset(&params, 0, sizeof(params));
-
-    params.database = database;
-    params.type = _OrthancPluginDatabaseAnswerType_Change;
-    params.valueUint32 = 1;
-    params.valueGeneric = NULL;
-
-    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
-  }
-
-  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerInt32(
-    OrthancPluginContext*          context,
-    OrthancPluginDatabaseContext*  database,
-    int32_t                        value)
-  {
-    _OrthancPluginDatabaseAnswer params;
-    memset(&params, 0, sizeof(params));
-    params.database = database;
-    params.type = _OrthancPluginDatabaseAnswerType_Int32;
-    params.valueInt32 = value;
-    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
-  }
-
-  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerInt64(
-    OrthancPluginContext*          context,
-    OrthancPluginDatabaseContext*  database,
-    int64_t                        value)
-  {
-    _OrthancPluginDatabaseAnswer params;
-    memset(&params, 0, sizeof(params));
-    params.database = database;
-    params.type = _OrthancPluginDatabaseAnswerType_Int64;
-    params.valueInt64 = value;
-    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
-  }
-
-  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerExportedResource(
-    OrthancPluginContext*                 context,
-    OrthancPluginDatabaseContext*         database,
-    const OrthancPluginExportedResource*  exported)
-  {
-    _OrthancPluginDatabaseAnswer params;
-    memset(&params, 0, sizeof(params));
-
-    params.database = database;
-    params.type = _OrthancPluginDatabaseAnswerType_ExportedResource;
-    params.valueUint32 = 0;
-    params.valueGeneric = exported;
-    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
-  }
-
-  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerExportedResourcesDone(
-    OrthancPluginContext*                 context,
-    OrthancPluginDatabaseContext*         database)
-  {
-    _OrthancPluginDatabaseAnswer params;
-    memset(&params, 0, sizeof(params));
-
-    params.database = database;
-    params.type = _OrthancPluginDatabaseAnswerType_ExportedResource;
-    params.valueUint32 = 1;
-    params.valueGeneric = NULL;
-    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
-  }
-
-  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerDicomTag(
-    OrthancPluginContext*          context,
-    OrthancPluginDatabaseContext*  database,
-    const OrthancPluginDicomTag*   tag)
-  {
-    _OrthancPluginDatabaseAnswer params;
-    memset(&params, 0, sizeof(params));
-    params.database = database;
-    params.type = _OrthancPluginDatabaseAnswerType_DicomTag;
-    params.valueGeneric = tag;
-    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
-  }
-
-  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerAttachment(
-    OrthancPluginContext*          context,
-    OrthancPluginDatabaseContext*  database,
-    const OrthancPluginAttachment* attachment)
-  {
-    _OrthancPluginDatabaseAnswer params;
-    memset(&params, 0, sizeof(params));
-    params.database = database;
-    params.type = _OrthancPluginDatabaseAnswerType_Attachment;
-    params.valueGeneric = attachment;
-    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
-  }
-
-  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerResource(
-    OrthancPluginContext*          context,
-    OrthancPluginDatabaseContext*  database,
-    int64_t                        id,
-    OrthancPluginResourceType      resourceType)
-  {
-    _OrthancPluginDatabaseAnswer params;
-    memset(&params, 0, sizeof(params));
-    params.database = database;
-    params.type = _OrthancPluginDatabaseAnswerType_Resource;
-    params.valueInt64 = id;
-    params.valueInt32 = (int32_t) resourceType;
-    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
-  }
-
-  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseSignalDeletedAttachment(
-    OrthancPluginContext*          context,
-    OrthancPluginDatabaseContext*  database,
-    const OrthancPluginAttachment* attachment)
-  {
-    _OrthancPluginDatabaseAnswer params;
-    memset(&params, 0, sizeof(params));
-    params.database = database;
-    params.type = _OrthancPluginDatabaseAnswerType_DeletedAttachment;
-    params.valueGeneric = attachment;
-    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
-  }
-
-  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseSignalDeletedResource(
-    OrthancPluginContext*          context,
-    OrthancPluginDatabaseContext*  database,
-    const char*                    publicId,
-    OrthancPluginResourceType      resourceType)
-  {
-    _OrthancPluginDatabaseAnswer params;
-    memset(&params, 0, sizeof(params));
-    params.database = database;
-    params.type = _OrthancPluginDatabaseAnswerType_DeletedResource;
-    params.valueString = publicId;
-    params.valueInt32 = (int32_t) resourceType;
-    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
-  }
-
-  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseSignalRemainingAncestor(
-    OrthancPluginContext*          context,
-    OrthancPluginDatabaseContext*  database,
-    const char*                    ancestorId,
-    OrthancPluginResourceType      ancestorType)
-  {
-    _OrthancPluginDatabaseAnswer params;
-    memset(&params, 0, sizeof(params));
-    params.database = database;
-    params.type = _OrthancPluginDatabaseAnswerType_RemainingAncestor;
-    params.valueString = ancestorId;
-    params.valueInt32 = (int32_t) ancestorType;
-    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
-  }
-
-
-
-
-
-  typedef struct
-  {
-    int32_t  (*addAttachment) (
-      /* inputs */
-      void* payload,
-      int64_t id,
-      const OrthancPluginAttachment* attachment);
-                             
-    int32_t  (*attachChild) (
-      /* inputs */
-      void* payload,
-      int64_t parent,
-      int64_t child);
-                             
-    int32_t  (*clearChanges) (
-      /* inputs */
-      void* payload);
-                             
-    int32_t  (*clearExportedResources) (
-      /* inputs */
-      void* payload);
-
-    int32_t  (*createResource) (
-      /* outputs */
-      int64_t* id, 
-      /* inputs */
-      void* payload,
-      const char* publicId,
-      OrthancPluginResourceType resourceType);           
-                   
-    int32_t  (*deleteAttachment) (
-      /* inputs */
-      void* payload,
-      int64_t id,
-      int32_t contentType);
-   
-    int32_t  (*deleteMetadata) (
-      /* inputs */
-      void* payload,
-      int64_t id,
-      int32_t metadataType);
-   
-    int32_t  (*deleteResource) (
-      /* inputs */
-      void* payload,
-      int64_t id);    
-
-    /* Output: Use OrthancPluginDatabaseAnswerString() */
-    int32_t  (*getAllPublicIds) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload,
-      OrthancPluginResourceType resourceType);
-
-    /* Output: Use OrthancPluginDatabaseAnswerChange() and
-     * OrthancPluginDatabaseAnswerChangesDone() */
-    int32_t  (*getChanges) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload,
-      int64_t since,
-      uint32_t maxResult);
-
-    /* Output: Use OrthancPluginDatabaseAnswerInt64() */
-    int32_t  (*getChildrenInternalId) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload,
-      int64_t id);
-                   
-    /* Output: Use OrthancPluginDatabaseAnswerString() */
-    int32_t  (*getChildrenPublicId) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload,
-      int64_t id);
-
-    /* Output: Use OrthancPluginDatabaseAnswerExportedResource() and
-     * OrthancPluginDatabaseAnswerExportedResourcesDone() */
-    int32_t  (*getExportedResources) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload,
-      int64_t  since,
-      uint32_t  maxResult);
-                   
-    /* Output: Use OrthancPluginDatabaseAnswerChange() */
-    int32_t  (*getLastChange) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload);
-
-    /* Output: Use OrthancPluginDatabaseAnswerExportedResource() */
-    int32_t  (*getLastExportedResource) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload);
-                   
-    /* Output: Use OrthancPluginDatabaseAnswerDicomTag() */
-    int32_t  (*getMainDicomTags) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload,
-      int64_t id);
-                   
-    /* Output: Use OrthancPluginDatabaseAnswerString() */
-    int32_t  (*getPublicId) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload,
-      int64_t id);
-
-    int32_t  (*getResourceCount) (
-      /* outputs */
-      uint64_t* target,
-      /* inputs */
-      void* payload,
-      OrthancPluginResourceType  resourceType);
-                   
-    int32_t  (*getResourceType) (
-      /* outputs */
-      OrthancPluginResourceType* resourceType,
-      /* inputs */
-      void* payload,
-      int64_t id);
-
-    int32_t  (*getTotalCompressedSize) (
-      /* outputs */
-      uint64_t* target,
-      /* inputs */
-      void* payload);
-                   
-    int32_t  (*getTotalUncompressedSize) (
-      /* outputs */
-      uint64_t* target,
-      /* inputs */
-      void* payload);
-                   
-    int32_t  (*isExistingResource) (
-      /* outputs */
-      int32_t* existing,
-      /* inputs */
-      void* payload,
-      int64_t id);
-
-    int32_t  (*isProtectedPatient) (
-      /* outputs */
-      int32_t* isProtected,
-      /* inputs */
-      void* payload,
-      int64_t id);
-
-    /* Output: Use OrthancPluginDatabaseAnswerInt32() */
-    int32_t  (*listAvailableMetadata) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload,
-      int64_t id);
-                   
-    /* Output: Use OrthancPluginDatabaseAnswerInt32() */
-    int32_t  (*listAvailableAttachments) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload,
-      int64_t id);
-
-    int32_t  (*logChange) (
-      /* inputs */
-      void* payload,
-      const OrthancPluginChange* change);
-                   
-    int32_t  (*logExportedResource) (
-      /* inputs */
-      void* payload,
-      const OrthancPluginExportedResource* exported);
-                   
-    /* Output: Use OrthancPluginDatabaseAnswerAttachment() */
-    int32_t  (*lookupAttachment) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload,
-      int64_t id,
-      int32_t contentType);
-
-    /* Output: Use OrthancPluginDatabaseAnswerString() */
-    int32_t  (*lookupGlobalProperty) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload,
-      int32_t property);
-
-    /* Output: Use OrthancPluginDatabaseAnswerInt64() */
-    int32_t  (*lookupIdentifier) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload,
-      const OrthancPluginDicomTag* tag);
-
-    /* Output: Use OrthancPluginDatabaseAnswerInt64() */
-    int32_t  (*lookupIdentifier2) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload,
-      const char* value);
-
-    /* Output: Use OrthancPluginDatabaseAnswerString() */
-    int32_t  (*lookupMetadata) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload,
-      int64_t id,
-      int32_t metadata);
-
-    /* Output: Use OrthancPluginDatabaseAnswerInt64() */
-    int32_t  (*lookupParent) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload,
-      int64_t id);
-
-    /* Output: Use OrthancPluginDatabaseAnswerResource() */
-    int32_t  (*lookupResource) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload,
-      const char* publicId);
-
-    /* Output: Use OrthancPluginDatabaseAnswerInt64() */
-    int32_t  (*selectPatientToRecycle) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload);
-
-    /* Output: Use OrthancPluginDatabaseAnswerInt64() */
-    int32_t  (*selectPatientToRecycle2) (
-      /* outputs */
-      OrthancPluginDatabaseContext* context,
-      /* inputs */
-      void* payload,
-      int64_t patientIdToAvoid);
-
-    int32_t  (*setGlobalProperty) (
-      /* inputs */
-      void* payload,
-      int32_t property,
-      const char* value);
-
-    int32_t  (*setMainDicomTag) (
-      /* inputs */
-      void* payload,
-      int64_t id,
-      const OrthancPluginDicomTag* tag);
-
-    int32_t  (*setIdentifierTag) (
-      /* inputs */
-      void* payload,
-      int64_t id,
-      const OrthancPluginDicomTag* tag);
-
-    int32_t  (*setMetadata) (
-      /* inputs */
-      void* payload,
-      int64_t id,
-      int32_t metadata,
-      const char* value);
-
-    int32_t  (*setProtectedPatient) (
-      /* inputs */
-      void* payload,
-      int64_t id,
-      int32_t isProtected);
-
-    int32_t (*startTransaction) (
-      /* inputs */
-      void* payload);
-
-    int32_t (*rollbackTransaction) (
-      /* inputs */
-      void* payload);
-
-    int32_t (*commitTransaction) (
-      /* inputs */
-      void* payload);
-
-    int32_t (*open) (
-      /* inputs */
-      void* payload);
-
-    int32_t (*close) (
-      /* inputs */
-      void* payload);
-
-  } OrthancPluginDatabaseBackend;
-
-
-
-  typedef struct
-  {
-    OrthancPluginDatabaseContext**       result;
-    const OrthancPluginDatabaseBackend*  backend;
-    void*                                payload;
-  } _OrthancPluginRegisterDatabaseBackend;
-
-  ORTHANC_PLUGIN_INLINE OrthancPluginDatabaseContext* OrthancPluginRegisterDatabaseBackend(
-    OrthancPluginContext*                context,
-    const OrthancPluginDatabaseBackend*  backend,
-    void*                                payload)
-  {
-    OrthancPluginDatabaseContext* result = NULL;
-
-    _OrthancPluginRegisterDatabaseBackend params;
-    memset(&params, 0, sizeof(params));
-    params.backend = backend;
-    params.result = &result;
-    params.payload = payload;
-
-    if (context->InvokeService(context, _OrthancPluginService_RegisterDatabaseBackend, &params) ||
-        result == NULL)
-    {
-      /* Error */
-      return NULL;
-    }
-    else
-    {
-      return result;
-    }
-  }
-
-
-
-#ifdef  __cplusplus
-}
-#endif
-
-
-/** @} */
-
--- a/Plugins/Include/OrthancCPlugin.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2093 +0,0 @@
-/**
- * \mainpage
- *
- * This C/C++ SDK allows external developers to create plugins that
- * can be loaded into Orthanc to extend its functionality. Each
- * Orthanc plugin must expose 4 public functions with the following
- * signatures:
- * 
- * -# <tt>int32_t OrthancPluginInitialize(const OrthancPluginContext* context)</tt>:
- *    This function is invoked by Orthanc when it loads the plugin on startup.
- *    The plugin must:
- *    - Check its compatibility with the Orthanc version using
- *      ::OrthancPluginCheckVersion().
- *    - Store the context pointer so that it can use the plugin 
- *      services of Orthanc.
- *    - Register all its REST callbacks using ::OrthancPluginRegisterRestCallback().
- *    - Register all its callbacks for received instances using ::OrthancPluginRegisterOnStoredInstanceCallback().
- *    - Possibly register a custom storage area using ::OrthancPluginRegisterStorageArea().
- *    - Possibly register a custom database back-end area using ::OrthancPluginRegisterDatabaseBackend().
- * -# <tt>void OrthancPluginFinalize()</tt>:
- *    This function is invoked by Orthanc during its shutdown. The plugin
- *    must free all its memory.
- * -# <tt>const char* OrthancPluginGetName()</tt>:
- *    The plugin must return a short string to identify itself.
- * -# <tt>const char* OrthancPluginGetVersion()</tt>:
- *    The plugin must return a string containing its version number.
- *
- * The name and the version of a plugin is only used to prevent it
- * from being loaded twice.
- * 
- * The various callbacks are guaranteed to be executed in mutual
- * exclusion since Orthanc 0.8.5.
- **/
-
-
-
-/**
- * @defgroup CInterface C Interface 
- * @brief The C interface to create Orthanc plugins.
- * 
- * These functions must be used to create C plugins for Orthanc.
- **/
-
-
-
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-
-#pragma once
-
-
-#include <stdio.h>
-#include <string.h>
-
-#ifdef WIN32
-#define ORTHANC_PLUGINS_API __declspec(dllexport)
-#else
-#define ORTHANC_PLUGINS_API
-#endif
-
-#define ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER     0
-#define ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER     8
-#define ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER  6
-
-
-
-/********************************************************************
- ** Check that function inlining is properly supported. The use of
- ** inlining is required, to avoid the duplication of object code
- ** between two compilation modules that would use the Orthanc Plugin
- ** API.
- ********************************************************************/
-
-/* If the auto-detection of the "inline" keyword below does not work
-   automatically and that your compiler is known to properly support
-   inlining, uncomment the following #define and adapt the definition
-   of "static inline". */
-
-/* #define ORTHANC_PLUGIN_INLINE static inline */
-
-#ifndef ORTHANC_PLUGIN_INLINE
-#  if __STDC_VERSION__ >= 199901L
-/*   This is C99 or above: http://predef.sourceforge.net/prestd.html */
-#    define ORTHANC_PLUGIN_INLINE static inline
-#  elif defined(__cplusplus)
-/*   This is C++ */
-#    define ORTHANC_PLUGIN_INLINE static inline
-#  elif defined(__GNUC__)
-/*   This is GCC running in C89 mode */
-#    define ORTHANC_PLUGIN_INLINE static __inline
-#  elif defined(_MSC_VER)
-/*   This is Visual Studio running in C89 mode */
-#    define ORTHANC_PLUGIN_INLINE static __inline
-#  else
-#    error Your compiler is not known to support the "inline" keyword
-#  endif
-#endif
-
-
-
-/********************************************************************
- ** Inclusion of standard libraries.
- ********************************************************************/
-
-#ifdef _MSC_VER
-#include "../../Resources/ThirdParty/VisualStudio/stdint.h"
-#else
-#include <stdint.h>
-#endif
-
-#include <stdlib.h>
-
-
-
-/********************************************************************
- ** Definition of the Orthanc Plugin API.
- ********************************************************************/
-
-/** @{ */
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-  /**
-   * Forward declaration of one of the mandatory functions for Orthanc
-   * plugins.
-   **/
-  ORTHANC_PLUGINS_API const char* OrthancPluginGetName();
-
-
-  /**
-   * The various HTTP methods for a REST call.
-   **/
-  typedef enum
-  {
-    OrthancPluginHttpMethod_Get = 1,    /*!< GET request */
-    OrthancPluginHttpMethod_Post = 2,   /*!< POST request */
-    OrthancPluginHttpMethod_Put = 3,    /*!< PUT request */
-    OrthancPluginHttpMethod_Delete = 4  /*!< DELETE request */
-  } OrthancPluginHttpMethod;
-
-
-  /**
-   * @brief The parameters of a REST request.
-   **/
-  typedef struct
-  {
-    /**
-     * @brief The HTTP method.
-     **/
-    OrthancPluginHttpMethod method;    
-
-    /**
-     * @brief The number of groups of the regular expression.
-     **/
-    uint32_t                groupsCount;
-
-    /**
-     * @brief The matched values for the groups of the regular expression.
-     **/
-    const char* const*      groups;
-
-    /**
-     * @brief For a GET request, the number of GET parameters.
-     **/
-    uint32_t                getCount;
-
-    /**
-     * @brief For a GET request, the keys of the GET parameters.
-     **/
-    const char* const*      getKeys;
-
-    /**
-     * @brief For a GET request, the values of the GET parameters.
-     **/
-    const char* const*      getValues;
-
-    /**
-     * @brief For a PUT or POST request, the content of the body.
-     **/
-    const char*             body;
-
-    /**
-     * @brief For a PUT or POST request, the number of bytes of the body.
-     **/
-    uint32_t                bodySize;
-
-
-    /* --------------------------------------------------
-       New in version 0.8.1
-       -------------------------------------------------- */
-
-    /**
-     * @brief The number of HTTP headers.
-     **/
-    uint32_t                headersCount;
-
-    /**
-     * @brief The keys of the HTTP headers (always converted to low-case).
-     **/
-    const char* const*      headersKeys;
-
-    /**
-     * @brief The values of the HTTP headers.
-     **/
-    const char* const*      headersValues;
-
-  } OrthancPluginHttpRequest;
-
-
-  typedef enum 
-  {
-    /* Generic services */
-    _OrthancPluginService_LogInfo = 1,
-    _OrthancPluginService_LogWarning = 2,
-    _OrthancPluginService_LogError = 3,
-    _OrthancPluginService_GetOrthancPath = 4,
-    _OrthancPluginService_GetOrthancDirectory = 5,
-    _OrthancPluginService_GetConfigurationPath = 6,
-    _OrthancPluginService_SetPluginProperty = 7,
-    _OrthancPluginService_GetGlobalProperty = 8,
-    _OrthancPluginService_SetGlobalProperty = 9,
-    _OrthancPluginService_GetCommandLineArgumentsCount = 10,
-    _OrthancPluginService_GetCommandLineArgument = 11,
-
-    /* Registration of callbacks */
-    _OrthancPluginService_RegisterRestCallback = 1000,
-    _OrthancPluginService_RegisterOnStoredInstanceCallback = 1001,
-    _OrthancPluginService_RegisterStorageArea = 1002,
-    _OrthancPluginService_RegisterOnChangeCallback = 1003,
-
-    /* Sending answers to REST calls */
-    _OrthancPluginService_AnswerBuffer = 2000,
-    _OrthancPluginService_CompressAndAnswerPngImage = 2001,
-    _OrthancPluginService_Redirect = 2002,
-    _OrthancPluginService_SendHttpStatusCode = 2003,
-    _OrthancPluginService_SendUnauthorized = 2004,
-    _OrthancPluginService_SendMethodNotAllowed = 2005,
-    _OrthancPluginService_SetCookie = 2006,
-    _OrthancPluginService_SetHttpHeader = 2007,
-
-    /* Access to the Orthanc database and API */
-    _OrthancPluginService_GetDicomForInstance = 3000,
-    _OrthancPluginService_RestApiGet = 3001,
-    _OrthancPluginService_RestApiPost = 3002,
-    _OrthancPluginService_RestApiDelete = 3003,
-    _OrthancPluginService_RestApiPut = 3004,
-    _OrthancPluginService_LookupPatient = 3005,
-    _OrthancPluginService_LookupStudy = 3006,
-    _OrthancPluginService_LookupSeries = 3007,
-    _OrthancPluginService_LookupInstance = 3008,
-    _OrthancPluginService_LookupStudyWithAccessionNumber = 3009,
-    _OrthancPluginService_RestApiGetAfterPlugins = 3010,
-    _OrthancPluginService_RestApiPostAfterPlugins = 3011,
-    _OrthancPluginService_RestApiDeleteAfterPlugins = 3012,
-    _OrthancPluginService_RestApiPutAfterPlugins = 3013,
-
-    /* Access to DICOM instances */
-    _OrthancPluginService_GetInstanceRemoteAet = 4000,
-    _OrthancPluginService_GetInstanceSize = 4001,
-    _OrthancPluginService_GetInstanceData = 4002,
-    _OrthancPluginService_GetInstanceJson = 4003,
-    _OrthancPluginService_GetInstanceSimplifiedJson = 4004,
-    _OrthancPluginService_HasInstanceMetadata = 4005,
-    _OrthancPluginService_GetInstanceMetadata = 4006,
-
-    /* Services for plugins implementing a database back-end */
-    _OrthancPluginService_RegisterDatabaseBackend = 5000,
-    _OrthancPluginService_DatabaseAnswer = 5001
-
-  } _OrthancPluginService;
-
-
-  typedef enum
-  {
-    _OrthancPluginProperty_Description = 1,
-    _OrthancPluginProperty_RootUri = 2,
-    _OrthancPluginProperty_OrthancExplorer = 3
-  } _OrthancPluginProperty;
-
-
-
-  /**
-   * The memory layout of the pixels of an image.
-   **/
-  typedef enum
-  {
-    /**
-     * @brief Graylevel 8bpp image.
-     *
-     * The image is graylevel. Each pixel is unsigned and stored in
-     * one byte.
-     **/
-    OrthancPluginPixelFormat_Grayscale8 = 1,
-
-    /**
-     * @brief Graylevel, unsigned 16bpp image.
-     *
-     * The image is graylevel. Each pixel is unsigned and stored in
-     * two bytes.
-     **/
-    OrthancPluginPixelFormat_Grayscale16 = 2,
-
-    /**
-     * @brief Graylevel, signed 16bpp image.
-     *
-     * The image is graylevel. Each pixel is signed and stored in two
-     * bytes.
-     **/
-    OrthancPluginPixelFormat_SignedGrayscale16 = 3,
-
-    /**
-     * @brief Color image in RGB24 format.
-     *
-     * This format describes a color image. The pixels are stored in 3
-     * consecutive bytes. The memory layout is RGB.
-     **/
-    OrthancPluginPixelFormat_RGB24 = 4,
-
-    /**
-     * @brief Color image in RGBA32 format.
-     *
-     * This format describes a color image. The pixels are stored in 4
-     * consecutive bytes. The memory layout is RGBA.
-     **/
-    OrthancPluginPixelFormat_RGBA32 = 5
-  } OrthancPluginPixelFormat;
-
-
-
-  /**
-   * The content types that are supported by Orthanc plugins.
-   **/
-  typedef enum
-  {
-    OrthancPluginContentType_Unknown = 0,      /*!< Unknown content type */
-    OrthancPluginContentType_Dicom = 1,        /*!< DICOM */
-    OrthancPluginContentType_DicomAsJson = 2   /*!< JSON summary of a DICOM file */
-  } OrthancPluginContentType;
-
-
-
-  /**
-   * The supported type of DICOM resources.
-   **/
-  typedef enum
-  {
-    OrthancPluginResourceType_Patient = 0,     /*!< Patient */
-    OrthancPluginResourceType_Study = 1,       /*!< Study */
-    OrthancPluginResourceType_Series = 2,      /*!< Series */
-    OrthancPluginResourceType_Instance = 3     /*!< Instance */
-  } OrthancPluginResourceType;
-
-
-
-  /**
-   * The supported type of changes that can happen to DICOM resources.
-   **/
-  typedef enum
-  {
-    OrthancPluginChangeType_CompletedSeries = 0,    /*!< Series is now complete */
-    OrthancPluginChangeType_Deleted = 1,            /*!< Deleted resource */
-    OrthancPluginChangeType_NewChildInstance = 2,   /*!< A new instance was added to this resource */
-    OrthancPluginChangeType_NewInstance = 3,        /*!< New instance received */
-    OrthancPluginChangeType_NewPatient = 4,         /*!< New patient created */
-    OrthancPluginChangeType_NewSeries = 5,          /*!< New series created */
-    OrthancPluginChangeType_NewStudy = 6,           /*!< New study created */
-    OrthancPluginChangeType_StablePatient = 7,      /*!< Timeout: No new instance in this patient */
-    OrthancPluginChangeType_StableSeries = 8,       /*!< Timeout: No new instance in this series */
-    OrthancPluginChangeType_StableStudy = 9         /*!< Timeout: No new instance in this study */
-  } OrthancPluginChangeType;
-
-
-
-  /**
-   * @brief A memory buffer allocated by the core system of Orthanc.
-   *
-   * A memory buffer allocated by the core system of Orthanc. When the
-   * content of the buffer is not useful anymore, it must be free by a
-   * call to ::OrthancPluginFreeMemoryBuffer().
-   **/
-  typedef struct
-  {
-    /**
-     * @brief The content of the buffer.
-     **/
-    void*      data;
-
-    /**
-     * @brief The number of bytes in the buffer.
-     **/
-    uint32_t   size;
-  } OrthancPluginMemoryBuffer;
-
-
-
-
-  /**
-   * @brief Opaque structure that represents the HTTP connection to the client application.
-   **/
-  typedef struct _OrthancPluginRestOutput_t OrthancPluginRestOutput;
-
-
-
-  /**
-   * @brief Opaque structure that represents a DICOM instance received by Orthanc.
-   **/
-  typedef struct _OrthancPluginDicomInstance_t OrthancPluginDicomInstance;
-
-
-
-  /**
-   * @brief Signature of a callback function that answers to a REST request.
-   **/
-  typedef int32_t (*OrthancPluginRestCallback) (
-    OrthancPluginRestOutput* output,
-    const char* url,
-    const OrthancPluginHttpRequest* request);
-
-
-
-  /**
-   * @brief Signature of a callback function that is triggered when Orthanc receives a DICOM instance.
-   **/
-  typedef int32_t (*OrthancPluginOnStoredInstanceCallback) (
-    OrthancPluginDicomInstance* instance,
-    const char* instanceId);
-
-
-
-  /**
-   * @brief Signature of a callback function that is triggered when a change happens to some DICOM resource.
-   **/
-  typedef int32_t (*OrthancPluginOnChangeCallback) (
-    OrthancPluginChangeType changeType,
-    OrthancPluginResourceType resourceType,
-    const char* resourceId);
-
-
-
-  /**
-   * @brief Signature of a function to free dynamic memory.
-   **/
-  typedef void (*OrthancPluginFree) (void* buffer);
-
-
-
-  /**
-   * @brief Callback for writing to the storage area.
-   *
-   * Signature of a callback function that is triggered when Orthanc writes a file to the storage area.
-   *
-   * @param uuid The UUID of the file.
-   * @param content The content of the file.
-   * @param size The size of the file.
-   * @param type The content type corresponding to this file. 
-   * @return 0 if success, other value if error.
-   **/
-  typedef int32_t (*OrthancPluginStorageCreate) (
-    const char* uuid,
-    const void* content,
-    int64_t size,
-    OrthancPluginContentType type);
-
-
-
-  /**
-   * @brief Callback for reading from the storage area.
-   *
-   * Signature of a callback function that is triggered when Orthanc reads a file from the storage area.
-   *
-   * @param content The content of the file (output).
-   * @param size The size of the file (output).
-   * @param uuid The UUID of the file of interest.
-   * @param type The content type corresponding to this file. 
-   * @return 0 if success, other value if error.
-   **/
-  typedef int32_t (*OrthancPluginStorageRead) (
-    void** content,
-    int64_t* size,
-    const char* uuid,
-    OrthancPluginContentType type);
-
-
-
-  /**
-   * @brief Callback for removing a file from the storage area.
-   *
-   * Signature of a callback function that is triggered when Orthanc deletes a file from the storage area.
-   *
-   * @param uuid The UUID of the file to be removed.
-   * @param type The content type corresponding to this file. 
-   * @return 0 if success, other value if error.
-   **/
-  typedef int32_t (*OrthancPluginStorageRemove) (
-    const char* uuid,
-    OrthancPluginContentType type);
-
-
-
-  /**
-   * @brief Data structure that contains information about the Orthanc core.
-   **/
-  typedef struct _OrthancPluginContext_t
-  {
-    void*              pluginsManager;
-    const char*        orthancVersion;
-    OrthancPluginFree  Free;
-    int32_t          (*InvokeService) (struct _OrthancPluginContext_t* context,
-                                       _OrthancPluginService service,
-                                       const void* params);
-  } OrthancPluginContext;
-
-
-
-  /**
-   * @brief Free a string.
-   * 
-   * Free a string that was allocated by the core system of Orthanc.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param str The string to be freed.
-   **/
-  ORTHANC_PLUGIN_INLINE void  OrthancPluginFreeString(
-    OrthancPluginContext* context, 
-    char* str)
-  {
-    if (str != NULL)
-    {
-      context->Free(str);
-    }
-  }
-
-
-  /**
-   * @brief Check the compatibility of the plugin wrt. the version of its hosting Orthanc.
-   * 
-   * This function checks whether the version of this C header is
-   * compatible with the current version of Orthanc. The result of
-   * this function should always be checked in the
-   * OrthancPluginInitialize() entry point of the plugin.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @return 1 if and only if the versions are compatible. If the
-   * result is 0, the initialization of the plugin should fail.
-   **/
-  ORTHANC_PLUGIN_INLINE int  OrthancPluginCheckVersion(
-    OrthancPluginContext* context)
-  {
-    int major, minor, revision;
-
-    /* Assume compatibility with the mainline */
-    if (!strcmp(context->orthancVersion, "mainline"))
-    {
-      return 1;
-    }
-
-    /* Parse the version of the Orthanc core */
-    if ( 
-#ifdef _MSC_VER
-      sscanf_s
-#else
-      sscanf
-#endif
-      (context->orthancVersion, "%4d.%4d.%4d", &major, &minor, &revision) != 3)
-    {
-      return 0;
-    }
-
-    /* Check the major number of the version */
-
-    if (major > ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER)
-    {
-      return 1;
-    }
-
-    if (major < ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER)
-    {
-      return 0;
-    }
-
-    /* Check the minor number of the version */
-
-    if (minor > ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER)
-    {
-      return 1;
-    }
-
-    if (minor < ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER)
-    {
-      return 0;
-    }
-
-    /* Check the revision number of the version */
-
-    if (revision >= ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER)
-    {
-      return 1;
-    }
-    else
-    {
-      return 0;
-    }
-  }
-
-
-  /**
-   * @brief Free a memory buffer.
-   * 
-   * Free a memory buffer that was allocated by the core system of Orthanc.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param buffer The memory buffer to release.
-   **/
-  ORTHANC_PLUGIN_INLINE void  OrthancPluginFreeMemoryBuffer(
-    OrthancPluginContext* context, 
-    OrthancPluginMemoryBuffer* buffer)
-  {
-    context->Free(buffer->data);
-  }
-
-
-  /**
-   * @brief Log an error.
-   *
-   * Log an error message using the Orthanc logging system.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param message The message to be logged.
-   **/
-  ORTHANC_PLUGIN_INLINE void OrthancPluginLogError(
-    OrthancPluginContext* context,
-    const char* message)
-  {
-    context->InvokeService(context, _OrthancPluginService_LogError, message);
-  }
-
-
-  /**
-   * @brief Log a warning.
-   *
-   * Log a warning message using the Orthanc logging system.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param message The message to be logged.
-   **/
-  ORTHANC_PLUGIN_INLINE void OrthancPluginLogWarning(
-    OrthancPluginContext* context,
-    const char* message)
-  {
-    context->InvokeService(context, _OrthancPluginService_LogWarning, message);
-  }
-
-
-  /**
-   * @brief Log an information.
-   *
-   * Log an information message using the Orthanc logging system.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param message The message to be logged.
-   **/
-  ORTHANC_PLUGIN_INLINE void OrthancPluginLogInfo(
-    OrthancPluginContext* context,
-    const char* message)
-  {
-    context->InvokeService(context, _OrthancPluginService_LogInfo, message);
-  }
-
-
-
-  typedef struct
-  {
-    const char* pathRegularExpression;
-    OrthancPluginRestCallback callback;
-  } _OrthancPluginRestCallback;
-
-  /**
-   * @brief Register a REST callback.
-   *
-   * This function registers a REST callback against a regular
-   * expression for a URI. This function must be called during the
-   * initialization of the plugin, i.e. inside the
-   * OrthancPluginInitialize() public function.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param pathRegularExpression Regular expression for the URI. May contain groups.
-   * @param callback The callback function to handle the REST call.
-   **/
-  ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterRestCallback(
-    OrthancPluginContext*     context,
-    const char*               pathRegularExpression,
-    OrthancPluginRestCallback callback)
-  {
-    _OrthancPluginRestCallback params;
-    params.pathRegularExpression = pathRegularExpression;
-    params.callback = callback;
-    context->InvokeService(context, _OrthancPluginService_RegisterRestCallback, &params);
-  }
-
-
-
-  typedef struct
-  {
-    OrthancPluginOnStoredInstanceCallback callback;
-  } _OrthancPluginOnStoredInstanceCallback;
-
-  /**
-   * @brief Register a callback for received instances.
-   *
-   * This function registers a callback function that is called
-   * whenever a new DICOM instance is stored into the Orthanc core.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param callback The callback function.
-   **/
-  ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterOnStoredInstanceCallback(
-    OrthancPluginContext*                  context,
-    OrthancPluginOnStoredInstanceCallback  callback)
-  {
-    _OrthancPluginOnStoredInstanceCallback params;
-    params.callback = callback;
-
-    context->InvokeService(context, _OrthancPluginService_RegisterOnStoredInstanceCallback, &params);
-  }
-
-
-
-  typedef struct
-  {
-    OrthancPluginRestOutput* output;
-    const char*              answer;
-    uint32_t                 answerSize;
-    const char*              mimeType;
-  } _OrthancPluginAnswerBuffer;
-
-  /**
-   * @brief Answer to a REST request.
-   *
-   * This function answers to a REST request with the content of a memory buffer.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param output The HTTP connection to the client application.
-   * @param answer Pointer to the memory buffer containing the answer.
-   * @param answerSize Number of bytes of the answer.
-   * @param mimeType The MIME type of the answer.
-   **/
-  ORTHANC_PLUGIN_INLINE void OrthancPluginAnswerBuffer(
-    OrthancPluginContext*    context,
-    OrthancPluginRestOutput* output,
-    const char*              answer,
-    uint32_t                 answerSize,
-    const char*              mimeType)
-  {
-    _OrthancPluginAnswerBuffer params;
-    params.output = output;
-    params.answer = answer;
-    params.answerSize = answerSize;
-    params.mimeType = mimeType;
-    context->InvokeService(context, _OrthancPluginService_AnswerBuffer, &params);
-  }
-
-
-  typedef struct
-  {
-    OrthancPluginRestOutput*  output;
-    OrthancPluginPixelFormat  format;
-    uint32_t                  width;
-    uint32_t                  height;
-    uint32_t                  pitch;
-    const void*               buffer;
-  } _OrthancPluginCompressAndAnswerPngImage;
-
-  /**
-   * @brief Answer to a REST request with a PNG image.
-   *
-   * This function answers to a REST request with a PNG image. The
-   * parameters of this function describe a memory buffer that
-   * contains an uncompressed image. The image will be automatically compressed
-   * as a PNG image by the core system of Orthanc.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param output The HTTP connection to the client application.
-   * @param format The memory layout of the uncompressed image.
-   * @param width The width of the image.
-   * @param height The height of the image.
-   * @param pitch The pitch of the image (i.e. the number of bytes
-   * between 2 successive lines of the image in the memory buffer.
-   * @param buffer The memory buffer containing the uncompressed image.
-   **/
-  ORTHANC_PLUGIN_INLINE void OrthancPluginCompressAndAnswerPngImage(
-    OrthancPluginContext*     context,
-    OrthancPluginRestOutput*  output,
-    OrthancPluginPixelFormat  format,
-    uint32_t                  width,
-    uint32_t                  height,
-    uint32_t                  pitch,
-    const void*               buffer)
-  {
-    _OrthancPluginCompressAndAnswerPngImage params;
-    params.output = output;
-    params.format = format;
-    params.width = width;
-    params.height = height;
-    params.pitch = pitch;
-    params.buffer = buffer;
-    context->InvokeService(context, _OrthancPluginService_CompressAndAnswerPngImage, &params);
-  }
-
-
-
-  typedef struct
-  {
-    OrthancPluginMemoryBuffer*  target;
-    const char*                 instanceId;
-  } _OrthancPluginGetDicomForInstance;
-
-  /**
-   * @brief Retrieve a DICOM instance using its Orthanc identifier.
-   * 
-   * Retrieve a DICOM instance using its Orthanc identifier. The DICOM
-   * file is stored into a newly allocated memory buffer.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param target The target memory buffer.
-   * @param instanceId The Orthanc identifier of the DICOM instance of interest.
-   * @return 0 if success, other value if error.
-   **/
-  ORTHANC_PLUGIN_INLINE int  OrthancPluginGetDicomForInstance(
-    OrthancPluginContext*       context,
-    OrthancPluginMemoryBuffer*  target,
-    const char*                 instanceId)
-  {
-    _OrthancPluginGetDicomForInstance params;
-    params.target = target;
-    params.instanceId = instanceId;
-    return context->InvokeService(context, _OrthancPluginService_GetDicomForInstance, &params);
-  }
-
-
-
-  typedef struct
-  {
-    OrthancPluginMemoryBuffer*  target;
-    const char*                 uri;
-  } _OrthancPluginRestApiGet;
-
-  /**
-   * @brief Make a GET call to the built-in Orthanc REST API.
-   * 
-   * Make a GET call to the built-in Orthanc REST API. The result to
-   * the query is stored into a newly allocated memory buffer.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param target The target memory buffer.
-   * @param uri The URI in the built-in Orthanc API.
-   * @return 0 if success, other value if error.
-   **/
-  ORTHANC_PLUGIN_INLINE int  OrthancPluginRestApiGet(
-    OrthancPluginContext*       context,
-    OrthancPluginMemoryBuffer*  target,
-    const char*                 uri)
-  {
-    _OrthancPluginRestApiGet params;
-    params.target = target;
-    params.uri = uri;
-    return context->InvokeService(context, _OrthancPluginService_RestApiGet, &params);
-  }
-
-
-
-  /**
-   * @brief Make a GET call to the REST API, as tainted by the plugins.
-   * 
-   * Make a GET call to the Orthanc REST API, after all the plugins
-   * are applied. In other words, if some plugin overrides or adds the
-   * called URI to the built-in Orthanc REST API, this call will
-   * return the result provided by this plugin. The result to the
-   * query is stored into a newly allocated memory buffer.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param target The target memory buffer.
-   * @param uri The URI in the built-in Orthanc API.
-   * @return 0 if success, other value if error.
-   **/
-  ORTHANC_PLUGIN_INLINE int  OrthancPluginRestApiGetAfterPlugins(
-    OrthancPluginContext*       context,
-    OrthancPluginMemoryBuffer*  target,
-    const char*                 uri)
-  {
-    _OrthancPluginRestApiGet params;
-    params.target = target;
-    params.uri = uri;
-    return context->InvokeService(context, _OrthancPluginService_RestApiGetAfterPlugins, &params);
-  }
-
-
-
-  typedef struct
-  {
-    OrthancPluginMemoryBuffer*  target;
-    const char*                 uri;
-    const char*                 body;
-    uint32_t                    bodySize;
-  } _OrthancPluginRestApiPostPut;
-
-  /**
-   * @brief Make a POST call to the built-in Orthanc REST API.
-   * 
-   * Make a POST call to the built-in Orthanc REST API. The result to
-   * the query is stored into a newly allocated memory buffer.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param target The target memory buffer.
-   * @param uri The URI in the built-in Orthanc API.
-   * @param body The body of the POST request.
-   * @param bodySize The size of the body.
-   * @return 0 if success, other value if error.
-   **/
-  ORTHANC_PLUGIN_INLINE int  OrthancPluginRestApiPost(
-    OrthancPluginContext*       context,
-    OrthancPluginMemoryBuffer*  target,
-    const char*                 uri,
-    const char*                 body,
-    uint32_t                    bodySize)
-  {
-    _OrthancPluginRestApiPostPut params;
-    params.target = target;
-    params.uri = uri;
-    params.body = body;
-    params.bodySize = bodySize;
-    return context->InvokeService(context, _OrthancPluginService_RestApiPost, &params);
-  }
-
-
-  /**
-   * @brief Make a POST call to the REST API, as tainted by the plugins.
-   * 
-   * Make a POST call to the Orthanc REST API, after all the plugins
-   * are applied. In other words, if some plugin overrides or adds the
-   * called URI to the built-in Orthanc REST API, this call will
-   * return the result provided by this plugin. The result to the
-   * query is stored into a newly allocated memory buffer.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param target The target memory buffer.
-   * @param uri The URI in the built-in Orthanc API.
-   * @param body The body of the POST request.
-   * @param bodySize The size of the body.
-   * @return 0 if success, other value if error.
-   **/
-  ORTHANC_PLUGIN_INLINE int  OrthancPluginRestApiPostAfterPlugins(
-    OrthancPluginContext*       context,
-    OrthancPluginMemoryBuffer*  target,
-    const char*                 uri,
-    const char*                 body,
-    uint32_t                    bodySize)
-  {
-    _OrthancPluginRestApiPostPut params;
-    params.target = target;
-    params.uri = uri;
-    params.body = body;
-    params.bodySize = bodySize;
-    return context->InvokeService(context, _OrthancPluginService_RestApiPostAfterPlugins, &params);
-  }
-
-
-
-  /**
-   * @brief Make a DELETE call to the built-in Orthanc REST API.
-   * 
-   * Make a DELETE call to the built-in Orthanc REST API.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param uri The URI to delete in the built-in Orthanc API.
-   * @return 0 if success, other value if error.
-   **/
-  ORTHANC_PLUGIN_INLINE int  OrthancPluginRestApiDelete(
-    OrthancPluginContext*       context,
-    const char*                 uri)
-  {
-    return context->InvokeService(context, _OrthancPluginService_RestApiDelete, uri);
-  }
-
-
-  /**
-   * @brief Make a DELETE call to the REST API, as tainted by the plugins.
-   * 
-   * Make a DELETE call to the Orthanc REST API, after all the plugins
-   * are applied. In other words, if some plugin overrides or adds the
-   * called URI to the built-in Orthanc REST API, this call will
-   * return the result provided by this plugin. 
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param uri The URI to delete in the built-in Orthanc API.
-   * @return 0 if success, other value if error.
-   **/
-  ORTHANC_PLUGIN_INLINE int  OrthancPluginRestApiDeleteAfterPlugins(
-    OrthancPluginContext*       context,
-    const char*                 uri)
-  {
-    return context->InvokeService(context, _OrthancPluginService_RestApiDeleteAfterPlugins, uri);
-  }
-
-
-
-  /**
-   * @brief Make a PUT call to the built-in Orthanc REST API.
-   * 
-   * Make a PUT call to the built-in Orthanc REST API. The result to
-   * the query is stored into a newly allocated memory buffer.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param target The target memory buffer.
-   * @param uri The URI in the built-in Orthanc API.
-   * @param body The body of the PUT request.
-   * @param bodySize The size of the body.
-   * @return 0 if success, other value if error.
-   **/
-  ORTHANC_PLUGIN_INLINE int  OrthancPluginRestApiPut(
-    OrthancPluginContext*       context,
-    OrthancPluginMemoryBuffer*  target,
-    const char*                 uri,
-    const char*                 body,
-    uint32_t                    bodySize)
-  {
-    _OrthancPluginRestApiPostPut params;
-    params.target = target;
-    params.uri = uri;
-    params.body = body;
-    params.bodySize = bodySize;
-    return context->InvokeService(context, _OrthancPluginService_RestApiPut, &params);
-  }
-
-
-
-  /**
-   * @brief Make a PUT call to the REST API, as tainted by the plugins.
-   * 
-   * Make a PUT call to the Orthanc REST API, after all the plugins
-   * are applied. In other words, if some plugin overrides or adds the
-   * called URI to the built-in Orthanc REST API, this call will
-   * return the result provided by this plugin. The result to the
-   * query is stored into a newly allocated memory buffer.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param target The target memory buffer.
-   * @param uri The URI in the built-in Orthanc API.
-   * @param body The body of the PUT request.
-   * @param bodySize The size of the body.
-   * @return 0 if success, other value if error.
-   **/
-  ORTHANC_PLUGIN_INLINE int  OrthancPluginRestApiPutAfterPlugins(
-    OrthancPluginContext*       context,
-    OrthancPluginMemoryBuffer*  target,
-    const char*                 uri,
-    const char*                 body,
-    uint32_t                    bodySize)
-  {
-    _OrthancPluginRestApiPostPut params;
-    params.target = target;
-    params.uri = uri;
-    params.body = body;
-    params.bodySize = bodySize;
-    return context->InvokeService(context, _OrthancPluginService_RestApiPutAfterPlugins, &params);
-  }
-
-
-
-  typedef struct
-  {
-    OrthancPluginRestOutput* output;
-    const char*              argument;
-  } _OrthancPluginOutputPlusArgument;
-
-  /**
-   * @brief Redirect a REST request.
-   *
-   * This function answers to a REST request by redirecting the user
-   * to another URI using HTTP status 301.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param output The HTTP connection to the client application.
-   * @param redirection Where to redirect.
-   **/
-  ORTHANC_PLUGIN_INLINE void OrthancPluginRedirect(
-    OrthancPluginContext*    context,
-    OrthancPluginRestOutput* output,
-    const char*              redirection)
-  {
-    _OrthancPluginOutputPlusArgument params;
-    params.output = output;
-    params.argument = redirection;
-    context->InvokeService(context, _OrthancPluginService_Redirect, &params);
-  }
-
-
-
-  typedef struct
-  {
-    char**       result;
-    const char*  argument;
-  } _OrthancPluginRetrieveDynamicString;
-
-  /**
-   * @brief Look for a patient.
-   *
-   * Look for a patient stored in Orthanc, using its Patient ID tag (0x0010, 0x0020).
-   * This function uses the database index to run as fast as possible (it does not loop
-   * over all the stored patients).
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param patientID The Patient ID of interest.
-   * @return The NULL value if the patient is non-existent, or a string containing the 
-   * Orthanc ID of the patient. This string must be freed by OrthancPluginFreeString().
-   **/
-  ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupPatient(
-    OrthancPluginContext*  context,
-    const char*            patientID)
-  {
-    char* result;
-
-    _OrthancPluginRetrieveDynamicString params;
-    params.result = &result;
-    params.argument = patientID;
-
-    if (context->InvokeService(context, _OrthancPluginService_LookupPatient, &params))
-    {
-      /* Error */
-      return NULL;
-    }
-    else
-    {
-      return result;
-    }
-  }
-
-
-  /**
-   * @brief Look for a study.
-   *
-   * Look for a study stored in Orthanc, using its Study Instance UID tag (0x0020, 0x000d).
-   * This function uses the database index to run as fast as possible (it does not loop
-   * over all the stored studies).
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param studyUID The Study Instance UID of interest.
-   * @return The NULL value if the study is non-existent, or a string containing the 
-   * Orthanc ID of the study. This string must be freed by OrthancPluginFreeString().
-   **/
-  ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupStudy(
-    OrthancPluginContext*  context,
-    const char*            studyUID)
-  {
-    char* result;
-
-    _OrthancPluginRetrieveDynamicString params;
-    params.result = &result;
-    params.argument = studyUID;
-
-    if (context->InvokeService(context, _OrthancPluginService_LookupStudy, &params))
-    {
-      /* Error */
-      return NULL;
-    }
-    else
-    {
-      return result;
-    }
-  }
-
-
-  /**
-   * @brief Look for a study, using the accession number.
-   *
-   * Look for a study stored in Orthanc, using its Accession Number tag (0x0008, 0x0050).
-   * This function uses the database index to run as fast as possible (it does not loop
-   * over all the stored studies).
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param accessionNumber The Accession Number of interest.
-   * @return The NULL value if the study is non-existent, or a string containing the 
-   * Orthanc ID of the study. This string must be freed by OrthancPluginFreeString().
-   **/
-  ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupStudyWithAccessionNumber(
-    OrthancPluginContext*  context,
-    const char*            accessionNumber)
-  {
-    char* result;
-
-    _OrthancPluginRetrieveDynamicString params;
-    params.result = &result;
-    params.argument = accessionNumber;
-
-    if (context->InvokeService(context, _OrthancPluginService_LookupStudyWithAccessionNumber, &params))
-    {
-      /* Error */
-      return NULL;
-    }
-    else
-    {
-      return result;
-    }
-  }
-
-
-  /**
-   * @brief Look for a series.
-   *
-   * Look for a series stored in Orthanc, using its Series Instance UID tag (0x0020, 0x000e).
-   * This function uses the database index to run as fast as possible (it does not loop
-   * over all the stored series).
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param seriesUID The Series Instance UID of interest.
-   * @return The NULL value if the series is non-existent, or a string containing the 
-   * Orthanc ID of the series. This string must be freed by OrthancPluginFreeString().
-   **/
-  ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupSeries(
-    OrthancPluginContext*  context,
-    const char*            seriesUID)
-  {
-    char* result;
-
-    _OrthancPluginRetrieveDynamicString params;
-    params.result = &result;
-    params.argument = seriesUID;
-
-    if (context->InvokeService(context, _OrthancPluginService_LookupSeries, &params))
-    {
-      /* Error */
-      return NULL;
-    }
-    else
-    {
-      return result;
-    }
-  }
-
-
-  /**
-   * @brief Look for an instance.
-   *
-   * Look for an instance stored in Orthanc, using its SOP Instance UID tag (0x0008, 0x0018).
-   * This function uses the database index to run as fast as possible (it does not loop
-   * over all the stored instances).
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param sopInstanceUID The SOP Instance UID of interest.
-   * @return The NULL value if the instance is non-existent, or a string containing the 
-   * Orthanc ID of the instance. This string must be freed by OrthancPluginFreeString().
-   **/
-  ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupInstance(
-    OrthancPluginContext*  context,
-    const char*            sopInstanceUID)
-  {
-    char* result;
-
-    _OrthancPluginRetrieveDynamicString params;
-    params.result = &result;
-    params.argument = sopInstanceUID;
-
-    if (context->InvokeService(context, _OrthancPluginService_LookupInstance, &params))
-    {
-      /* Error */
-      return NULL;
-    }
-    else
-    {
-      return result;
-    }
-  }
-
-
-
-  typedef struct
-  {
-    OrthancPluginRestOutput* output;
-    uint16_t                 status;
-  } _OrthancPluginSendHttpStatusCode;
-
-  /**
-   * @brief Send a HTTP status code.
-   *
-   * This function answers to a REST request by sending a HTTP status
-   * code (such as "400 - Bad Request"). Note that:
-   * - Successful requests (status 200) must use ::OrthancPluginAnswerBuffer().
-   * - Redirections (status 301) must use ::OrthancPluginRedirect().
-   * - Unauthorized access (status 401) must use ::OrthancPluginSendUnauthorized().
-   * - Methods not allowed (status 405) must use ::OrthancPluginSendMethodNotAllowed().
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param output The HTTP connection to the client application.
-   * @param status The HTTP status code to be sent.
-   **/
-  ORTHANC_PLUGIN_INLINE void OrthancPluginSendHttpStatusCode(
-    OrthancPluginContext*    context,
-    OrthancPluginRestOutput* output,
-    uint16_t                 status)
-  {
-    _OrthancPluginSendHttpStatusCode params;
-    params.output = output;
-    params.status = status;
-    context->InvokeService(context, _OrthancPluginService_SendHttpStatusCode, &params);
-  }
-
-
-  /**
-   * @brief Signal that a REST request is not authorized.
-   *
-   * This function answers to a REST request by signaling that it is
-   * not authorized.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param output The HTTP connection to the client application.
-   * @param realm The realm for the authorization process.
-   **/
-  ORTHANC_PLUGIN_INLINE void OrthancPluginSendUnauthorized(
-    OrthancPluginContext*    context,
-    OrthancPluginRestOutput* output,
-    const char*              realm)
-  {
-    _OrthancPluginOutputPlusArgument params;
-    params.output = output;
-    params.argument = realm;
-    context->InvokeService(context, _OrthancPluginService_SendUnauthorized, &params);
-  }
-
-
-  /**
-   * @brief Signal that this URI does not support this HTTP method.
-   *
-   * This function answers to a REST request by signaling that the
-   * queried URI does not support this method.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param output The HTTP connection to the client application.
-   * @param allowedMethods The allowed methods for this URI (e.g. "GET,POST" after a PUT or a POST request).
-   **/
-  ORTHANC_PLUGIN_INLINE void OrthancPluginSendMethodNotAllowed(
-    OrthancPluginContext*    context,
-    OrthancPluginRestOutput* output,
-    const char*              allowedMethods)
-  {
-    _OrthancPluginOutputPlusArgument params;
-    params.output = output;
-    params.argument = allowedMethods;
-    context->InvokeService(context, _OrthancPluginService_SendMethodNotAllowed, &params);
-  }
-
-
-  typedef struct
-  {
-    OrthancPluginRestOutput* output;
-    const char*              key;
-    const char*              value;
-  } _OrthancPluginSetHttpHeader;
-
-  /**
-   * @brief Set a cookie.
-   *
-   * This function sets a cookie in the HTTP client.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param output The HTTP connection to the client application.
-   * @param cookie The cookie to be set.
-   * @param value The value of the cookie.
-   **/
-  ORTHANC_PLUGIN_INLINE void OrthancPluginSetCookie(
-    OrthancPluginContext*    context,
-    OrthancPluginRestOutput* output,
-    const char*              cookie,
-    const char*              value)
-  {
-    _OrthancPluginSetHttpHeader params;
-    params.output = output;
-    params.key = cookie;
-    params.value = value;
-    context->InvokeService(context, _OrthancPluginService_SetCookie, &params);
-  }
-
-
-  /**
-   * @brief Set some HTTP header.
-   *
-   * This function sets a HTTP header in the HTTP answer.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param output The HTTP connection to the client application.
-   * @param key The HTTP header to be set.
-   * @param value The value of the HTTP header.
-   **/
-  ORTHANC_PLUGIN_INLINE void OrthancPluginSetHttpHeader(
-    OrthancPluginContext*    context,
-    OrthancPluginRestOutput* output,
-    const char*              key,
-    const char*              value)
-  {
-    _OrthancPluginSetHttpHeader params;
-    params.output = output;
-    params.key = key;
-    params.value = value;
-    context->InvokeService(context, _OrthancPluginService_SetHttpHeader, &params);
-  }
-
-
-  typedef struct
-  {
-    char**                      resultStringToFree;
-    const char**                resultString;
-    int64_t*                    resultInt64;
-    const char*                 key;
-    OrthancPluginDicomInstance* instance;
-  } _OrthancPluginAccessDicomInstance;
-
-
-  /**
-   * @brief Get the AET of a DICOM instance.
-   *
-   * This function returns the Application Entity Title (AET) of the
-   * DICOM modality from which a DICOM instance originates.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param instance The instance of interest.
-   * @return The AET if success, NULL if error.
-   **/
-  ORTHANC_PLUGIN_INLINE const char* OrthancPluginGetInstanceRemoteAet(
-    OrthancPluginContext*        context,
-    OrthancPluginDicomInstance*  instance)
-  {
-    const char* result;
-
-    _OrthancPluginAccessDicomInstance params;
-    memset(&params, 0, sizeof(params));
-    params.resultString = &result;
-    params.instance = instance;
-
-    if (context->InvokeService(context, _OrthancPluginService_GetInstanceRemoteAet, &params))
-    {
-      /* Error */
-      return NULL;
-    }
-    else
-    {
-      return result;
-    }
-  }
-
-
-  /**
-   * @brief Get the size of a DICOM file.
-   *
-   * This function returns the number of bytes of the given DICOM instance.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param instance The instance of interest.
-   * @return The size of the file, -1 in case of error.
-   **/
-  ORTHANC_PLUGIN_INLINE int64_t OrthancPluginGetInstanceSize(
-    OrthancPluginContext*       context,
-    OrthancPluginDicomInstance* instance)
-  {
-    int64_t size;
-
-    _OrthancPluginAccessDicomInstance params;
-    memset(&params, 0, sizeof(params));
-    params.resultInt64 = &size;
-    params.instance = instance;
-
-    if (context->InvokeService(context, _OrthancPluginService_GetInstanceSize, &params))
-    {
-      /* Error */
-      return -1;
-    }
-    else
-    {
-      return size;
-    }
-  }
-
-
-  /**
-   * @brief Get the data of a DICOM file.
-   *
-   * This function returns a pointer to the content of the given DICOM instance.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param instance The instance of interest.
-   * @return The pointer to the DICOM data, NULL in case of error.
-   **/
-  ORTHANC_PLUGIN_INLINE const char* OrthancPluginGetInstanceData(
-    OrthancPluginContext*        context,
-    OrthancPluginDicomInstance*  instance)
-  {
-    const char* result;
-
-    _OrthancPluginAccessDicomInstance params;
-    memset(&params, 0, sizeof(params));
-    params.resultString = &result;
-    params.instance = instance;
-
-    if (context->InvokeService(context, _OrthancPluginService_GetInstanceData, &params))
-    {
-      /* Error */
-      return NULL;
-    }
-    else
-    {
-      return result;
-    }
-  }
-
-
-  /**
-   * @brief Get the DICOM tag hierarchy as a JSON file.
-   *
-   * This function returns a pointer to a newly created string
-   * containing a JSON file. This JSON file encodes the tag hierarchy
-   * of the given DICOM instance.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param instance The instance of interest.
-   * @return The NULL value in case of error, or a string containing the JSON file.
-   * This string must be freed by OrthancPluginFreeString().
-   **/
-  ORTHANC_PLUGIN_INLINE char* OrthancPluginGetInstanceJson(
-    OrthancPluginContext*        context,
-    OrthancPluginDicomInstance*  instance)
-  {
-    char* result;
-
-    _OrthancPluginAccessDicomInstance params;
-    memset(&params, 0, sizeof(params));
-    params.resultStringToFree = &result;
-    params.instance = instance;
-
-    if (context->InvokeService(context, _OrthancPluginService_GetInstanceJson, &params))
-    {
-      /* Error */
-      return NULL;
-    }
-    else
-    {
-      return result;
-    }
-  }
-
-
-  /**
-   * @brief Get the DICOM tag hierarchy as a JSON file (with simplification).
-   *
-   * This function returns a pointer to a newly created string
-   * containing a JSON file. This JSON file encodes the tag hierarchy
-   * of the given DICOM instance. In contrast with
-   * ::OrthancPluginGetInstanceJson(), the returned JSON file is in
-   * its simplified version.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param instance The instance of interest.
-   * @return The NULL value in case of error, or a string containing the JSON file.
-   * This string must be freed by OrthancPluginFreeString().
-   **/
-  ORTHANC_PLUGIN_INLINE char* OrthancPluginGetInstanceSimplifiedJson(
-    OrthancPluginContext*        context,
-    OrthancPluginDicomInstance*  instance)
-  {
-    char* result;
-
-    _OrthancPluginAccessDicomInstance params;
-    memset(&params, 0, sizeof(params));
-    params.resultStringToFree = &result;
-    params.instance = instance;
-
-    if (context->InvokeService(context, _OrthancPluginService_GetInstanceSimplifiedJson, &params))
-    {
-      /* Error */
-      return NULL;
-    }
-    else
-    {
-      return result;
-    }
-  }
-
-
-  /**
-   * @brief Check whether a DICOM instance is associated with some metadata.
-   *
-   * This function checks whether the DICOM instance of interest is
-   * associated with some metadata. As of Orthanc 0.8.1, in the
-   * callbacks registered by
-   * ::OrthancPluginRegisterOnStoredInstanceCallback(), the only
-   * possibly available metadata are "ReceptionDate", "RemoteAET" and
-   * "IndexInSeries".
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param instance The instance of interest.
-   * @param metadata The metadata of interest.
-   * @return 1 if the metadata is present, 0 if it is absent, -1 in case of error.
-   **/
-  ORTHANC_PLUGIN_INLINE int  OrthancPluginHasInstanceMetadata(
-    OrthancPluginContext*        context,
-    OrthancPluginDicomInstance*  instance,
-    const char*                  metadata)
-  {
-    int64_t result;
-
-    _OrthancPluginAccessDicomInstance params;
-    memset(&params, 0, sizeof(params));
-    params.resultInt64 = &result;
-    params.instance = instance;
-    params.key = metadata;
-
-    if (context->InvokeService(context, _OrthancPluginService_HasInstanceMetadata, &params))
-    {
-      /* Error */
-      return -1;
-    }
-    else
-    {
-      return (result != 0);
-    }
-  }
-
-
-  /**
-   * @brief Get the value of some metadata associated with a given DICOM instance.
-   *
-   * This functions returns the value of some metadata that is associated with the DICOM instance of interest.
-   * Before calling this function, the existence of the metadata must have been checked with
-   * ::OrthancPluginHasInstanceMetadata().
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param instance The instance of interest.
-   * @param metadata The metadata of interest.
-   * @return The metadata value if success, NULL if error.
-   **/
-  ORTHANC_PLUGIN_INLINE const char* OrthancPluginGetInstanceMetadata(
-    OrthancPluginContext*        context,
-    OrthancPluginDicomInstance*  instance,
-    const char*                  metadata)
-  {
-    const char* result;
-
-    _OrthancPluginAccessDicomInstance params;
-    memset(&params, 0, sizeof(params));
-    params.resultString = &result;
-    params.instance = instance;
-    params.key = metadata;
-
-    if (context->InvokeService(context, _OrthancPluginService_GetInstanceMetadata, &params))
-    {
-      /* Error */
-      return NULL;
-    }
-    else
-    {
-      return result;
-    }
-  }
-
-
-
-  typedef struct
-  {
-    OrthancPluginStorageCreate  create;
-    OrthancPluginStorageRead    read;
-    OrthancPluginStorageRemove  remove;
-    OrthancPluginFree           free;
-  } _OrthancPluginRegisterStorageArea;
-
-  /**
-   * @brief Register a custom storage area.
-   *
-   * This function registers a custom storage area, to replace the
-   * built-in way Orthanc stores its files on the filesystem. This
-   * function must be called during the initialization of the plugin,
-   * i.e. inside the OrthancPluginInitialize() public function.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param create The callback function to store a file on the custom storage area.
-   * @param read The callback function to read a file from the custom storage area.
-   * @param remove The callback function to remove a file from the custom storage area.
-   **/
-  ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterStorageArea(
-    OrthancPluginContext*       context,
-    OrthancPluginStorageCreate  create,
-    OrthancPluginStorageRead    read,
-    OrthancPluginStorageRemove  remove)
-  {
-    _OrthancPluginRegisterStorageArea params;
-    params.create = create;
-    params.read = read;
-    params.remove = remove;
-
-#ifdef  __cplusplus
-    params.free = ::free;
-#else
-    params.free = free;
-#endif
-
-    context->InvokeService(context, _OrthancPluginService_RegisterStorageArea, &params);
-  }
-
-
-
-  /**
-   * @brief Return the path to the Orthanc executable.
-   *
-   * This function returns the path to the Orthanc executable.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @return NULL in the case of an error, or a newly allocated string
-   * containing the path. This string must be freed by
-   * OrthancPluginFreeString().
-   **/
-  ORTHANC_PLUGIN_INLINE char *OrthancPluginGetOrthancPath(OrthancPluginContext* context)
-  {
-    char* result;
-
-    _OrthancPluginRetrieveDynamicString params;
-    params.result = &result;
-    params.argument = NULL;
-
-    if (context->InvokeService(context, _OrthancPluginService_GetOrthancPath, &params))
-    {
-      /* Error */
-      return NULL;
-    }
-    else
-    {
-      return result;
-    }
-  }
-
-
-  /**
-   * @brief Return the directory containing the Orthanc.
-   *
-   * This function returns the path to the directory containing the Orthanc executable.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @return NULL in the case of an error, or a newly allocated string
-   * containing the path. This string must be freed by
-   * OrthancPluginFreeString().
-   **/
-  ORTHANC_PLUGIN_INLINE char *OrthancPluginGetOrthancDirectory(OrthancPluginContext* context)
-  {
-    char* result;
-
-    _OrthancPluginRetrieveDynamicString params;
-    params.result = &result;
-    params.argument = NULL;
-
-    if (context->InvokeService(context, _OrthancPluginService_GetOrthancDirectory, &params))
-    {
-      /* Error */
-      return NULL;
-    }
-    else
-    {
-      return result;
-    }
-  }
-
-
-  /**
-   * @brief Return the path to the configuration file.
-   *
-   * This function returns the path to the configuration file that was
-   * specified when starting Orthanc.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @return NULL in the case of an error, or a newly allocated string
-   * containing the path. This string must be freed by
-   * OrthancPluginFreeString().
-   **/
-  ORTHANC_PLUGIN_INLINE char *OrthancPluginGetConfigurationPath(OrthancPluginContext* context)
-  {
-    char* result;
-
-    _OrthancPluginRetrieveDynamicString params;
-    params.result = &result;
-    params.argument = NULL;
-
-    if (context->InvokeService(context, _OrthancPluginService_GetConfigurationPath, &params))
-    {
-      /* Error */
-      return NULL;
-    }
-    else
-    {
-      return result;
-    }
-  }
-
-
-
-  typedef struct
-  {
-    OrthancPluginOnChangeCallback callback;
-  } _OrthancPluginOnChangeCallback;
-
-  /**
-   * @brief Register a callback to monitor changes.
-   *
-   * This function registers a callback function that is called
-   * whenever a change happens to some DICOM resource.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param callback The callback function.
-   **/
-  ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterOnChangeCallback(
-    OrthancPluginContext*          context,
-    OrthancPluginOnChangeCallback  callback)
-  {
-    _OrthancPluginOnChangeCallback params;
-    params.callback = callback;
-
-    context->InvokeService(context, _OrthancPluginService_RegisterOnChangeCallback, &params);
-  }
-
-
-
-  typedef struct
-  {
-    const char* plugin;
-    _OrthancPluginProperty property;
-    const char* value;
-  } _OrthancPluginSetPluginProperty;
-
-
-  /**
-   * @brief Set the URI where the plugin provides its Web interface.
-   *
-   * For plugins that come with a Web interface, this function
-   * declares the entry path where to find this interface. This
-   * information is notably used in the "Plugins" page of Orthanc
-   * Explorer.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param uri The root URI for this plugin.
-   **/ 
-  ORTHANC_PLUGIN_INLINE void OrthancPluginSetRootUri(
-    OrthancPluginContext*  context,
-    const char*            uri)
-  {
-    _OrthancPluginSetPluginProperty params;
-    params.plugin = OrthancPluginGetName();
-    params.property = _OrthancPluginProperty_RootUri;
-    params.value = uri;
-
-    context->InvokeService(context, _OrthancPluginService_SetPluginProperty, &params);
-  }
-
-
-  /**
-   * @brief Set a description for this plugin.
-   *
-   * Set a description for this plugin. It is displayed in the
-   * "Plugins" page of Orthanc Explorer.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param description The description.
-   **/ 
-  ORTHANC_PLUGIN_INLINE void OrthancPluginSetDescription(
-    OrthancPluginContext*  context,
-    const char*            description)
-  {
-    _OrthancPluginSetPluginProperty params;
-    params.plugin = OrthancPluginGetName();
-    params.property = _OrthancPluginProperty_Description;
-    params.value = description;
-
-    context->InvokeService(context, _OrthancPluginService_SetPluginProperty, &params);
-  }
-
-
-  /**
-   * @brief Extend the JavaScript code of Orthanc Explorer.
-   *
-   * Add JavaScript code to customize the default behavior of Orthanc
-   * Explorer. This can for instance be used to add new buttons.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param javascript The custom JavaScript code.
-   **/ 
-  ORTHANC_PLUGIN_INLINE void OrthancPluginExtendOrthancExplorer(
-    OrthancPluginContext*  context,
-    const char*            javascript)
-  {
-    _OrthancPluginSetPluginProperty params;
-    params.plugin = OrthancPluginGetName();
-    params.property = _OrthancPluginProperty_OrthancExplorer;
-    params.value = javascript;
-
-    context->InvokeService(context, _OrthancPluginService_SetPluginProperty, &params);
-  }
-
-
-  typedef struct
-  {
-    char**       result;
-    int32_t      property;
-    const char*  value;
-  } _OrthancPluginGlobalProperty;
-
-
-  /**
-   * @brief Get the value of a global property.
-   *
-   * Get the value of a global property that is stored in the Orthanc database. Global
-   * properties whose index is below 1024 are reserved by Orthanc.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param property The global property of interest.
-   * @param defaultValue The value to return, if the global property is unset.
-   * @return The value of the global property, or NULL in the case of an error. This
-   * string must be freed by OrthancPluginFreeString().
-   **/
-  ORTHANC_PLUGIN_INLINE char* OrthancPluginGetGlobalProperty(
-    OrthancPluginContext*  context,
-    int32_t                property,
-    const char*            defaultValue)
-  {
-    char* result;
-
-    _OrthancPluginGlobalProperty params;
-    params.result = &result;
-    params.property = property;
-    params.value = defaultValue;
-
-    if (context->InvokeService(context, _OrthancPluginService_GetGlobalProperty, &params))
-    {
-      /* Error */
-      return NULL;
-    }
-    else
-    {
-      return result;
-    }
-  }
-
-
-  /**
-   * @brief Set the value of a global property.
-   *
-   * Set the value of a global property into the Orthanc
-   * database. Setting a global property can be used by plugins to
-   * save their internal parameters. Plugins are only allowed to set
-   * properties whose index are above or equal to 1024 (properties
-   * below 1024 are read-only and reserved by Orthanc).
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param property The global property of interest.
-   * @param value The value to be set in the global property.
-   * @return 0 if success, -1 in case of error.
-   **/
-  ORTHANC_PLUGIN_INLINE int32_t OrthancPluginSetGlobalProperty(
-    OrthancPluginContext*  context,
-    int32_t                property,
-    const char*            value)
-  {
-    _OrthancPluginGlobalProperty params;
-    params.result = NULL;
-    params.property = property;
-    params.value = value;
-
-    if (context->InvokeService(context, _OrthancPluginService_SetGlobalProperty, &params))
-    {
-      /* Error */
-      return -1;
-    }
-    else
-    {
-      return 0;
-    }
-  }
-
-
-
-  typedef struct
-  {
-    int32_t   *resultInt32;
-    uint32_t  *resultUint32;
-    int64_t   *resultInt64;
-    uint64_t  *resultUint64;
-  } _OrthancPluginReturnSingleValue;
-
-  /**
-   * @brief Get the number of command-line arguments.
-   *
-   * Retrieve the number of command-line arguments that were used to launch Orthanc.
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @return The number of arguments.
-   **/
-  ORTHANC_PLUGIN_INLINE uint32_t OrthancPluginGetCommandLineArgumentsCount(
-    OrthancPluginContext*  context)
-  {
-    uint32_t count = 0;
-
-    _OrthancPluginReturnSingleValue params;
-    memset(&params, 0, sizeof(params));
-    params.resultUint32 = &count;
-
-    if (context->InvokeService(context, _OrthancPluginService_GetCommandLineArgumentsCount, &params))
-    {
-      /* Error */
-      return 0;
-    }
-    else
-    {
-      return count;
-    }
-  }
-
-
-
-  /**
-   * @brief Get the value of a command-line argument.
-   *
-   * Get the value of one of the command-line arguments that were used
-   * to launch Orthanc. The number of available arguments can be
-   * retrieved by OrthancPluginGetCommandLineArgumentsCount().
-   * 
-   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param argument The index of the argument.
-   * @return The value of the argument, or NULL in the case of an error. This
-   * string must be freed by OrthancPluginFreeString().
-   **/
-  ORTHANC_PLUGIN_INLINE char* OrthancPluginGetCommandLineArgument(
-    OrthancPluginContext*  context,
-    uint32_t               argument)
-  {
-    char* result;
-
-    _OrthancPluginGlobalProperty params;
-    params.result = &result;
-    params.property = (int32_t) argument;
-    params.value = NULL;
-
-    if (context->InvokeService(context, _OrthancPluginService_GetCommandLineArgument, &params))
-    {
-      /* Error */
-      return NULL;
-    }
-    else
-    {
-      return result;
-    }
-  }
-
-
-#ifdef  __cplusplus
-}
-#endif
-
-
-/** @} */
-
--- a/Plugins/Include/OrthancCppDatabasePlugin.h	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1532 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-
-#pragma once
-
-#include "OrthancCDatabasePlugin.h"
-
-#include <stdexcept>
-#include <list>
-#include <string>
-
-namespace OrthancPlugins
-{
-  // This class mimics "boost::noncopyable"
-  class NonCopyable
-  {
-  private:
-    NonCopyable(const NonCopyable&);
-
-    NonCopyable& operator= (const NonCopyable&);
-
-  protected:
-    NonCopyable()
-    {
-    }
-
-    ~NonCopyable()
-    {
-    }
-  };
-
-
-
-  class DatabaseBackendOutput : public NonCopyable
-  {
-    friend class DatabaseBackendAdapter;
-
-  private:
-    enum AllowedAnswers
-    {
-      AllowedAnswers_All,
-      AllowedAnswers_None,
-      AllowedAnswers_Attachment,
-      AllowedAnswers_Change,
-      AllowedAnswers_DicomTag,
-      AllowedAnswers_ExportedResource
-    };
-
-    OrthancPluginContext*         context_;
-    OrthancPluginDatabaseContext* database_;
-    AllowedAnswers                allowedAnswers_;
-
-    void SetAllowedAnswers(AllowedAnswers allowed)
-    {
-      allowedAnswers_ = allowed;
-    }
-
-  public:
-    DatabaseBackendOutput(OrthancPluginContext*         context,
-                          OrthancPluginDatabaseContext* database) :
-      context_(context),
-      database_(database),
-      allowedAnswers_(AllowedAnswers_All /* for unit tests */)
-    {
-    }
-
-    void LogError(const std::string& message)
-    {
-      OrthancPluginLogError(context_, message.c_str());
-    }
-
-    void LogWarning(const std::string& message)
-    {
-      OrthancPluginLogWarning(context_, message.c_str());
-    }
-
-    void LogInfo(const std::string& message)
-    {
-      OrthancPluginLogInfo(context_, message.c_str());
-    }
-
-    void SignalDeletedAttachment(const std::string& uuid,
-                                 int32_t            contentType,
-                                 uint64_t           uncompressedSize,
-                                 const std::string& uncompressedHash,
-                                 int32_t            compressionType,
-                                 uint64_t           compressedSize,
-                                 const std::string& compressedHash)
-    {
-      OrthancPluginAttachment attachment;
-      attachment.uuid = uuid.c_str();
-      attachment.contentType = contentType;
-      attachment.uncompressedSize = uncompressedSize;
-      attachment.uncompressedHash = uncompressedHash.c_str();
-      attachment.compressionType = compressionType;
-      attachment.compressedSize = compressedSize;
-      attachment.compressedHash = compressedHash.c_str();
-
-      OrthancPluginDatabaseSignalDeletedAttachment(context_, database_, &attachment);
-    }
-
-    void SignalDeletedResource(const std::string& publicId,
-                               OrthancPluginResourceType resourceType)
-    {
-      OrthancPluginDatabaseSignalDeletedResource(context_, database_, publicId.c_str(), resourceType);
-    }
-
-    void SignalRemainingAncestor(const std::string& ancestorId,
-                                 OrthancPluginResourceType ancestorType)
-    {
-      OrthancPluginDatabaseSignalRemainingAncestor(context_, database_, ancestorId.c_str(), ancestorType);
-    }
-
-    void AnswerAttachment(const std::string& uuid,
-                          int32_t            contentType,
-                          uint64_t           uncompressedSize,
-                          const std::string& uncompressedHash,
-                          int32_t            compressionType,
-                          uint64_t           compressedSize,
-                          const std::string& compressedHash)
-    {
-      if (allowedAnswers_ != AllowedAnswers_All &&
-          allowedAnswers_ != AllowedAnswers_Attachment)
-      {
-        throw std::runtime_error("Cannot answer with an attachment in the current state");
-      }
-
-      OrthancPluginAttachment attachment;
-      attachment.uuid = uuid.c_str();
-      attachment.contentType = contentType;
-      attachment.uncompressedSize = uncompressedSize;
-      attachment.uncompressedHash = uncompressedHash.c_str();
-      attachment.compressionType = compressionType;
-      attachment.compressedSize = compressedSize;
-      attachment.compressedHash = compressedHash.c_str();
-
-      OrthancPluginDatabaseAnswerAttachment(context_, database_, &attachment);
-    }
-
-    void AnswerChange(int64_t                    seq,
-                      int32_t                    changeType,
-                      OrthancPluginResourceType  resourceType,
-                      const std::string&         publicId,
-                      const std::string&         date)
-    {
-      if (allowedAnswers_ != AllowedAnswers_All &&
-          allowedAnswers_ != AllowedAnswers_Change)
-      {
-        throw std::runtime_error("Cannot answer with a change in the current state");
-      }
-
-      OrthancPluginChange change;
-      change.seq = seq;
-      change.changeType = changeType;
-      change.resourceType = resourceType;
-      change.publicId = publicId.c_str();
-      change.date = date.c_str();
-
-      OrthancPluginDatabaseAnswerChange(context_, database_, &change);
-    }
-
-    void AnswerDicomTag(uint16_t group,
-                        uint16_t element,
-                        const std::string& value)
-    {
-      if (allowedAnswers_ != AllowedAnswers_All &&
-          allowedAnswers_ != AllowedAnswers_DicomTag)
-      {
-        throw std::runtime_error("Cannot answer with a DICOM tag in the current state");
-      }
-
-      OrthancPluginDicomTag tag;
-      tag.group = group;
-      tag.element = element;
-      tag.value = value.c_str();
-
-      OrthancPluginDatabaseAnswerDicomTag(context_, database_, &tag);
-    }
-
-    void AnswerExportedResource(int64_t                    seq,
-                                OrthancPluginResourceType  resourceType,
-                                const std::string&         publicId,
-                                const std::string&         modality,
-                                const std::string&         date,
-                                const std::string&         patientId,
-                                const std::string&         studyInstanceUid,
-                                const std::string&         seriesInstanceUid,
-                                const std::string&         sopInstanceUid)
-    {
-      if (allowedAnswers_ != AllowedAnswers_All &&
-          allowedAnswers_ != AllowedAnswers_ExportedResource)
-      {
-        throw std::runtime_error("Cannot answer with an exported resource in the current state");
-      }
-
-      OrthancPluginExportedResource exported;
-      exported.seq = seq;
-      exported.resourceType = resourceType;
-      exported.publicId = publicId.c_str();
-      exported.modality = modality.c_str();
-      exported.date = date.c_str();
-      exported.patientId = patientId.c_str();
-      exported.studyInstanceUid = studyInstanceUid.c_str();
-      exported.seriesInstanceUid = seriesInstanceUid.c_str();
-      exported.sopInstanceUid = sopInstanceUid.c_str();
-
-      OrthancPluginDatabaseAnswerExportedResource(context_, database_, &exported);
-    }
-  };
-
-
-
-  class IDatabaseBackend : public NonCopyable
-  {
-    friend class DatabaseBackendAdapter;
-
-  private:
-    DatabaseBackendOutput*  output_;
-
-    void Finalize()
-    {
-      if (output_ != NULL)
-      {
-        delete output_;
-        output_ = NULL;
-      }
-    }
-
-  protected:
-    DatabaseBackendOutput& GetOutput()
-    {
-      return *output_;
-    }
-
-  public:
-    IDatabaseBackend() : output_(NULL)
-    {
-    }
-
-    virtual ~IDatabaseBackend()
-    {
-      Finalize();
-    }
-
-    // This takes the ownership
-    void RegisterOutput(DatabaseBackendOutput* output)
-    {
-      Finalize();
-      output_ = output;
-    }
-
-    virtual void Open() = 0;
-
-    virtual void Close() = 0;
-
-    virtual void AddAttachment(int64_t id,
-                               const OrthancPluginAttachment& attachment) = 0;
-
-    virtual void AttachChild(int64_t parent,
-                             int64_t child) = 0;
-
-    virtual void ClearChanges() = 0;
-
-    virtual void ClearExportedResources() = 0;
-
-    virtual int64_t CreateResource(const char* publicId,
-                                   OrthancPluginResourceType type) = 0;
-
-    virtual void DeleteAttachment(int64_t id,
-                                  int32_t attachment) = 0;
-
-    virtual void DeleteMetadata(int64_t id,
-                                int32_t metadataType) = 0;
-
-    virtual void DeleteResource(int64_t id) = 0;
-
-    virtual void GetAllPublicIds(std::list<std::string>& target,
-                                 OrthancPluginResourceType resourceType) = 0;
-
-    /* Use GetOutput().AnswerChange() */
-    virtual void GetChanges(bool& done /*out*/,
-                            int64_t since,
-                            uint32_t maxResults) = 0;
-
-    virtual void GetChildrenInternalId(std::list<int64_t>& target /*out*/,
-                                       int64_t id) = 0;
-
-    virtual void GetChildrenPublicId(std::list<std::string>& target /*out*/,
-                                     int64_t id) = 0;
-
-    /* Use GetOutput().AnswerExportedResource() */
-    virtual void GetExportedResources(bool& done /*out*/,
-                                      int64_t since,
-                                      uint32_t maxResults) = 0;
-
-    /* Use GetOutput().AnswerChange() */
-    virtual void GetLastChange() = 0;
-
-    /* Use GetOutput().AnswerExportedResource() */
-    virtual void GetLastExportedResource() = 0;
-
-    /* Use GetOutput().AnswerDicomTag() */
-    virtual void GetMainDicomTags(int64_t id) = 0;
-
-    virtual std::string GetPublicId(int64_t resourceId) = 0;
-
-    virtual uint64_t GetResourceCount(OrthancPluginResourceType resourceType) = 0;
-
-    virtual OrthancPluginResourceType GetResourceType(int64_t resourceId) = 0;
-
-    virtual uint64_t GetTotalCompressedSize() = 0;
-    
-    virtual uint64_t GetTotalUncompressedSize() = 0;
-
-    virtual bool IsExistingResource(int64_t internalId) = 0;
-
-    virtual bool IsProtectedPatient(int64_t internalId) = 0;
-
-    virtual void ListAvailableMetadata(std::list<int32_t>& target /*out*/,
-                                       int64_t id) = 0;
-
-    virtual void ListAvailableAttachments(std::list<int32_t>& target /*out*/,
-                                          int64_t id) = 0;
-
-    virtual void LogChange(const OrthancPluginChange& change) = 0;
-
-    virtual void LogExportedResource(const OrthancPluginExportedResource& resource) = 0;
-    
-    /* Use GetOutput().AnswerAttachment() */
-    virtual bool LookupAttachment(int64_t id,
-                                  int32_t contentType) = 0;
-
-    virtual bool LookupGlobalProperty(std::string& target /*out*/,
-                                      int32_t property) = 0;
-
-    /**
-     * "Identifiers" are necessarily one of the following tags:
-     * PatientID (0x0010, 0x0020), StudyInstanceUID (0x0020, 0x000d),
-     * SeriesInstanceUID (0x0020, 0x000e), SOPInstanceUID (0x0008,
-     * 0x0018) or AccessionNumber (0x0008, 0x0050).
-     **/
-    virtual void LookupIdentifier(std::list<int64_t>& target /*out*/,
-                                  uint16_t group,
-                                  uint16_t element,
-                                  const char* value) = 0;
-
-    virtual void LookupIdentifier(std::list<int64_t>& target /*out*/,
-                                  const char* value) = 0;
-
-    virtual bool LookupMetadata(std::string& target /*out*/,
-                                int64_t id,
-                                int32_t metadataType) = 0;
-
-    virtual bool LookupParent(int64_t& parentId /*out*/,
-                              int64_t resourceId) = 0;
-
-    virtual bool LookupResource(int64_t& id /*out*/,
-                                OrthancPluginResourceType& type /*out*/,
-                                const char* publicId) = 0;
-
-    virtual bool SelectPatientToRecycle(int64_t& internalId /*out*/) = 0;
-
-    virtual bool SelectPatientToRecycle(int64_t& internalId /*out*/,
-                                        int64_t patientIdToAvoid) = 0;
-
-    virtual void SetGlobalProperty(int32_t property,
-                                   const char* value) = 0;
-
-    virtual void SetMainDicomTag(int64_t id,
-                                 uint16_t group,
-                                 uint16_t element,
-                                 const char* value) = 0;
-
-    virtual void SetIdentifierTag(int64_t id,
-                                  uint16_t group,
-                                  uint16_t element,
-                                  const char* value) = 0;
-
-    virtual void SetMetadata(int64_t id,
-                             int32_t metadataType,
-                             const char* value) = 0;
-
-    virtual void SetProtectedPatient(int64_t internalId, 
-                                     bool isProtected) = 0;
-
-    virtual void StartTransaction() = 0;
-
-    virtual void RollbackTransaction() = 0;
-
-    virtual void CommitTransaction() = 0;
-  };
-
-
-
-  class DatabaseBackendAdapter
-  {
-  private:
-    // This class cannot be instantiated
-    DatabaseBackendAdapter()
-    {
-    }
-
-    static void LogError(IDatabaseBackend* backend,
-                         const std::runtime_error& e)
-    {
-      backend->GetOutput().LogError("Exception in database back-end: " + std::string(e.what()));
-    }
-
-
-    static int32_t  AddAttachment(void* payload,
-                                  int64_t id,
-                                  const OrthancPluginAttachment* attachment)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->AddAttachment(id, *attachment);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-                             
-    static int32_t  AttachChild(void* payload,
-                                int64_t parent,
-                                int64_t child)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->AttachChild(parent, child);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-          
-                   
-    static int32_t  ClearChanges(void* payload)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->ClearChanges();
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-                             
-
-    static int32_t  ClearExportedResources(void* payload)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->ClearExportedResources();
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  CreateResource(int64_t* id, 
-                                   void* payload,
-                                   const char* publicId,
-                                   OrthancPluginResourceType resourceType)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        *id = backend->CreateResource(publicId, resourceType);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-          
-         
-    static int32_t  DeleteAttachment(void* payload,
-                                     int64_t id,
-                                     int32_t contentType)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->DeleteAttachment(id, contentType);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-   
-
-    static int32_t  DeleteMetadata(void* payload,
-                                   int64_t id,
-                                   int32_t metadataType)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->DeleteMetadata(id, metadataType);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-   
-
-    static int32_t  DeleteResource(void* payload,
-                                   int64_t id)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->DeleteResource(id);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  GetAllPublicIds(OrthancPluginDatabaseContext* context,
-                                    void* payload,
-                                    OrthancPluginResourceType resourceType)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        std::list<std::string> ids;
-        backend->GetAllPublicIds(ids, resourceType);
-
-        for (std::list<std::string>::const_iterator
-               it = ids.begin(); it != ids.end(); ++it)
-        {
-          OrthancPluginDatabaseAnswerString(backend->GetOutput().context_,
-                                            backend->GetOutput().database_,
-                                            it->c_str());
-        }
-
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  GetChanges(OrthancPluginDatabaseContext* context,
-                               void* payload,
-                               int64_t since,
-                               uint32_t maxResult)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_Change);
-
-      try
-      {
-        bool done;
-        backend->GetChanges(done, since, maxResult);
-        
-        if (done)
-        {
-          OrthancPluginDatabaseAnswerChangesDone(backend->GetOutput().context_,
-                                                 backend->GetOutput().database_);
-        }
-
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  GetChildrenInternalId(OrthancPluginDatabaseContext* context,
-                                          void* payload,
-                                          int64_t id)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        std::list<int64_t> target;
-        backend->GetChildrenInternalId(target, id);
-
-        for (std::list<int64_t>::const_iterator
-               it = target.begin(); it != target.end(); ++it)
-        {
-          OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_,
-                                           backend->GetOutput().database_, *it);
-        }
-
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-          
-         
-    static int32_t  GetChildrenPublicId(OrthancPluginDatabaseContext* context,
-                                        void* payload,
-                                        int64_t id)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        std::list<std::string> ids;
-        backend->GetChildrenPublicId(ids, id);
-
-        for (std::list<std::string>::const_iterator
-               it = ids.begin(); it != ids.end(); ++it)
-        {
-          OrthancPluginDatabaseAnswerString(backend->GetOutput().context_,
-                                            backend->GetOutput().database_,
-                                            it->c_str());
-        }
-
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  GetExportedResources(OrthancPluginDatabaseContext* context,
-                                         void* payload,
-                                         int64_t  since,
-                                         uint32_t  maxResult)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_ExportedResource);
-
-      try
-      {
-        bool done;
-        backend->GetExportedResources(done, since, maxResult);
-
-        if (done)
-        {
-          OrthancPluginDatabaseAnswerExportedResourcesDone(backend->GetOutput().context_,
-                                                           backend->GetOutput().database_);
-        }
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-          
-         
-    static int32_t  GetLastChange(OrthancPluginDatabaseContext* context,
-                                  void* payload)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_Change);
-
-      try
-      {
-        backend->GetLastChange();
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  GetLastExportedResource(OrthancPluginDatabaseContext* context,
-                                            void* payload)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_ExportedResource);
-
-      try
-      {
-        backend->GetLastExportedResource();
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-    
-               
-    static int32_t  GetMainDicomTags(OrthancPluginDatabaseContext* context,
-                                     void* payload,
-                                     int64_t id)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_DicomTag);
-
-      try
-      {
-        backend->GetMainDicomTags(id);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-          
-         
-    static int32_t  GetPublicId(OrthancPluginDatabaseContext* context,
-                                void* payload,
-                                int64_t id)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        std::string s = backend->GetPublicId(id);
-        OrthancPluginDatabaseAnswerString(backend->GetOutput().context_,
-                                          backend->GetOutput().database_,
-                                          s.c_str());
-
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  GetResourceCount(uint64_t* target,
-                                     void* payload,
-                                     OrthancPluginResourceType  resourceType)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        *target = backend->GetResourceCount(resourceType);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-                   
-
-    static int32_t  GetResourceType(OrthancPluginResourceType* resourceType,
-                                    void* payload,
-                                    int64_t id)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        *resourceType = backend->GetResourceType(id);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  GetTotalCompressedSize(uint64_t* target,
-                                           void* payload)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        *target = backend->GetTotalCompressedSize();
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-          
-         
-    static int32_t  GetTotalUncompressedSize(uint64_t* target,
-                                             void* payload)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        *target = backend->GetTotalUncompressedSize();
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-                   
-
-    static int32_t  IsExistingResource(int32_t* existing,
-                                       void* payload,
-                                       int64_t id)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        *existing = backend->IsExistingResource(id);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  IsProtectedPatient(int32_t* isProtected,
-                                       void* payload,
-                                       int64_t id)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        *isProtected = backend->IsProtectedPatient(id);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  ListAvailableMetadata(OrthancPluginDatabaseContext* context,
-                                          void* payload,
-                                          int64_t id)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        std::list<int32_t> target;
-        backend->ListAvailableMetadata(target, id);
-
-        for (std::list<int32_t>::const_iterator
-               it = target.begin(); it != target.end(); ++it)
-        {
-          OrthancPluginDatabaseAnswerInt32(backend->GetOutput().context_,
-                                           backend->GetOutput().database_,
-                                           *it);
-        }
-
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-          
-         
-    static int32_t  ListAvailableAttachments(OrthancPluginDatabaseContext* context,
-                                             void* payload,
-                                             int64_t id)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        std::list<int32_t> target;
-        backend->ListAvailableAttachments(target, id);
-
-        for (std::list<int32_t>::const_iterator
-               it = target.begin(); it != target.end(); ++it)
-        {
-          OrthancPluginDatabaseAnswerInt32(backend->GetOutput().context_,
-                                           backend->GetOutput().database_,
-                                           *it);
-        }
-
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  LogChange(void* payload,
-                              const OrthancPluginChange* change)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->LogChange(*change);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-          
-         
-    static int32_t  LogExportedResource(void* payload,
-                                        const OrthancPluginExportedResource* exported)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->LogExportedResource(*exported);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-          
-         
-    static int32_t  LookupAttachment(OrthancPluginDatabaseContext* context,
-                                     void* payload,
-                                     int64_t id,
-                                     int32_t contentType)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_Attachment);
-
-      try
-      {
-        backend->LookupAttachment(id, contentType);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  LookupGlobalProperty(OrthancPluginDatabaseContext* context,
-                                         void* payload,
-                                         int32_t property)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        std::string s;
-        if (backend->LookupGlobalProperty(s, property))
-        {
-          OrthancPluginDatabaseAnswerString(backend->GetOutput().context_,
-                                            backend->GetOutput().database_,
-                                            s.c_str());
-        }
-
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  LookupIdentifier(OrthancPluginDatabaseContext* context,
-                                     void* payload,
-                                     const OrthancPluginDicomTag* tag)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        std::list<int64_t> target;
-        backend->LookupIdentifier(target, tag->group, tag->element, tag->value);
-
-        for (std::list<int64_t>::const_iterator
-               it = target.begin(); it != target.end(); ++it)
-        {
-          OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_,
-                                           backend->GetOutput().database_, *it);
-        }
-
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  LookupIdentifier2(OrthancPluginDatabaseContext* context,
-                                      void* payload,
-                                      const char* value)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        std::list<int64_t> target;
-        backend->LookupIdentifier(target, value);
-
-        for (std::list<int64_t>::const_iterator
-               it = target.begin(); it != target.end(); ++it)
-        {
-          OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_,
-                                           backend->GetOutput().database_, *it);
-        }
-
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  LookupMetadata(OrthancPluginDatabaseContext* context,
-                                   void* payload,
-                                   int64_t id,
-                                   int32_t metadata)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        std::string s;
-        if (backend->LookupMetadata(s, id, metadata))
-        {
-          OrthancPluginDatabaseAnswerString(backend->GetOutput().context_,
-                                            backend->GetOutput().database_, s.c_str());
-        }
-
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  LookupParent(OrthancPluginDatabaseContext* context,
-                                 void* payload,
-                                 int64_t id)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        int64_t parent;
-        if (backend->LookupParent(parent, id))
-        {
-          OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_,
-                                           backend->GetOutput().database_, parent);
-        }
-
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  LookupResource(OrthancPluginDatabaseContext* context,
-                                   void* payload,
-                                   const char* publicId)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        int64_t id;
-        OrthancPluginResourceType type;
-        if (backend->LookupResource(id, type, publicId))
-        {
-          OrthancPluginDatabaseAnswerResource(backend->GetOutput().context_,
-                                              backend->GetOutput().database_, 
-                                              id, type);
-        }
-
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  SelectPatientToRecycle(OrthancPluginDatabaseContext* context,
-                                           void* payload)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        int64_t id;
-        if (backend->SelectPatientToRecycle(id))
-        {
-          OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_,
-                                           backend->GetOutput().database_, id);
-        }
-
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  SelectPatientToRecycle2(OrthancPluginDatabaseContext* context,
-                                            void* payload,
-                                            int64_t patientIdToAvoid)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        int64_t id;
-        if (backend->SelectPatientToRecycle(id, patientIdToAvoid))
-        {
-          OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_,
-                                           backend->GetOutput().database_, id);
-        }
-
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  SetGlobalProperty(void* payload,
-                                      int32_t property,
-                                      const char* value)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->SetGlobalProperty(property, value);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  SetMainDicomTag(void* payload,
-                                    int64_t id,
-                                    const OrthancPluginDicomTag* tag)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->SetMainDicomTag(id, tag->group, tag->element, tag->value);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  SetIdentifierTag(void* payload,
-                                    int64_t id,
-                                    const OrthancPluginDicomTag* tag)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->SetIdentifierTag(id, tag->group, tag->element, tag->value);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  SetMetadata(void* payload,
-                                int64_t id,
-                                int32_t metadata,
-                                const char* value)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->SetMetadata(id, metadata, value);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t  SetProtectedPatient(void* payload,
-                                        int64_t id,
-                                        int32_t isProtected)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->SetProtectedPatient(id, isProtected);
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t StartTransaction(void* payload)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->StartTransaction();
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t RollbackTransaction(void* payload)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->RollbackTransaction();
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t CommitTransaction(void* payload)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->CommitTransaction();
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t Open(void* payload)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->Open();
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-
-    static int32_t Close(void* payload)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        backend->Close();
-        return 0;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return -1;
-      }
-    }
-
-    
-  public:
-    static void Register(OrthancPluginContext* context,
-                         IDatabaseBackend& backend)
-    {
-      OrthancPluginDatabaseBackend  params;
-      memset(&params, 0, sizeof(params));
-
-      params.addAttachment = AddAttachment;
-      params.attachChild = AttachChild;
-      params.clearChanges = ClearChanges;
-      params.clearExportedResources = ClearExportedResources;
-      params.createResource = CreateResource;
-      params.deleteAttachment = DeleteAttachment;
-      params.deleteMetadata = DeleteMetadata;
-      params.deleteResource = DeleteResource;
-      params.getAllPublicIds = GetAllPublicIds;
-      params.getChanges = GetChanges;
-      params.getChildrenInternalId = GetChildrenInternalId;
-      params.getChildrenPublicId = GetChildrenPublicId;
-      params.getExportedResources = GetExportedResources;
-      params.getLastChange = GetLastChange;
-      params.getLastExportedResource = GetLastExportedResource;
-      params.getMainDicomTags = GetMainDicomTags;
-      params.getPublicId = GetPublicId;
-      params.getResourceCount = GetResourceCount;
-      params.getResourceType = GetResourceType;
-      params.getTotalCompressedSize = GetTotalCompressedSize;
-      params.getTotalUncompressedSize = GetTotalUncompressedSize;
-      params.isExistingResource = IsExistingResource;
-      params.isProtectedPatient = IsProtectedPatient;
-      params.listAvailableMetadata = ListAvailableMetadata;
-      params.listAvailableAttachments = ListAvailableAttachments;
-      params.logChange = LogChange;
-      params.logExportedResource = LogExportedResource;
-      params.lookupAttachment = LookupAttachment;
-      params.lookupGlobalProperty = LookupGlobalProperty;
-      params.lookupIdentifier = LookupIdentifier;
-      params.lookupIdentifier2 = LookupIdentifier2;
-      params.lookupMetadata = LookupMetadata;
-      params.lookupParent = LookupParent;
-      params.lookupResource = LookupResource;
-      params.selectPatientToRecycle = SelectPatientToRecycle;
-      params.selectPatientToRecycle2 = SelectPatientToRecycle2;
-      params.setGlobalProperty = SetGlobalProperty;
-      params.setMainDicomTag = SetMainDicomTag;
-      params.setIdentifierTag = SetIdentifierTag;
-      params.setMetadata = SetMetadata;
-      params.setProtectedPatient = SetProtectedPatient;
-      params.startTransaction = StartTransaction;
-      params.rollbackTransaction = RollbackTransaction;
-      params.commitTransaction = CommitTransaction;
-      params.open = Open;
-      params.close = Close;
-
-      OrthancPluginDatabaseContext* database = OrthancPluginRegisterDatabaseBackend(context, &params, &backend);
-      if (!context)
-      {
-        throw std::runtime_error("Unable to register the database backend");
-      }
-
-      backend.RegisterOutput(new DatabaseBackendOutput(context, database));
-    }
-  };
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Include/orthanc/OrthancCDatabasePlugin.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,782 @@
+/**
+ * @ingroup CInterface
+ **/
+
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+
+#pragma once
+
+#include "OrthancCPlugin.h"
+
+
+/** @{ */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+  /**
+   * Opaque structure that represents the context of a custom database engine.
+   * @ingroup Callbacks
+   **/
+  typedef struct _OrthancPluginDatabaseContext_t OrthancPluginDatabaseContext;
+
+
+/*<! @cond Doxygen_Suppress */
+  typedef enum
+  {
+    _OrthancPluginDatabaseAnswerType_None = 0,
+
+    /* Events */
+    _OrthancPluginDatabaseAnswerType_DeletedAttachment = 1,
+    _OrthancPluginDatabaseAnswerType_DeletedResource = 2,
+    _OrthancPluginDatabaseAnswerType_RemainingAncestor = 3,
+
+    /* Return value */
+    _OrthancPluginDatabaseAnswerType_Attachment = 10,
+    _OrthancPluginDatabaseAnswerType_Change = 11,
+    _OrthancPluginDatabaseAnswerType_DicomTag = 12,
+    _OrthancPluginDatabaseAnswerType_ExportedResource = 13,
+    _OrthancPluginDatabaseAnswerType_Int32 = 14,
+    _OrthancPluginDatabaseAnswerType_Int64 = 15,
+    _OrthancPluginDatabaseAnswerType_Resource = 16,
+    _OrthancPluginDatabaseAnswerType_String = 17,
+
+    _OrthancPluginDatabaseAnswerType_INTERNAL = 0x7fffffff
+  } _OrthancPluginDatabaseAnswerType;
+
+
+  typedef struct
+  {
+    const char* uuid;
+    int32_t     contentType;
+    uint64_t    uncompressedSize;
+    const char* uncompressedHash;
+    int32_t     compressionType;
+    uint64_t    compressedSize;
+    const char* compressedHash;
+  } OrthancPluginAttachment;
+
+  typedef struct
+  {
+    uint16_t     group;
+    uint16_t     element;
+    const char*  value;
+  } OrthancPluginDicomTag;
+
+  typedef struct
+  {
+    int64_t                    seq;
+    int32_t                    changeType;
+    OrthancPluginResourceType  resourceType;
+    const char*                publicId;
+    const char*                date;
+  } OrthancPluginChange;
+
+  typedef struct
+  {
+    int64_t                    seq;
+    OrthancPluginResourceType  resourceType;
+    const char*                publicId;
+    const char*                modality;
+    const char*                date;
+    const char*                patientId;
+    const char*                studyInstanceUid;
+    const char*                seriesInstanceUid;
+    const char*                sopInstanceUid;
+  } OrthancPluginExportedResource;
+
+
+  typedef struct
+  {
+    OrthancPluginDatabaseContext* database;
+    _OrthancPluginDatabaseAnswerType  type;
+    int32_t      valueInt32;
+    uint32_t     valueUint32;
+    int64_t      valueInt64;
+    const char  *valueString;
+    const void  *valueGeneric;
+  } _OrthancPluginDatabaseAnswer;
+
+  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerString(
+    OrthancPluginContext*          context,
+    OrthancPluginDatabaseContext*  database,
+    const char*                    value)
+  {
+    _OrthancPluginDatabaseAnswer params;
+    memset(&params, 0, sizeof(params));
+    params.database = database;
+    params.type = _OrthancPluginDatabaseAnswerType_String;
+    params.valueString = value;
+    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
+  }
+
+  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerChange(
+    OrthancPluginContext*          context,
+    OrthancPluginDatabaseContext*  database,
+    const OrthancPluginChange*     change)
+  {
+    _OrthancPluginDatabaseAnswer params;
+    memset(&params, 0, sizeof(params));
+
+    params.database = database;
+    params.type = _OrthancPluginDatabaseAnswerType_Change;
+    params.valueUint32 = 0;
+    params.valueGeneric = change;
+
+    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
+  }
+
+  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerChangesDone(
+    OrthancPluginContext*          context,
+    OrthancPluginDatabaseContext*  database)
+  {
+    _OrthancPluginDatabaseAnswer params;
+    memset(&params, 0, sizeof(params));
+
+    params.database = database;
+    params.type = _OrthancPluginDatabaseAnswerType_Change;
+    params.valueUint32 = 1;
+    params.valueGeneric = NULL;
+
+    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
+  }
+
+  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerInt32(
+    OrthancPluginContext*          context,
+    OrthancPluginDatabaseContext*  database,
+    int32_t                        value)
+  {
+    _OrthancPluginDatabaseAnswer params;
+    memset(&params, 0, sizeof(params));
+    params.database = database;
+    params.type = _OrthancPluginDatabaseAnswerType_Int32;
+    params.valueInt32 = value;
+    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
+  }
+
+  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerInt64(
+    OrthancPluginContext*          context,
+    OrthancPluginDatabaseContext*  database,
+    int64_t                        value)
+  {
+    _OrthancPluginDatabaseAnswer params;
+    memset(&params, 0, sizeof(params));
+    params.database = database;
+    params.type = _OrthancPluginDatabaseAnswerType_Int64;
+    params.valueInt64 = value;
+    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
+  }
+
+  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerExportedResource(
+    OrthancPluginContext*                 context,
+    OrthancPluginDatabaseContext*         database,
+    const OrthancPluginExportedResource*  exported)
+  {
+    _OrthancPluginDatabaseAnswer params;
+    memset(&params, 0, sizeof(params));
+
+    params.database = database;
+    params.type = _OrthancPluginDatabaseAnswerType_ExportedResource;
+    params.valueUint32 = 0;
+    params.valueGeneric = exported;
+    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
+  }
+
+  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerExportedResourcesDone(
+    OrthancPluginContext*                 context,
+    OrthancPluginDatabaseContext*         database)
+  {
+    _OrthancPluginDatabaseAnswer params;
+    memset(&params, 0, sizeof(params));
+
+    params.database = database;
+    params.type = _OrthancPluginDatabaseAnswerType_ExportedResource;
+    params.valueUint32 = 1;
+    params.valueGeneric = NULL;
+    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
+  }
+
+  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerDicomTag(
+    OrthancPluginContext*          context,
+    OrthancPluginDatabaseContext*  database,
+    const OrthancPluginDicomTag*   tag)
+  {
+    _OrthancPluginDatabaseAnswer params;
+    memset(&params, 0, sizeof(params));
+    params.database = database;
+    params.type = _OrthancPluginDatabaseAnswerType_DicomTag;
+    params.valueGeneric = tag;
+    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
+  }
+
+  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerAttachment(
+    OrthancPluginContext*          context,
+    OrthancPluginDatabaseContext*  database,
+    const OrthancPluginAttachment* attachment)
+  {
+    _OrthancPluginDatabaseAnswer params;
+    memset(&params, 0, sizeof(params));
+    params.database = database;
+    params.type = _OrthancPluginDatabaseAnswerType_Attachment;
+    params.valueGeneric = attachment;
+    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
+  }
+
+  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerResource(
+    OrthancPluginContext*          context,
+    OrthancPluginDatabaseContext*  database,
+    int64_t                        id,
+    OrthancPluginResourceType      resourceType)
+  {
+    _OrthancPluginDatabaseAnswer params;
+    memset(&params, 0, sizeof(params));
+    params.database = database;
+    params.type = _OrthancPluginDatabaseAnswerType_Resource;
+    params.valueInt64 = id;
+    params.valueInt32 = (int32_t) resourceType;
+    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
+  }
+
+  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseSignalDeletedAttachment(
+    OrthancPluginContext*          context,
+    OrthancPluginDatabaseContext*  database,
+    const OrthancPluginAttachment* attachment)
+  {
+    _OrthancPluginDatabaseAnswer params;
+    memset(&params, 0, sizeof(params));
+    params.database = database;
+    params.type = _OrthancPluginDatabaseAnswerType_DeletedAttachment;
+    params.valueGeneric = attachment;
+    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
+  }
+
+  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseSignalDeletedResource(
+    OrthancPluginContext*          context,
+    OrthancPluginDatabaseContext*  database,
+    const char*                    publicId,
+    OrthancPluginResourceType      resourceType)
+  {
+    _OrthancPluginDatabaseAnswer params;
+    memset(&params, 0, sizeof(params));
+    params.database = database;
+    params.type = _OrthancPluginDatabaseAnswerType_DeletedResource;
+    params.valueString = publicId;
+    params.valueInt32 = (int32_t) resourceType;
+    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
+  }
+
+  ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseSignalRemainingAncestor(
+    OrthancPluginContext*          context,
+    OrthancPluginDatabaseContext*  database,
+    const char*                    ancestorId,
+    OrthancPluginResourceType      ancestorType)
+  {
+    _OrthancPluginDatabaseAnswer params;
+    memset(&params, 0, sizeof(params));
+    params.database = database;
+    params.type = _OrthancPluginDatabaseAnswerType_RemainingAncestor;
+    params.valueString = ancestorId;
+    params.valueInt32 = (int32_t) ancestorType;
+    context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, &params);
+  }
+
+
+
+
+
+  typedef struct
+  {
+    OrthancPluginErrorCode  (*addAttachment) (
+      /* inputs */
+      void* payload,
+      int64_t id,
+      const OrthancPluginAttachment* attachment);
+                             
+    OrthancPluginErrorCode  (*attachChild) (
+      /* inputs */
+      void* payload,
+      int64_t parent,
+      int64_t child);
+                             
+    OrthancPluginErrorCode  (*clearChanges) (
+      /* inputs */
+      void* payload);
+                             
+    OrthancPluginErrorCode  (*clearExportedResources) (
+      /* inputs */
+      void* payload);
+
+    OrthancPluginErrorCode  (*createResource) (
+      /* outputs */
+      int64_t* id, 
+      /* inputs */
+      void* payload,
+      const char* publicId,
+      OrthancPluginResourceType resourceType);           
+                   
+    OrthancPluginErrorCode  (*deleteAttachment) (
+      /* inputs */
+      void* payload,
+      int64_t id,
+      int32_t contentType);
+   
+    OrthancPluginErrorCode  (*deleteMetadata) (
+      /* inputs */
+      void* payload,
+      int64_t id,
+      int32_t metadataType);
+   
+    OrthancPluginErrorCode  (*deleteResource) (
+      /* inputs */
+      void* payload,
+      int64_t id);    
+
+    /* Output: Use OrthancPluginDatabaseAnswerString() */
+    OrthancPluginErrorCode  (*getAllPublicIds) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload,
+      OrthancPluginResourceType resourceType);
+
+    /* Output: Use OrthancPluginDatabaseAnswerChange() and
+     * OrthancPluginDatabaseAnswerChangesDone() */
+    OrthancPluginErrorCode  (*getChanges) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload,
+      int64_t since,
+      uint32_t maxResult);
+
+    /* Output: Use OrthancPluginDatabaseAnswerInt64() */
+    OrthancPluginErrorCode  (*getChildrenInternalId) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload,
+      int64_t id);
+                   
+    /* Output: Use OrthancPluginDatabaseAnswerString() */
+    OrthancPluginErrorCode  (*getChildrenPublicId) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload,
+      int64_t id);
+
+    /* Output: Use OrthancPluginDatabaseAnswerExportedResource() and
+     * OrthancPluginDatabaseAnswerExportedResourcesDone() */
+    OrthancPluginErrorCode  (*getExportedResources) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload,
+      int64_t  since,
+      uint32_t  maxResult);
+                   
+    /* Output: Use OrthancPluginDatabaseAnswerChange() */
+    OrthancPluginErrorCode  (*getLastChange) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload);
+
+    /* Output: Use OrthancPluginDatabaseAnswerExportedResource() */
+    OrthancPluginErrorCode  (*getLastExportedResource) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload);
+                   
+    /* Output: Use OrthancPluginDatabaseAnswerDicomTag() */
+    OrthancPluginErrorCode  (*getMainDicomTags) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload,
+      int64_t id);
+                   
+    /* Output: Use OrthancPluginDatabaseAnswerString() */
+    OrthancPluginErrorCode  (*getPublicId) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload,
+      int64_t id);
+
+    OrthancPluginErrorCode  (*getResourceCount) (
+      /* outputs */
+      uint64_t* target,
+      /* inputs */
+      void* payload,
+      OrthancPluginResourceType  resourceType);
+                   
+    OrthancPluginErrorCode  (*getResourceType) (
+      /* outputs */
+      OrthancPluginResourceType* resourceType,
+      /* inputs */
+      void* payload,
+      int64_t id);
+
+    OrthancPluginErrorCode  (*getTotalCompressedSize) (
+      /* outputs */
+      uint64_t* target,
+      /* inputs */
+      void* payload);
+                   
+    OrthancPluginErrorCode  (*getTotalUncompressedSize) (
+      /* outputs */
+      uint64_t* target,
+      /* inputs */
+      void* payload);
+                   
+    OrthancPluginErrorCode  (*isExistingResource) (
+      /* outputs */
+      int32_t* existing,
+      /* inputs */
+      void* payload,
+      int64_t id);
+
+    OrthancPluginErrorCode  (*isProtectedPatient) (
+      /* outputs */
+      int32_t* isProtected,
+      /* inputs */
+      void* payload,
+      int64_t id);
+
+    /* Output: Use OrthancPluginDatabaseAnswerInt32() */
+    OrthancPluginErrorCode  (*listAvailableMetadata) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload,
+      int64_t id);
+                   
+    /* Output: Use OrthancPluginDatabaseAnswerInt32() */
+    OrthancPluginErrorCode  (*listAvailableAttachments) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload,
+      int64_t id);
+
+    OrthancPluginErrorCode  (*logChange) (
+      /* inputs */
+      void* payload,
+      const OrthancPluginChange* change);
+                   
+    OrthancPluginErrorCode  (*logExportedResource) (
+      /* inputs */
+      void* payload,
+      const OrthancPluginExportedResource* exported);
+                   
+    /* Output: Use OrthancPluginDatabaseAnswerAttachment() */
+    OrthancPluginErrorCode  (*lookupAttachment) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload,
+      int64_t id,
+      int32_t contentType);
+
+    /* Output: Use OrthancPluginDatabaseAnswerString() */
+    OrthancPluginErrorCode  (*lookupGlobalProperty) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload,
+      int32_t property);
+
+    /* Output: Use OrthancPluginDatabaseAnswerInt64() */
+    OrthancPluginErrorCode  (*lookupIdentifier) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload,
+      const OrthancPluginDicomTag* tag);
+
+    /* Output: Use OrthancPluginDatabaseAnswerInt64() */
+    OrthancPluginErrorCode  (*lookupIdentifier2) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload,
+      const char* value);
+
+    /* Output: Use OrthancPluginDatabaseAnswerString() */
+    OrthancPluginErrorCode  (*lookupMetadata) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload,
+      int64_t id,
+      int32_t metadata);
+
+    /* Output: Use OrthancPluginDatabaseAnswerInt64() */
+    OrthancPluginErrorCode  (*lookupParent) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload,
+      int64_t id);
+
+    /* Output: Use OrthancPluginDatabaseAnswerResource() */
+    OrthancPluginErrorCode  (*lookupResource) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload,
+      const char* publicId);
+
+    /* Output: Use OrthancPluginDatabaseAnswerInt64() */
+    OrthancPluginErrorCode  (*selectPatientToRecycle) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload);
+
+    /* Output: Use OrthancPluginDatabaseAnswerInt64() */
+    OrthancPluginErrorCode  (*selectPatientToRecycle2) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload,
+      int64_t patientIdToAvoid);
+
+    OrthancPluginErrorCode  (*setGlobalProperty) (
+      /* inputs */
+      void* payload,
+      int32_t property,
+      const char* value);
+
+    OrthancPluginErrorCode  (*setMainDicomTag) (
+      /* inputs */
+      void* payload,
+      int64_t id,
+      const OrthancPluginDicomTag* tag);
+
+    OrthancPluginErrorCode  (*setIdentifierTag) (
+      /* inputs */
+      void* payload,
+      int64_t id,
+      const OrthancPluginDicomTag* tag);
+
+    OrthancPluginErrorCode  (*setMetadata) (
+      /* inputs */
+      void* payload,
+      int64_t id,
+      int32_t metadata,
+      const char* value);
+
+    OrthancPluginErrorCode  (*setProtectedPatient) (
+      /* inputs */
+      void* payload,
+      int64_t id,
+      int32_t isProtected);
+
+    OrthancPluginErrorCode  (*startTransaction) (
+      /* inputs */
+      void* payload);
+
+    OrthancPluginErrorCode  (*rollbackTransaction) (
+      /* inputs */
+      void* payload);
+
+    OrthancPluginErrorCode  (*commitTransaction) (
+      /* inputs */
+      void* payload);
+
+    OrthancPluginErrorCode  (*open) (
+      /* inputs */
+      void* payload);
+
+    OrthancPluginErrorCode  (*close) (
+      /* inputs */
+      void* payload);
+
+  } OrthancPluginDatabaseBackend;
+
+
+  typedef struct
+  {
+    /* Output: Use OrthancPluginDatabaseAnswerString() */
+    OrthancPluginErrorCode  (*getAllPublicIdsWithLimit) (
+      /* outputs */
+      OrthancPluginDatabaseContext* context,
+      /* inputs */
+      void* payload,
+      OrthancPluginResourceType resourceType,
+      uint64_t since,
+      uint64_t limit);
+
+    OrthancPluginErrorCode  (*getDatabaseVersion) (
+      /* outputs */
+      uint32_t* version,
+      /* inputs */
+      void* payload);
+
+    OrthancPluginErrorCode  (*upgradeDatabase) (
+      /* inputs */
+      void* payload,
+      uint32_t targetVersion,
+      OrthancPluginStorageArea* storageArea);
+  } OrthancPluginDatabaseExtensions;
+
+/*<! @endcond */
+
+
+  typedef struct
+  {
+    OrthancPluginDatabaseContext**       result;
+    const OrthancPluginDatabaseBackend*  backend;
+    void*                                payload;
+  } _OrthancPluginRegisterDatabaseBackend;
+
+  /**
+   * Register a custom database back-end.
+   *
+   * Instead of manually filling the OrthancPluginDatabaseBackend
+   * structure, you should instead implement a concrete C++ class
+   * deriving from ::OrthancPlugins::IDatabaseBackend, and register it
+   * using ::OrthancPlugins::DatabaseBackendAdapter::Register().
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param backend The callbacks of the custom database engine.
+   * @param payload Pointer containing private information for the database engine.
+   * @return The context of the database engine (it must not be manually freed).
+   * @ingroup Callbacks
+   * @deprecated
+   * @see OrthancPluginRegisterDatabaseBackendV2
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginDatabaseContext* OrthancPluginRegisterDatabaseBackend(
+    OrthancPluginContext*                context,
+    const OrthancPluginDatabaseBackend*  backend,
+    void*                                payload)
+  {
+    OrthancPluginDatabaseContext* result = NULL;
+    _OrthancPluginRegisterDatabaseBackend params;
+
+    if (sizeof(int32_t) != sizeof(_OrthancPluginDatabaseAnswerType))
+    {
+      return NULL;
+    }
+
+    memset(&params, 0, sizeof(params));
+    params.backend = backend;
+    params.result = &result;
+    params.payload = payload;
+
+    if (context->InvokeService(context, _OrthancPluginService_RegisterDatabaseBackend, &params) ||
+        result == NULL)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+  typedef struct
+  {
+    OrthancPluginDatabaseContext**          result;
+    const OrthancPluginDatabaseBackend*     backend;
+    void*                                   payload;
+    const OrthancPluginDatabaseExtensions*  extensions;
+    uint32_t                                extensionsSize;
+  } _OrthancPluginRegisterDatabaseBackendV2;
+
+
+  /**
+   * Register a custom database back-end.
+   *
+   * Instead of manually filling the OrthancPluginDatabaseBackendV2
+   * structure, you should instead implement a concrete C++ class
+   * deriving from ::OrthancPlugins::IDatabaseBackend, and register it
+   * using ::OrthancPlugins::DatabaseBackendAdapter::Register().
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param backend The callbacks of the custom database engine.
+   * @param payload Pointer containing private information for the database engine.
+   * @param extensions Extensions to the base database SDK that was shipped until Orthanc 0.9.3.
+   * @return The context of the database engine (it must not be manually freed).
+   * @ingroup Callbacks
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginDatabaseContext* OrthancPluginRegisterDatabaseBackendV2(
+    OrthancPluginContext*                   context,
+    const OrthancPluginDatabaseBackend*     backend,
+    const OrthancPluginDatabaseExtensions*  extensions,
+    void*                                   payload)
+  {
+    OrthancPluginDatabaseContext* result = NULL;
+    _OrthancPluginRegisterDatabaseBackendV2 params;
+
+    if (sizeof(int32_t) != sizeof(_OrthancPluginDatabaseAnswerType))
+    {
+      return NULL;
+    }
+
+    memset(&params, 0, sizeof(params));
+    params.backend = backend;
+    params.result = &result;
+    params.payload = payload;
+    params.extensions = extensions;
+    params.extensionsSize = sizeof(OrthancPluginDatabaseExtensions);
+
+    if (context->InvokeService(context, _OrthancPluginService_RegisterDatabaseBackendV2, &params) ||
+        result == NULL)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+#ifdef  __cplusplus
+}
+#endif
+
+
+/** @} */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Include/orthanc/OrthancCPlugin.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,3775 @@
+/**
+ * \mainpage
+ *
+ * This C/C++ SDK allows external developers to create plugins that
+ * can be loaded into Orthanc to extend its functionality. Each
+ * Orthanc plugin must expose 4 public functions with the following
+ * signatures:
+ * 
+ * -# <tt>int32_t OrthancPluginInitialize(const OrthancPluginContext* context)</tt>:
+ *    This function is invoked by Orthanc when it loads the plugin on startup.
+ *    The plugin must:
+ *    - Check its compatibility with the Orthanc version using
+ *      ::OrthancPluginCheckVersion().
+ *    - Store the context pointer so that it can use the plugin 
+ *      services of Orthanc.
+ *    - Register all its REST callbacks using ::OrthancPluginRegisterRestCallback().
+ *    - Possibly register its callback for received DICOM instances using ::OrthancPluginRegisterOnStoredInstanceCallback().
+ *    - Possibly register its callback for changes to the DICOM store using ::OrthancPluginRegisterOnChangeCallback().
+ *    - Possibly register a custom storage area using ::OrthancPluginRegisterStorageArea().
+ *    - Possibly register a custom database back-end area using OrthancPluginRegisterDatabaseBackendV2().
+ * -# <tt>void OrthancPluginFinalize()</tt>:
+ *    This function is invoked by Orthanc during its shutdown. The plugin
+ *    must free all its memory.
+ * -# <tt>const char* OrthancPluginGetName()</tt>:
+ *    The plugin must return a short string to identify itself.
+ * -# <tt>const char* OrthancPluginGetVersion()</tt>:
+ *    The plugin must return a string containing its version number.
+ *
+ * The name and the version of a plugin is only used to prevent it
+ * from being loaded twice. Note that, in C++, it is mandatory to
+ * declare these functions within an <tt>extern "C"</tt> section.
+ * 
+ * To ensure multi-threading safety, the various REST callbacks are
+ * guaranteed to be executed in mutual exclusion since Orthanc
+ * 0.8.5. If this feature is undesired (notably when developing
+ * high-performance plugins handling simultaneous requests), use
+ * ::OrthancPluginRegisterRestCallbackNoLock().
+ **/
+
+
+
+/**
+ * @defgroup Images Images and compression
+ * @brief Functions to deal with images and compressed buffers.
+ *
+ * @defgroup REST REST
+ * @brief Functions to answer REST requests in a callback.
+ *
+ * @defgroup Callbacks Callbacks
+ * @brief Functions to register and manage callbacks by the plugins.
+ *
+ * @defgroup Orthanc Orthanc
+ * @brief Functions to access the content of the Orthanc server.
+ **/
+
+
+
+/**
+ * @defgroup Toolbox Toolbox
+ * @brief Generic functions to help with the creation of plugins.
+ **/
+
+
+
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+
+#pragma once
+
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef WIN32
+#define ORTHANC_PLUGINS_API __declspec(dllexport)
+#else
+#define ORTHANC_PLUGINS_API
+#endif
+
+#define ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER     0
+#define ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER     9
+#define ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER  5
+
+
+
+/********************************************************************
+ ** Check that function inlining is properly supported. The use of
+ ** inlining is required, to avoid the duplication of object code
+ ** between two compilation modules that would use the Orthanc Plugin
+ ** API.
+ ********************************************************************/
+
+/* If the auto-detection of the "inline" keyword below does not work
+   automatically and that your compiler is known to properly support
+   inlining, uncomment the following #define and adapt the definition
+   of "static inline". */
+
+/* #define ORTHANC_PLUGIN_INLINE static inline */
+
+#ifndef ORTHANC_PLUGIN_INLINE
+#  if __STDC_VERSION__ >= 199901L
+/*   This is C99 or above: http://predef.sourceforge.net/prestd.html */
+#    define ORTHANC_PLUGIN_INLINE static inline
+#  elif defined(__cplusplus)
+/*   This is C++ */
+#    define ORTHANC_PLUGIN_INLINE static inline
+#  elif defined(__GNUC__)
+/*   This is GCC running in C89 mode */
+#    define ORTHANC_PLUGIN_INLINE static __inline
+#  elif defined(_MSC_VER)
+/*   This is Visual Studio running in C89 mode */
+#    define ORTHANC_PLUGIN_INLINE static __inline
+#  else
+#    error Your compiler is not known to support the "inline" keyword
+#  endif
+#endif
+
+
+
+/********************************************************************
+ ** Inclusion of standard libraries.
+ ********************************************************************/
+
+/**
+ * For Microsoft Visual Studio, a compatibility "stdint.h" can be
+ * downloaded at the following URL:
+ * https://orthanc.googlecode.com/hg/Resources/ThirdParty/VisualStudio/stdint.h
+ **/
+#include <stdint.h>
+
+#include <stdlib.h>
+
+
+
+/********************************************************************
+ ** Definition of the Orthanc Plugin API.
+ ********************************************************************/
+
+/** @{ */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+  /**
+   * The various error codes that can be returned by the Orthanc core.
+   **/
+  typedef enum
+  {
+    OrthancPluginErrorCode_InternalError = -1    /*!< Internal error */,
+    OrthancPluginErrorCode_Success = 0    /*!< Success */,
+    OrthancPluginErrorCode_Plugin = 1    /*!< Error encountered within the plugin engine */,
+    OrthancPluginErrorCode_NotImplemented = 2    /*!< Not implemented yet */,
+    OrthancPluginErrorCode_ParameterOutOfRange = 3    /*!< Parameter out of range */,
+    OrthancPluginErrorCode_NotEnoughMemory = 4    /*!< Not enough memory */,
+    OrthancPluginErrorCode_BadParameterType = 5    /*!< Bad type for a parameter */,
+    OrthancPluginErrorCode_BadSequenceOfCalls = 6    /*!< Bad sequence of calls */,
+    OrthancPluginErrorCode_InexistentItem = 7    /*!< Accessing an inexistent item */,
+    OrthancPluginErrorCode_BadRequest = 8    /*!< Bad request */,
+    OrthancPluginErrorCode_NetworkProtocol = 9    /*!< Error in the network protocol */,
+    OrthancPluginErrorCode_SystemCommand = 10    /*!< Error while calling a system command */,
+    OrthancPluginErrorCode_Database = 11    /*!< Error with the database engine */,
+    OrthancPluginErrorCode_UriSyntax = 12    /*!< Badly formatted URI */,
+    OrthancPluginErrorCode_InexistentFile = 13    /*!< Inexistent file */,
+    OrthancPluginErrorCode_CannotWriteFile = 14    /*!< Cannot write to file */,
+    OrthancPluginErrorCode_BadFileFormat = 15    /*!< Bad file format */,
+    OrthancPluginErrorCode_Timeout = 16    /*!< Timeout */,
+    OrthancPluginErrorCode_UnknownResource = 17    /*!< Unknown resource */,
+    OrthancPluginErrorCode_IncompatibleDatabaseVersion = 18    /*!< Incompatible version of the database */,
+    OrthancPluginErrorCode_FullStorage = 19    /*!< The file storage is full */,
+    OrthancPluginErrorCode_CorruptedFile = 20    /*!< Corrupted file (e.g. inconsistent MD5 hash) */,
+    OrthancPluginErrorCode_InexistentTag = 21    /*!< Inexistent tag */,
+    OrthancPluginErrorCode_ReadOnly = 22    /*!< Cannot modify a read-only data structure */,
+    OrthancPluginErrorCode_IncompatibleImageFormat = 23    /*!< Incompatible format of the images */,
+    OrthancPluginErrorCode_IncompatibleImageSize = 24    /*!< Incompatible size of the images */,
+    OrthancPluginErrorCode_SharedLibrary = 25    /*!< Error while using a shared library (plugin) */,
+    OrthancPluginErrorCode_UnknownPluginService = 26    /*!< Plugin invoking an unknown service */,
+    OrthancPluginErrorCode_UnknownDicomTag = 27    /*!< Unknown DICOM tag */,
+    OrthancPluginErrorCode_BadJson = 28    /*!< Cannot parse a JSON document */,
+    OrthancPluginErrorCode_Unauthorized = 29    /*!< Bad credentials were provided to an HTTP request */,
+    OrthancPluginErrorCode_BadFont = 30    /*!< Badly formatted font file */,
+    OrthancPluginErrorCode_DatabasePlugin = 31    /*!< The plugin implementing a custom database back-end does not fulfill the proper interface */,
+    OrthancPluginErrorCode_StorageAreaPlugin = 32    /*!< Error in the plugin implementing a custom storage area */,
+    OrthancPluginErrorCode_SQLiteNotOpened = 1000    /*!< SQLite: The database is not opened */,
+    OrthancPluginErrorCode_SQLiteAlreadyOpened = 1001    /*!< SQLite: Connection is already open */,
+    OrthancPluginErrorCode_SQLiteCannotOpen = 1002    /*!< SQLite: Unable to open the database */,
+    OrthancPluginErrorCode_SQLiteStatementAlreadyUsed = 1003    /*!< SQLite: This cached statement is already being referred to */,
+    OrthancPluginErrorCode_SQLiteExecute = 1004    /*!< SQLite: Cannot execute a command */,
+    OrthancPluginErrorCode_SQLiteRollbackWithoutTransaction = 1005    /*!< SQLite: Rolling back a nonexistent transaction (have you called Begin()?) */,
+    OrthancPluginErrorCode_SQLiteCommitWithoutTransaction = 1006    /*!< SQLite: Committing a nonexistent transaction */,
+    OrthancPluginErrorCode_SQLiteRegisterFunction = 1007    /*!< SQLite: Unable to register a function */,
+    OrthancPluginErrorCode_SQLiteFlush = 1008    /*!< SQLite: Unable to flush the database */,
+    OrthancPluginErrorCode_SQLiteCannotRun = 1009    /*!< SQLite: Cannot run a cached statement */,
+    OrthancPluginErrorCode_SQLiteCannotStep = 1010    /*!< SQLite: Cannot step over a cached statement */,
+    OrthancPluginErrorCode_SQLiteBindOutOfRange = 1011    /*!< SQLite: Bing a value while out of range (serious error) */,
+    OrthancPluginErrorCode_SQLitePrepareStatement = 1012    /*!< SQLite: Cannot prepare a cached statement */,
+    OrthancPluginErrorCode_SQLiteTransactionAlreadyStarted = 1013    /*!< SQLite: Beginning the same transaction twice */,
+    OrthancPluginErrorCode_SQLiteTransactionCommit = 1014    /*!< SQLite: Failure when committing the transaction */,
+    OrthancPluginErrorCode_SQLiteTransactionBegin = 1015    /*!< SQLite: Cannot start a transaction */,
+    OrthancPluginErrorCode_DirectoryOverFile = 2000    /*!< The directory to be created is already occupied by a regular file */,
+    OrthancPluginErrorCode_FileStorageCannotWrite = 2001    /*!< Unable to create a subdirectory or a file in the file storage */,
+    OrthancPluginErrorCode_DirectoryExpected = 2002    /*!< The specified path does not point to a directory */,
+    OrthancPluginErrorCode_HttpPortInUse = 2003    /*!< The TCP port of the HTTP server is already in use */,
+    OrthancPluginErrorCode_DicomPortInUse = 2004    /*!< The TCP port of the DICOM server is already in use */,
+    OrthancPluginErrorCode_BadHttpStatusInRest = 2005    /*!< This HTTP status is not allowed in a REST API */,
+    OrthancPluginErrorCode_RegularFileExpected = 2006    /*!< The specified path does not point to a regular file */,
+    OrthancPluginErrorCode_PathToExecutable = 2007    /*!< Unable to get the path to the executable */,
+    OrthancPluginErrorCode_MakeDirectory = 2008    /*!< Cannot create a directory */,
+    OrthancPluginErrorCode_BadApplicationEntityTitle = 2009    /*!< An application entity title (AET) cannot be empty or be longer than 16 characters */,
+    OrthancPluginErrorCode_NoCFindHandler = 2010    /*!< No request handler factory for DICOM C-FIND SCP */,
+    OrthancPluginErrorCode_NoCMoveHandler = 2011    /*!< No request handler factory for DICOM C-MOVE SCP */,
+    OrthancPluginErrorCode_NoCStoreHandler = 2012    /*!< No request handler factory for DICOM C-STORE SCP */,
+    OrthancPluginErrorCode_NoApplicationEntityFilter = 2013    /*!< No application entity filter */,
+    OrthancPluginErrorCode_NoSopClassOrInstance = 2014    /*!< DicomUserConnection: Unable to find the SOP class and instance */,
+    OrthancPluginErrorCode_NoPresentationContext = 2015    /*!< DicomUserConnection: No acceptable presentation context for modality */,
+    OrthancPluginErrorCode_DicomFindUnavailable = 2016    /*!< DicomUserConnection: The C-FIND command is not supported by the remote SCP */,
+    OrthancPluginErrorCode_DicomMoveUnavailable = 2017    /*!< DicomUserConnection: The C-MOVE command is not supported by the remote SCP */,
+    OrthancPluginErrorCode_CannotStoreInstance = 2018    /*!< Cannot store an instance */,
+    OrthancPluginErrorCode_CreateDicomNotString = 2019    /*!< Only string values are supported when creating DICOM instances */,
+    OrthancPluginErrorCode_CreateDicomOverrideTag = 2020    /*!< Trying to override a value inherited from a parent module */,
+    OrthancPluginErrorCode_CreateDicomUseContent = 2021    /*!< Use \"Content\" to inject an image into a new DICOM instance */,
+    OrthancPluginErrorCode_CreateDicomNoPayload = 2022    /*!< No payload is present for one instance in the series */,
+    OrthancPluginErrorCode_CreateDicomUseDataUriScheme = 2023    /*!< The payload of the DICOM instance must be specified according to Data URI scheme */,
+    OrthancPluginErrorCode_CreateDicomBadParent = 2024    /*!< Trying to attach a new DICOM instance to an inexistent resource */,
+    OrthancPluginErrorCode_CreateDicomParentIsInstance = 2025    /*!< Trying to attach a new DICOM instance to an instance (must be a series, study or patient) */,
+    OrthancPluginErrorCode_CreateDicomParentEncoding = 2026    /*!< Unable to get the encoding of the parent resource */,
+    OrthancPluginErrorCode_UnknownModality = 2027    /*!< Unknown modality */,
+    OrthancPluginErrorCode_BadJobOrdering = 2028    /*!< Bad ordering of filters in a job */,
+    OrthancPluginErrorCode_JsonToLuaTable = 2029    /*!< Cannot convert the given JSON object to a Lua table */,
+    OrthancPluginErrorCode_CannotCreateLua = 2030    /*!< Cannot create the Lua context */,
+    OrthancPluginErrorCode_CannotExecuteLua = 2031    /*!< Cannot execute a Lua command */,
+    OrthancPluginErrorCode_LuaAlreadyExecuted = 2032    /*!< Arguments cannot be pushed after the Lua function is executed */,
+    OrthancPluginErrorCode_LuaBadOutput = 2033    /*!< The Lua function does not give the expected number of outputs */,
+    OrthancPluginErrorCode_NotLuaPredicate = 2034    /*!< The Lua function is not a predicate (only true/false outputs allowed) */,
+    OrthancPluginErrorCode_LuaReturnsNoString = 2035    /*!< The Lua function does not return a string */,
+    OrthancPluginErrorCode_StorageAreaAlreadyRegistered = 2036    /*!< Another plugin has already registered a custom storage area */,
+    OrthancPluginErrorCode_DatabaseBackendAlreadyRegistered = 2037    /*!< Another plugin has already registered a custom database back-end */,
+    OrthancPluginErrorCode_DatabaseNotInitialized = 2038    /*!< Plugin trying to call the database during its initialization */,
+
+    _OrthancPluginErrorCode_INTERNAL = 0x7fffffff
+  } OrthancPluginErrorCode;
+
+
+  /**
+   * Forward declaration of one of the mandatory functions for Orthanc
+   * plugins.
+   **/
+  ORTHANC_PLUGINS_API const char* OrthancPluginGetName();
+
+
+  /**
+   * The various HTTP methods for a REST call.
+   **/
+  typedef enum
+  {
+    OrthancPluginHttpMethod_Get = 1,    /*!< GET request */
+    OrthancPluginHttpMethod_Post = 2,   /*!< POST request */
+    OrthancPluginHttpMethod_Put = 3,    /*!< PUT request */
+    OrthancPluginHttpMethod_Delete = 4, /*!< DELETE request */
+
+    _OrthancPluginHttpMethod_INTERNAL = 0x7fffffff
+  } OrthancPluginHttpMethod;
+
+
+  /**
+   * @brief The parameters of a REST request.
+   * @ingroup Callbacks
+   **/
+  typedef struct
+  {
+    /**
+     * @brief The HTTP method.
+     **/
+    OrthancPluginHttpMethod method;    
+
+    /**
+     * @brief The number of groups of the regular expression.
+     **/
+    uint32_t                groupsCount;
+
+    /**
+     * @brief The matched values for the groups of the regular expression.
+     **/
+    const char* const*      groups;
+
+    /**
+     * @brief For a GET request, the number of GET parameters.
+     **/
+    uint32_t                getCount;
+
+    /**
+     * @brief For a GET request, the keys of the GET parameters.
+     **/
+    const char* const*      getKeys;
+
+    /**
+     * @brief For a GET request, the values of the GET parameters.
+     **/
+    const char* const*      getValues;
+
+    /**
+     * @brief For a PUT or POST request, the content of the body.
+     **/
+    const char*             body;
+
+    /**
+     * @brief For a PUT or POST request, the number of bytes of the body.
+     **/
+    uint32_t                bodySize;
+
+
+    /* --------------------------------------------------
+       New in version 0.8.1
+       -------------------------------------------------- */
+
+    /**
+     * @brief The number of HTTP headers.
+     **/
+    uint32_t                headersCount;
+
+    /**
+     * @brief The keys of the HTTP headers (always converted to low-case).
+     **/
+    const char* const*      headersKeys;
+
+    /**
+     * @brief The values of the HTTP headers.
+     **/
+    const char* const*      headersValues;
+
+  } OrthancPluginHttpRequest;
+
+
+  typedef enum 
+  {
+    /* Generic services */
+    _OrthancPluginService_LogInfo = 1,
+    _OrthancPluginService_LogWarning = 2,
+    _OrthancPluginService_LogError = 3,
+    _OrthancPluginService_GetOrthancPath = 4,
+    _OrthancPluginService_GetOrthancDirectory = 5,
+    _OrthancPluginService_GetConfigurationPath = 6,
+    _OrthancPluginService_SetPluginProperty = 7,
+    _OrthancPluginService_GetGlobalProperty = 8,
+    _OrthancPluginService_SetGlobalProperty = 9,
+    _OrthancPluginService_GetCommandLineArgumentsCount = 10,
+    _OrthancPluginService_GetCommandLineArgument = 11,
+    _OrthancPluginService_GetExpectedDatabaseVersion = 12,
+    _OrthancPluginService_GetConfiguration = 13,
+    _OrthancPluginService_BufferCompression = 14,
+    _OrthancPluginService_ReadFile = 15,
+    _OrthancPluginService_WriteFile = 16,
+    _OrthancPluginService_GetErrorDescription = 17,
+    _OrthancPluginService_CallHttpClient = 18,
+    _OrthancPluginService_RegisterErrorCode = 19,
+    _OrthancPluginService_RegisterDictionaryTag = 20,
+
+    /* Registration of callbacks */
+    _OrthancPluginService_RegisterRestCallback = 1000,
+    _OrthancPluginService_RegisterOnStoredInstanceCallback = 1001,
+    _OrthancPluginService_RegisterStorageArea = 1002,
+    _OrthancPluginService_RegisterOnChangeCallback = 1003,
+    _OrthancPluginService_RegisterRestCallbackNoLock = 1004,
+
+    /* Sending answers to REST calls */
+    _OrthancPluginService_AnswerBuffer = 2000,
+    _OrthancPluginService_CompressAndAnswerPngImage = 2001,  /* Unused as of Orthanc 0.9.4 */
+    _OrthancPluginService_Redirect = 2002,
+    _OrthancPluginService_SendHttpStatusCode = 2003,
+    _OrthancPluginService_SendUnauthorized = 2004,
+    _OrthancPluginService_SendMethodNotAllowed = 2005,
+    _OrthancPluginService_SetCookie = 2006,
+    _OrthancPluginService_SetHttpHeader = 2007,
+    _OrthancPluginService_StartMultipartAnswer = 2008,
+    _OrthancPluginService_SendMultipartItem = 2009,
+    _OrthancPluginService_SendHttpStatus = 2010,
+    _OrthancPluginService_CompressAndAnswerImage = 2011,
+
+    /* Access to the Orthanc database and API */
+    _OrthancPluginService_GetDicomForInstance = 3000,
+    _OrthancPluginService_RestApiGet = 3001,
+    _OrthancPluginService_RestApiPost = 3002,
+    _OrthancPluginService_RestApiDelete = 3003,
+    _OrthancPluginService_RestApiPut = 3004,
+    _OrthancPluginService_LookupPatient = 3005,
+    _OrthancPluginService_LookupStudy = 3006,
+    _OrthancPluginService_LookupSeries = 3007,
+    _OrthancPluginService_LookupInstance = 3008,
+    _OrthancPluginService_LookupStudyWithAccessionNumber = 3009,
+    _OrthancPluginService_RestApiGetAfterPlugins = 3010,
+    _OrthancPluginService_RestApiPostAfterPlugins = 3011,
+    _OrthancPluginService_RestApiDeleteAfterPlugins = 3012,
+    _OrthancPluginService_RestApiPutAfterPlugins = 3013,
+
+    /* Access to DICOM instances */
+    _OrthancPluginService_GetInstanceRemoteAet = 4000,
+    _OrthancPluginService_GetInstanceSize = 4001,
+    _OrthancPluginService_GetInstanceData = 4002,
+    _OrthancPluginService_GetInstanceJson = 4003,
+    _OrthancPluginService_GetInstanceSimplifiedJson = 4004,
+    _OrthancPluginService_HasInstanceMetadata = 4005,
+    _OrthancPluginService_GetInstanceMetadata = 4006,
+
+    /* Services for plugins implementing a database back-end */
+    _OrthancPluginService_RegisterDatabaseBackend = 5000,
+    _OrthancPluginService_DatabaseAnswer = 5001,
+    _OrthancPluginService_RegisterDatabaseBackendV2 = 5002,
+    _OrthancPluginService_StorageAreaCreate = 5003,
+    _OrthancPluginService_StorageAreaRead = 5004,
+    _OrthancPluginService_StorageAreaRemove = 5005,
+
+    /* Primitives for handling images */
+    _OrthancPluginService_GetImagePixelFormat = 6000,
+    _OrthancPluginService_GetImageWidth = 6001,
+    _OrthancPluginService_GetImageHeight = 6002,
+    _OrthancPluginService_GetImagePitch = 6003,
+    _OrthancPluginService_GetImageBuffer = 6004,
+    _OrthancPluginService_UncompressImage = 6005,
+    _OrthancPluginService_FreeImage = 6006,
+    _OrthancPluginService_CompressImage = 6007,
+    _OrthancPluginService_ConvertPixelFormat = 6008,
+    _OrthancPluginService_GetFontsCount = 6009,
+    _OrthancPluginService_GetFontInfo = 6010,
+    _OrthancPluginService_DrawText = 6011,
+
+    _OrthancPluginService_INTERNAL = 0x7fffffff
+  } _OrthancPluginService;
+
+
+  typedef enum
+  {
+    _OrthancPluginProperty_Description = 1,
+    _OrthancPluginProperty_RootUri = 2,
+    _OrthancPluginProperty_OrthancExplorer = 3,
+
+    _OrthancPluginProperty_INTERNAL = 0x7fffffff
+  } _OrthancPluginProperty;
+
+
+
+  /**
+   * The memory layout of the pixels of an image.
+   * @ingroup Images
+   **/
+  typedef enum
+  {
+    /**
+     * @brief Graylevel 8bpp image.
+     *
+     * The image is graylevel. Each pixel is unsigned and stored in
+     * one byte.
+     **/
+    OrthancPluginPixelFormat_Grayscale8 = 1,
+
+    /**
+     * @brief Graylevel, unsigned 16bpp image.
+     *
+     * The image is graylevel. Each pixel is unsigned and stored in
+     * two bytes.
+     **/
+    OrthancPluginPixelFormat_Grayscale16 = 2,
+
+    /**
+     * @brief Graylevel, signed 16bpp image.
+     *
+     * The image is graylevel. Each pixel is signed and stored in two
+     * bytes.
+     **/
+    OrthancPluginPixelFormat_SignedGrayscale16 = 3,
+
+    /**
+     * @brief Color image in RGB24 format.
+     *
+     * This format describes a color image. The pixels are stored in 3
+     * consecutive bytes. The memory layout is RGB.
+     **/
+    OrthancPluginPixelFormat_RGB24 = 4,
+
+    /**
+     * @brief Color image in RGBA32 format.
+     *
+     * This format describes a color image. The pixels are stored in 4
+     * consecutive bytes. The memory layout is RGBA.
+     **/
+    OrthancPluginPixelFormat_RGBA32 = 5,
+
+    OrthancPluginPixelFormat_Unknown = 6,   /*!< Unknown pixel format */
+
+    _OrthancPluginPixelFormat_INTERNAL = 0x7fffffff
+  } OrthancPluginPixelFormat;
+
+
+
+  /**
+   * The content types that are supported by Orthanc plugins.
+   **/
+  typedef enum
+  {
+    OrthancPluginContentType_Unknown = 0,      /*!< Unknown content type */
+    OrthancPluginContentType_Dicom = 1,        /*!< DICOM */
+    OrthancPluginContentType_DicomAsJson = 2,  /*!< JSON summary of a DICOM file */
+
+    _OrthancPluginContentType_INTERNAL = 0x7fffffff
+  } OrthancPluginContentType;
+
+
+
+  /**
+   * The supported types of DICOM resources.
+   **/
+  typedef enum
+  {
+    OrthancPluginResourceType_Patient = 0,     /*!< Patient */
+    OrthancPluginResourceType_Study = 1,       /*!< Study */
+    OrthancPluginResourceType_Series = 2,      /*!< Series */
+    OrthancPluginResourceType_Instance = 3,    /*!< Instance */
+
+    _OrthancPluginResourceType_INTERNAL = 0x7fffffff
+  } OrthancPluginResourceType;
+
+
+
+  /**
+   * The supported types of changes that can happen to DICOM resources.
+   * @ingroup Callbacks
+   **/
+  typedef enum
+  {
+    OrthancPluginChangeType_CompletedSeries = 0,    /*!< Series is now complete */
+    OrthancPluginChangeType_Deleted = 1,            /*!< Deleted resource */
+    OrthancPluginChangeType_NewChildInstance = 2,   /*!< A new instance was added to this resource */
+    OrthancPluginChangeType_NewInstance = 3,        /*!< New instance received */
+    OrthancPluginChangeType_NewPatient = 4,         /*!< New patient created */
+    OrthancPluginChangeType_NewSeries = 5,          /*!< New series created */
+    OrthancPluginChangeType_NewStudy = 6,           /*!< New study created */
+    OrthancPluginChangeType_StablePatient = 7,      /*!< Timeout: No new instance in this patient */
+    OrthancPluginChangeType_StableSeries = 8,       /*!< Timeout: No new instance in this series */
+    OrthancPluginChangeType_StableStudy = 9,        /*!< Timeout: No new instance in this study */
+
+    _OrthancPluginChangeType_INTERNAL = 0x7fffffff
+  } OrthancPluginChangeType;
+
+
+  /**
+   * The compression algorithms that are supported by the Orthanc core.
+   * @ingroup Images
+   **/
+  typedef enum
+  {
+    OrthancPluginCompressionType_Zlib = 0,          /*!< Standard zlib compression */
+    OrthancPluginCompressionType_ZlibWithSize = 1,  /*!< zlib, prefixed with uncompressed size (uint64_t) */
+    OrthancPluginCompressionType_Gzip = 2,          /*!< Standard gzip compression */
+    OrthancPluginCompressionType_GzipWithSize = 3,  /*!< gzip, prefixed with uncompressed size (uint64_t) */
+
+    _OrthancPluginCompressionType_INTERNAL = 0x7fffffff
+  } OrthancPluginCompressionType;
+
+
+  /**
+   * The image formats that are supported by the Orthanc core.
+   * @ingroup Images
+   **/
+  typedef enum
+  {
+    OrthancPluginImageFormat_Png = 0,   /*!< Image compressed using PNG */
+    OrthancPluginImageFormat_Jpeg = 1,  /*!< Image compressed using JPEG */
+
+    _OrthancPluginImageFormat_INTERNAL = 0x7fffffff
+  } OrthancPluginImageFormat;
+
+
+  /**
+   * The value representations present in the DICOM standard (version 2013).
+   * @ingroup Toolbox
+   **/
+  typedef enum
+  {
+    OrthancPluginValueRepresentation_AE = 1,   /*!< Application Entity */
+    OrthancPluginValueRepresentation_AS = 2,   /*!< Age String */
+    OrthancPluginValueRepresentation_AT = 3,   /*!< Attribute Tag */
+    OrthancPluginValueRepresentation_CS = 4,   /*!< Code String */
+    OrthancPluginValueRepresentation_DA = 5,   /*!< Date */
+    OrthancPluginValueRepresentation_DS = 6,   /*!< Decimal String */
+    OrthancPluginValueRepresentation_DT = 7,   /*!< Date Time */
+    OrthancPluginValueRepresentation_FD = 8,   /*!< Floating Point Double */
+    OrthancPluginValueRepresentation_FL = 9,   /*!< Floating Point Single */
+    OrthancPluginValueRepresentation_IS = 10,  /*!< Integer String */
+    OrthancPluginValueRepresentation_LO = 11,  /*!< Long String */
+    OrthancPluginValueRepresentation_LT = 12,  /*!< Long Text */
+    OrthancPluginValueRepresentation_OB = 13,  /*!< Other Byte String */
+    OrthancPluginValueRepresentation_OF = 14,  /*!< Other Float String */
+    OrthancPluginValueRepresentation_OW = 15,  /*!< Other Word String */
+    OrthancPluginValueRepresentation_PN = 16,  /*!< Person Name */
+    OrthancPluginValueRepresentation_SH = 17,  /*!< Short String */
+    OrthancPluginValueRepresentation_SL = 18,  /*!< Signed Long */
+    OrthancPluginValueRepresentation_SQ = 19,  /*!< Sequence of Items */
+    OrthancPluginValueRepresentation_SS = 20,  /*!< Signed Short */
+    OrthancPluginValueRepresentation_ST = 21,  /*!< Short Text */
+    OrthancPluginValueRepresentation_TM = 22,  /*!< Time */
+    OrthancPluginValueRepresentation_UI = 23,  /*!< Unique Identifier (UID) */
+    OrthancPluginValueRepresentation_UL = 24,  /*!< Unsigned Long */
+    OrthancPluginValueRepresentation_UN = 25,  /*!< Unknown */
+    OrthancPluginValueRepresentation_US = 26,  /*!< Unsigned Short */
+    OrthancPluginValueRepresentation_UT = 27,  /*!< Unlimited Text */
+
+    _OrthancPluginValueRepresentation_INTERNAL = 0x7fffffff
+  } OrthancPluginValueRepresentation;
+
+
+  /**
+   * @brief A memory buffer allocated by the core system of Orthanc.
+   *
+   * A memory buffer allocated by the core system of Orthanc. When the
+   * content of the buffer is not useful anymore, it must be free by a
+   * call to ::OrthancPluginFreeMemoryBuffer().
+   **/
+  typedef struct
+  {
+    /**
+     * @brief The content of the buffer.
+     **/
+    void*      data;
+
+    /**
+     * @brief The number of bytes in the buffer.
+     **/
+    uint32_t   size;
+  } OrthancPluginMemoryBuffer;
+
+
+
+
+  /**
+   * @brief Opaque structure that represents the HTTP connection to the client application.
+   * @ingroup Callback
+   **/
+  typedef struct _OrthancPluginRestOutput_t OrthancPluginRestOutput;
+
+
+
+  /**
+   * @brief Opaque structure that represents a DICOM instance received by Orthanc.
+   **/
+  typedef struct _OrthancPluginDicomInstance_t OrthancPluginDicomInstance;
+
+
+
+  /**
+   * @brief Opaque structure that represents an image that is uncompressed in memory.
+   * @ingroup Images
+   **/
+  typedef struct _OrthancPluginImage_t OrthancPluginImage;
+
+
+
+  /**
+   * @brief Opaque structure that represents the storage area that is actually used by Orthanc.
+   * @ingroup Images
+   **/
+  typedef struct _OrthancPluginStorageArea_t OrthancPluginStorageArea;
+
+
+
+  /**
+   * @brief Signature of a callback function that answers to a REST request.
+   * @ingroup Callbacks
+   **/
+  typedef OrthancPluginErrorCode (*OrthancPluginRestCallback) (
+    OrthancPluginRestOutput* output,
+    const char* url,
+    const OrthancPluginHttpRequest* request);
+
+
+
+  /**
+   * @brief Signature of a callback function that is triggered when Orthanc receives a DICOM instance.
+   * @ingroup Callbacks
+   **/
+  typedef OrthancPluginErrorCode (*OrthancPluginOnStoredInstanceCallback) (
+    OrthancPluginDicomInstance* instance,
+    const char* instanceId);
+
+
+
+  /**
+   * @brief Signature of a callback function that is triggered when a change happens to some DICOM resource.
+   * @ingroup Callbacks
+   **/
+  typedef OrthancPluginErrorCode (*OrthancPluginOnChangeCallback) (
+    OrthancPluginChangeType changeType,
+    OrthancPluginResourceType resourceType,
+    const char* resourceId);
+
+
+
+  /**
+   * @brief Signature of a function to free dynamic memory.
+   **/
+  typedef void (*OrthancPluginFree) (void* buffer);
+
+
+
+  /**
+   * @brief Callback for writing to the storage area.
+   *
+   * Signature of a callback function that is triggered when Orthanc writes a file to the storage area.
+   *
+   * @param uuid The UUID of the file.
+   * @param content The content of the file.
+   * @param size The size of the file.
+   * @param type The content type corresponding to this file. 
+   * @return 0 if success, other value if error.
+   * @ingroup Callbacks
+   **/
+  typedef OrthancPluginErrorCode (*OrthancPluginStorageCreate) (
+    const char* uuid,
+    const void* content,
+    int64_t size,
+    OrthancPluginContentType type);
+
+
+
+  /**
+   * @brief Callback for reading from the storage area.
+   *
+   * Signature of a callback function that is triggered when Orthanc reads a file from the storage area.
+   *
+   * @param content The content of the file (output).
+   * @param size The size of the file (output).
+   * @param uuid The UUID of the file of interest.
+   * @param type The content type corresponding to this file. 
+   * @return 0 if success, other value if error.
+   * @ingroup Callbacks
+   **/
+  typedef OrthancPluginErrorCode (*OrthancPluginStorageRead) (
+    void** content,
+    int64_t* size,
+    const char* uuid,
+    OrthancPluginContentType type);
+
+
+
+  /**
+   * @brief Callback for removing a file from the storage area.
+   *
+   * Signature of a callback function that is triggered when Orthanc deletes a file from the storage area.
+   *
+   * @param uuid The UUID of the file to be removed.
+   * @param type The content type corresponding to this file. 
+   * @return 0 if success, other value if error.
+   * @ingroup Callbacks
+   **/
+  typedef OrthancPluginErrorCode (*OrthancPluginStorageRemove) (
+    const char* uuid,
+    OrthancPluginContentType type);
+
+
+
+  /**
+   * @brief Data structure that contains information about the Orthanc core.
+   **/
+  typedef struct _OrthancPluginContext_t
+  {
+    void*                     pluginsManager;
+    const char*               orthancVersion;
+    OrthancPluginFree         Free;
+    OrthancPluginErrorCode  (*InvokeService) (struct _OrthancPluginContext_t* context,
+                                              _OrthancPluginService service,
+                                              const void* params);
+  } OrthancPluginContext;
+
+
+
+  /**
+   * @brief Free a string.
+   * 
+   * Free a string that was allocated by the core system of Orthanc.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param str The string to be freed.
+   **/
+  ORTHANC_PLUGIN_INLINE void  OrthancPluginFreeString(
+    OrthancPluginContext* context, 
+    char* str)
+  {
+    if (str != NULL)
+    {
+      context->Free(str);
+    }
+  }
+
+
+  /**
+   * @brief Check the compatibility of the plugin wrt. the version of its hosting Orthanc.
+   * 
+   * This function checks whether the version of this C header is
+   * compatible with the current version of Orthanc. The result of
+   * this function should always be checked in the
+   * OrthancPluginInitialize() entry point of the plugin.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @return 1 if and only if the versions are compatible. If the
+   * result is 0, the initialization of the plugin should fail.
+   * @ingroup Callbacks
+   **/
+  ORTHANC_PLUGIN_INLINE int  OrthancPluginCheckVersion(
+    OrthancPluginContext* context)
+  {
+    int major, minor, revision;
+
+    if (sizeof(int32_t) != sizeof(OrthancPluginErrorCode) ||
+        sizeof(int32_t) != sizeof(OrthancPluginHttpMethod) ||
+        sizeof(int32_t) != sizeof(_OrthancPluginService) ||
+        sizeof(int32_t) != sizeof(_OrthancPluginProperty) ||
+        sizeof(int32_t) != sizeof(OrthancPluginPixelFormat) ||
+        sizeof(int32_t) != sizeof(OrthancPluginContentType) ||
+        sizeof(int32_t) != sizeof(OrthancPluginResourceType) ||
+        sizeof(int32_t) != sizeof(OrthancPluginChangeType) ||
+        sizeof(int32_t) != sizeof(OrthancPluginCompressionType) ||
+        sizeof(int32_t) != sizeof(OrthancPluginImageFormat) ||
+        sizeof(int32_t) != sizeof(OrthancPluginValueRepresentation))
+    {
+      /* Mismatch in the size of the enumerations */
+      return 0;
+    }
+
+    /* Assume compatibility with the mainline */
+    if (!strcmp(context->orthancVersion, "mainline"))
+    {
+      return 1;
+    }
+
+    /* Parse the version of the Orthanc core */
+    if ( 
+#ifdef _MSC_VER
+      sscanf_s
+#else
+      sscanf
+#endif
+      (context->orthancVersion, "%4d.%4d.%4d", &major, &minor, &revision) != 3)
+    {
+      return 0;
+    }
+
+    /* Check the major number of the version */
+
+    if (major > ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER)
+    {
+      return 1;
+    }
+
+    if (major < ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER)
+    {
+      return 0;
+    }
+
+    /* Check the minor number of the version */
+
+    if (minor > ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER)
+    {
+      return 1;
+    }
+
+    if (minor < ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER)
+    {
+      return 0;
+    }
+
+    /* Check the revision number of the version */
+
+    if (revision >= ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER)
+    {
+      return 1;
+    }
+    else
+    {
+      return 0;
+    }
+  }
+
+
+  /**
+   * @brief Free a memory buffer.
+   * 
+   * Free a memory buffer that was allocated by the core system of Orthanc.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param buffer The memory buffer to release.
+   **/
+  ORTHANC_PLUGIN_INLINE void  OrthancPluginFreeMemoryBuffer(
+    OrthancPluginContext* context, 
+    OrthancPluginMemoryBuffer* buffer)
+  {
+    context->Free(buffer->data);
+  }
+
+
+  /**
+   * @brief Log an error.
+   *
+   * Log an error message using the Orthanc logging system.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param message The message to be logged.
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginLogError(
+    OrthancPluginContext* context,
+    const char* message)
+  {
+    context->InvokeService(context, _OrthancPluginService_LogError, message);
+  }
+
+
+  /**
+   * @brief Log a warning.
+   *
+   * Log a warning message using the Orthanc logging system.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param message The message to be logged.
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginLogWarning(
+    OrthancPluginContext* context,
+    const char* message)
+  {
+    context->InvokeService(context, _OrthancPluginService_LogWarning, message);
+  }
+
+
+  /**
+   * @brief Log an information.
+   *
+   * Log an information message using the Orthanc logging system.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param message The message to be logged.
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginLogInfo(
+    OrthancPluginContext* context,
+    const char* message)
+  {
+    context->InvokeService(context, _OrthancPluginService_LogInfo, message);
+  }
+
+
+
+  typedef struct
+  {
+    const char* pathRegularExpression;
+    OrthancPluginRestCallback callback;
+  } _OrthancPluginRestCallback;
+
+  /**
+   * @brief Register a REST callback.
+   *
+   * This function registers a REST callback against a regular
+   * expression for a URI. This function must be called during the
+   * initialization of the plugin, i.e. inside the
+   * OrthancPluginInitialize() public function.
+   *
+   * Each REST callback is guaranteed to run in mutual exclusion.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param pathRegularExpression Regular expression for the URI. May contain groups.
+   * @param callback The callback function to handle the REST call.
+   * @see OrthancPluginRegisterRestCallbackNoLock()
+   * @ingroup Callbacks
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterRestCallback(
+    OrthancPluginContext*     context,
+    const char*               pathRegularExpression,
+    OrthancPluginRestCallback callback)
+  {
+    _OrthancPluginRestCallback params;
+    params.pathRegularExpression = pathRegularExpression;
+    params.callback = callback;
+    context->InvokeService(context, _OrthancPluginService_RegisterRestCallback, &params);
+  }
+
+
+
+  /**
+   * @brief Register a REST callback, without locking.
+   *
+   * This function registers a REST callback against a regular
+   * expression for a URI. This function must be called during the
+   * initialization of the plugin, i.e. inside the
+   * OrthancPluginInitialize() public function.
+   *
+   * Contrarily to OrthancPluginRegisterRestCallback(), the callback
+   * will NOT be invoked in mutual exclusion. This can be useful for
+   * high-performance plugins that must handle concurrent requests
+   * (Orthanc uses a pool of threads, one thread being assigned to
+   * each incoming HTTP request). Of course, it is up to the plugin to
+   * implement the required locking mechanisms.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param pathRegularExpression Regular expression for the URI. May contain groups.
+   * @param callback The callback function to handle the REST call.
+   * @see OrthancPluginRegisterRestCallback()
+   * @ingroup Callbacks
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterRestCallbackNoLock(
+    OrthancPluginContext*     context,
+    const char*               pathRegularExpression,
+    OrthancPluginRestCallback callback)
+  {
+    _OrthancPluginRestCallback params;
+    params.pathRegularExpression = pathRegularExpression;
+    params.callback = callback;
+    context->InvokeService(context, _OrthancPluginService_RegisterRestCallbackNoLock, &params);
+  }
+
+
+
+  typedef struct
+  {
+    OrthancPluginOnStoredInstanceCallback callback;
+  } _OrthancPluginOnStoredInstanceCallback;
+
+  /**
+   * @brief Register a callback for received instances.
+   *
+   * This function registers a callback function that is called
+   * whenever a new DICOM instance is stored into the Orthanc core.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param callback The callback function.
+   * @ingroup Callbacks
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterOnStoredInstanceCallback(
+    OrthancPluginContext*                  context,
+    OrthancPluginOnStoredInstanceCallback  callback)
+  {
+    _OrthancPluginOnStoredInstanceCallback params;
+    params.callback = callback;
+
+    context->InvokeService(context, _OrthancPluginService_RegisterOnStoredInstanceCallback, &params);
+  }
+
+
+
+  typedef struct
+  {
+    OrthancPluginRestOutput* output;
+    const char*              answer;
+    uint32_t                 answerSize;
+    const char*              mimeType;
+  } _OrthancPluginAnswerBuffer;
+
+  /**
+   * @brief Answer to a REST request.
+   *
+   * This function answers to a REST request with the content of a memory buffer.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param output The HTTP connection to the client application.
+   * @param answer Pointer to the memory buffer containing the answer.
+   * @param answerSize Number of bytes of the answer.
+   * @param mimeType The MIME type of the answer.
+   * @ingroup REST
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginAnswerBuffer(
+    OrthancPluginContext*    context,
+    OrthancPluginRestOutput* output,
+    const char*              answer,
+    uint32_t                 answerSize,
+    const char*              mimeType)
+  {
+    _OrthancPluginAnswerBuffer params;
+    params.output = output;
+    params.answer = answer;
+    params.answerSize = answerSize;
+    params.mimeType = mimeType;
+    context->InvokeService(context, _OrthancPluginService_AnswerBuffer, &params);
+  }
+
+
+  typedef struct
+  {
+    OrthancPluginRestOutput*  output;
+    OrthancPluginPixelFormat  format;
+    uint32_t                  width;
+    uint32_t                  height;
+    uint32_t                  pitch;
+    const void*               buffer;
+  } _OrthancPluginCompressAndAnswerPngImage;
+
+  typedef struct
+  {
+    OrthancPluginRestOutput*  output;
+    OrthancPluginImageFormat  imageFormat;
+    OrthancPluginPixelFormat  pixelFormat;
+    uint32_t                  width;
+    uint32_t                  height;
+    uint32_t                  pitch;
+    const void*               buffer;
+    uint8_t                   quality;
+  } _OrthancPluginCompressAndAnswerImage;
+
+
+  /**
+   * @brief Answer to a REST request with a PNG image.
+   *
+   * This function answers to a REST request with a PNG image. The
+   * parameters of this function describe a memory buffer that
+   * contains an uncompressed image. The image will be automatically compressed
+   * as a PNG image by the core system of Orthanc.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param output The HTTP connection to the client application.
+   * @param format The memory layout of the uncompressed image.
+   * @param width The width of the image.
+   * @param height The height of the image.
+   * @param pitch The pitch of the image (i.e. the number of bytes
+   * between 2 successive lines of the image in the memory buffer).
+   * @param buffer The memory buffer containing the uncompressed image.
+   * @ingroup REST
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginCompressAndAnswerPngImage(
+    OrthancPluginContext*     context,
+    OrthancPluginRestOutput*  output,
+    OrthancPluginPixelFormat  format,
+    uint32_t                  width,
+    uint32_t                  height,
+    uint32_t                  pitch,
+    const void*               buffer)
+  {
+    _OrthancPluginCompressAndAnswerImage params;
+    params.output = output;
+    params.imageFormat = OrthancPluginImageFormat_Png;
+    params.pixelFormat = format;
+    params.width = width;
+    params.height = height;
+    params.pitch = pitch;
+    params.buffer = buffer;
+    params.quality = 0;  /* No quality for PNG */
+    context->InvokeService(context, _OrthancPluginService_CompressAndAnswerImage, &params);
+  }
+
+
+
+  typedef struct
+  {
+    OrthancPluginMemoryBuffer*  target;
+    const char*                 instanceId;
+  } _OrthancPluginGetDicomForInstance;
+
+  /**
+   * @brief Retrieve a DICOM instance using its Orthanc identifier.
+   * 
+   * Retrieve a DICOM instance using its Orthanc identifier. The DICOM
+   * file is stored into a newly allocated memory buffer.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param target The target memory buffer.
+   * @param instanceId The Orthanc identifier of the DICOM instance of interest.
+   * @return 0 if success, or the error code if failure.
+   * @ingroup Orthanc
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginGetDicomForInstance(
+    OrthancPluginContext*       context,
+    OrthancPluginMemoryBuffer*  target,
+    const char*                 instanceId)
+  {
+    _OrthancPluginGetDicomForInstance params;
+    params.target = target;
+    params.instanceId = instanceId;
+    return context->InvokeService(context, _OrthancPluginService_GetDicomForInstance, &params);
+  }
+
+
+
+  typedef struct
+  {
+    OrthancPluginMemoryBuffer*  target;
+    const char*                 uri;
+  } _OrthancPluginRestApiGet;
+
+  /**
+   * @brief Make a GET call to the built-in Orthanc REST API.
+   * 
+   * Make a GET call to the built-in Orthanc REST API. The result to
+   * the query is stored into a newly allocated memory buffer.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param target The target memory buffer.
+   * @param uri The URI in the built-in Orthanc API.
+   * @return 0 if success, or the error code if failure.
+   * @see OrthancPluginRestApiGetAfterPlugins
+   * @ingroup Orthanc
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginRestApiGet(
+    OrthancPluginContext*       context,
+    OrthancPluginMemoryBuffer*  target,
+    const char*                 uri)
+  {
+    _OrthancPluginRestApiGet params;
+    params.target = target;
+    params.uri = uri;
+    return context->InvokeService(context, _OrthancPluginService_RestApiGet, &params);
+  }
+
+
+
+  /**
+   * @brief Make a GET call to the REST API, as tainted by the plugins.
+   * 
+   * Make a GET call to the Orthanc REST API, after all the plugins
+   * are applied. In other words, if some plugin overrides or adds the
+   * called URI to the built-in Orthanc REST API, this call will
+   * return the result provided by this plugin. The result to the
+   * query is stored into a newly allocated memory buffer.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param target The target memory buffer.
+   * @param uri The URI in the built-in Orthanc API.
+   * @return 0 if success, or the error code if failure.
+   * @see OrthancPluginRestApiGet
+   * @ingroup Orthanc
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginRestApiGetAfterPlugins(
+    OrthancPluginContext*       context,
+    OrthancPluginMemoryBuffer*  target,
+    const char*                 uri)
+  {
+    _OrthancPluginRestApiGet params;
+    params.target = target;
+    params.uri = uri;
+    return context->InvokeService(context, _OrthancPluginService_RestApiGetAfterPlugins, &params);
+  }
+
+
+
+  typedef struct
+  {
+    OrthancPluginMemoryBuffer*  target;
+    const char*                 uri;
+    const char*                 body;
+    uint32_t                    bodySize;
+  } _OrthancPluginRestApiPostPut;
+
+  /**
+   * @brief Make a POST call to the built-in Orthanc REST API.
+   * 
+   * Make a POST call to the built-in Orthanc REST API. The result to
+   * the query is stored into a newly allocated memory buffer.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param target The target memory buffer.
+   * @param uri The URI in the built-in Orthanc API.
+   * @param body The body of the POST request.
+   * @param bodySize The size of the body.
+   * @return 0 if success, or the error code if failure.
+   * @see OrthancPluginRestApiPostAfterPlugins
+   * @ingroup Orthanc
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginRestApiPost(
+    OrthancPluginContext*       context,
+    OrthancPluginMemoryBuffer*  target,
+    const char*                 uri,
+    const char*                 body,
+    uint32_t                    bodySize)
+  {
+    _OrthancPluginRestApiPostPut params;
+    params.target = target;
+    params.uri = uri;
+    params.body = body;
+    params.bodySize = bodySize;
+    return context->InvokeService(context, _OrthancPluginService_RestApiPost, &params);
+  }
+
+
+  /**
+   * @brief Make a POST call to the REST API, as tainted by the plugins.
+   * 
+   * Make a POST call to the Orthanc REST API, after all the plugins
+   * are applied. In other words, if some plugin overrides or adds the
+   * called URI to the built-in Orthanc REST API, this call will
+   * return the result provided by this plugin. The result to the
+   * query is stored into a newly allocated memory buffer.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param target The target memory buffer.
+   * @param uri The URI in the built-in Orthanc API.
+   * @param body The body of the POST request.
+   * @param bodySize The size of the body.
+   * @return 0 if success, or the error code if failure.
+   * @see OrthancPluginRestApiPost
+   * @ingroup Orthanc
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginRestApiPostAfterPlugins(
+    OrthancPluginContext*       context,
+    OrthancPluginMemoryBuffer*  target,
+    const char*                 uri,
+    const char*                 body,
+    uint32_t                    bodySize)
+  {
+    _OrthancPluginRestApiPostPut params;
+    params.target = target;
+    params.uri = uri;
+    params.body = body;
+    params.bodySize = bodySize;
+    return context->InvokeService(context, _OrthancPluginService_RestApiPostAfterPlugins, &params);
+  }
+
+
+
+  /**
+   * @brief Make a DELETE call to the built-in Orthanc REST API.
+   * 
+   * Make a DELETE call to the built-in Orthanc REST API.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param uri The URI to delete in the built-in Orthanc API.
+   * @return 0 if success, or the error code if failure.
+   * @see OrthancPluginRestApiDeleteAfterPlugins
+   * @ingroup Orthanc
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginRestApiDelete(
+    OrthancPluginContext*       context,
+    const char*                 uri)
+  {
+    return context->InvokeService(context, _OrthancPluginService_RestApiDelete, uri);
+  }
+
+
+  /**
+   * @brief Make a DELETE call to the REST API, as tainted by the plugins.
+   * 
+   * Make a DELETE call to the Orthanc REST API, after all the plugins
+   * are applied. In other words, if some plugin overrides or adds the
+   * called URI to the built-in Orthanc REST API, this call will
+   * return the result provided by this plugin. 
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param uri The URI to delete in the built-in Orthanc API.
+   * @return 0 if success, or the error code if failure.
+   * @see OrthancPluginRestApiDelete
+   * @ingroup Orthanc
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginRestApiDeleteAfterPlugins(
+    OrthancPluginContext*       context,
+    const char*                 uri)
+  {
+    return context->InvokeService(context, _OrthancPluginService_RestApiDeleteAfterPlugins, uri);
+  }
+
+
+
+  /**
+   * @brief Make a PUT call to the built-in Orthanc REST API.
+   * 
+   * Make a PUT call to the built-in Orthanc REST API. The result to
+   * the query is stored into a newly allocated memory buffer.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param target The target memory buffer.
+   * @param uri The URI in the built-in Orthanc API.
+   * @param body The body of the PUT request.
+   * @param bodySize The size of the body.
+   * @return 0 if success, or the error code if failure.
+   * @see OrthancPluginRestApiPutAfterPlugins
+   * @ingroup Orthanc
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginRestApiPut(
+    OrthancPluginContext*       context,
+    OrthancPluginMemoryBuffer*  target,
+    const char*                 uri,
+    const char*                 body,
+    uint32_t                    bodySize)
+  {
+    _OrthancPluginRestApiPostPut params;
+    params.target = target;
+    params.uri = uri;
+    params.body = body;
+    params.bodySize = bodySize;
+    return context->InvokeService(context, _OrthancPluginService_RestApiPut, &params);
+  }
+
+
+
+  /**
+   * @brief Make a PUT call to the REST API, as tainted by the plugins.
+   * 
+   * Make a PUT call to the Orthanc REST API, after all the plugins
+   * are applied. In other words, if some plugin overrides or adds the
+   * called URI to the built-in Orthanc REST API, this call will
+   * return the result provided by this plugin. The result to the
+   * query is stored into a newly allocated memory buffer.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param target The target memory buffer.
+   * @param uri The URI in the built-in Orthanc API.
+   * @param body The body of the PUT request.
+   * @param bodySize The size of the body.
+   * @return 0 if success, or the error code if failure.
+   * @see OrthancPluginRestApiPut
+   * @ingroup Orthanc
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginRestApiPutAfterPlugins(
+    OrthancPluginContext*       context,
+    OrthancPluginMemoryBuffer*  target,
+    const char*                 uri,
+    const char*                 body,
+    uint32_t                    bodySize)
+  {
+    _OrthancPluginRestApiPostPut params;
+    params.target = target;
+    params.uri = uri;
+    params.body = body;
+    params.bodySize = bodySize;
+    return context->InvokeService(context, _OrthancPluginService_RestApiPutAfterPlugins, &params);
+  }
+
+
+
+  typedef struct
+  {
+    OrthancPluginRestOutput* output;
+    const char*              argument;
+  } _OrthancPluginOutputPlusArgument;
+
+  /**
+   * @brief Redirect a REST request.
+   *
+   * This function answers to a REST request by redirecting the user
+   * to another URI using HTTP status 301.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param output The HTTP connection to the client application.
+   * @param redirection Where to redirect.
+   * @ingroup REST
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginRedirect(
+    OrthancPluginContext*    context,
+    OrthancPluginRestOutput* output,
+    const char*              redirection)
+  {
+    _OrthancPluginOutputPlusArgument params;
+    params.output = output;
+    params.argument = redirection;
+    context->InvokeService(context, _OrthancPluginService_Redirect, &params);
+  }
+
+
+
+  typedef struct
+  {
+    char**       result;
+    const char*  argument;
+  } _OrthancPluginRetrieveDynamicString;
+
+  /**
+   * @brief Look for a patient.
+   *
+   * Look for a patient stored in Orthanc, using its Patient ID tag (0x0010, 0x0020).
+   * This function uses the database index to run as fast as possible (it does not loop
+   * over all the stored patients).
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param patientID The Patient ID of interest.
+   * @return The NULL value if the patient is non-existent, or a string containing the 
+   * Orthanc ID of the patient. This string must be freed by OrthancPluginFreeString().
+   * @ingroup Orthanc
+   **/
+  ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupPatient(
+    OrthancPluginContext*  context,
+    const char*            patientID)
+  {
+    char* result;
+
+    _OrthancPluginRetrieveDynamicString params;
+    params.result = &result;
+    params.argument = patientID;
+
+    if (context->InvokeService(context, _OrthancPluginService_LookupPatient, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+  /**
+   * @brief Look for a study.
+   *
+   * Look for a study stored in Orthanc, using its Study Instance UID tag (0x0020, 0x000d).
+   * This function uses the database index to run as fast as possible (it does not loop
+   * over all the stored studies).
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param studyUID The Study Instance UID of interest.
+   * @return The NULL value if the study is non-existent, or a string containing the 
+   * Orthanc ID of the study. This string must be freed by OrthancPluginFreeString().
+   * @ingroup Orthanc
+   **/
+  ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupStudy(
+    OrthancPluginContext*  context,
+    const char*            studyUID)
+  {
+    char* result;
+
+    _OrthancPluginRetrieveDynamicString params;
+    params.result = &result;
+    params.argument = studyUID;
+
+    if (context->InvokeService(context, _OrthancPluginService_LookupStudy, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+  /**
+   * @brief Look for a study, using the accession number.
+   *
+   * Look for a study stored in Orthanc, using its Accession Number tag (0x0008, 0x0050).
+   * This function uses the database index to run as fast as possible (it does not loop
+   * over all the stored studies).
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param accessionNumber The Accession Number of interest.
+   * @return The NULL value if the study is non-existent, or a string containing the 
+   * Orthanc ID of the study. This string must be freed by OrthancPluginFreeString().
+   * @ingroup Orthanc
+   **/
+  ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupStudyWithAccessionNumber(
+    OrthancPluginContext*  context,
+    const char*            accessionNumber)
+  {
+    char* result;
+
+    _OrthancPluginRetrieveDynamicString params;
+    params.result = &result;
+    params.argument = accessionNumber;
+
+    if (context->InvokeService(context, _OrthancPluginService_LookupStudyWithAccessionNumber, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+  /**
+   * @brief Look for a series.
+   *
+   * Look for a series stored in Orthanc, using its Series Instance UID tag (0x0020, 0x000e).
+   * This function uses the database index to run as fast as possible (it does not loop
+   * over all the stored series).
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param seriesUID The Series Instance UID of interest.
+   * @return The NULL value if the series is non-existent, or a string containing the 
+   * Orthanc ID of the series. This string must be freed by OrthancPluginFreeString().
+   * @ingroup Orthanc
+   **/
+  ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupSeries(
+    OrthancPluginContext*  context,
+    const char*            seriesUID)
+  {
+    char* result;
+
+    _OrthancPluginRetrieveDynamicString params;
+    params.result = &result;
+    params.argument = seriesUID;
+
+    if (context->InvokeService(context, _OrthancPluginService_LookupSeries, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+  /**
+   * @brief Look for an instance.
+   *
+   * Look for an instance stored in Orthanc, using its SOP Instance UID tag (0x0008, 0x0018).
+   * This function uses the database index to run as fast as possible (it does not loop
+   * over all the stored instances).
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param sopInstanceUID The SOP Instance UID of interest.
+   * @return The NULL value if the instance is non-existent, or a string containing the 
+   * Orthanc ID of the instance. This string must be freed by OrthancPluginFreeString().
+   * @ingroup Orthanc
+   **/
+  ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupInstance(
+    OrthancPluginContext*  context,
+    const char*            sopInstanceUID)
+  {
+    char* result;
+
+    _OrthancPluginRetrieveDynamicString params;
+    params.result = &result;
+    params.argument = sopInstanceUID;
+
+    if (context->InvokeService(context, _OrthancPluginService_LookupInstance, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+
+  typedef struct
+  {
+    OrthancPluginRestOutput* output;
+    uint16_t                 status;
+  } _OrthancPluginSendHttpStatusCode;
+
+  /**
+   * @brief Send a HTTP status code.
+   *
+   * This function answers to a REST request by sending a HTTP status
+   * code (such as "400 - Bad Request"). Note that:
+   * - Successful requests (status 200) must use ::OrthancPluginAnswerBuffer().
+   * - Redirections (status 301) must use ::OrthancPluginRedirect().
+   * - Unauthorized access (status 401) must use ::OrthancPluginSendUnauthorized().
+   * - Methods not allowed (status 405) must use ::OrthancPluginSendMethodNotAllowed().
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param output The HTTP connection to the client application.
+   * @param status The HTTP status code to be sent.
+   * @ingroup REST
+   * @see OrthancPluginSendHttpStatus()
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginSendHttpStatusCode(
+    OrthancPluginContext*    context,
+    OrthancPluginRestOutput* output,
+    uint16_t                 status)
+  {
+    _OrthancPluginSendHttpStatusCode params;
+    params.output = output;
+    params.status = status;
+    context->InvokeService(context, _OrthancPluginService_SendHttpStatusCode, &params);
+  }
+
+
+  /**
+   * @brief Signal that a REST request is not authorized.
+   *
+   * This function answers to a REST request by signaling that it is
+   * not authorized.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param output The HTTP connection to the client application.
+   * @param realm The realm for the authorization process.
+   * @ingroup REST
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginSendUnauthorized(
+    OrthancPluginContext*    context,
+    OrthancPluginRestOutput* output,
+    const char*              realm)
+  {
+    _OrthancPluginOutputPlusArgument params;
+    params.output = output;
+    params.argument = realm;
+    context->InvokeService(context, _OrthancPluginService_SendUnauthorized, &params);
+  }
+
+
+  /**
+   * @brief Signal that this URI does not support this HTTP method.
+   *
+   * This function answers to a REST request by signaling that the
+   * queried URI does not support this method.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param output The HTTP connection to the client application.
+   * @param allowedMethods The allowed methods for this URI (e.g. "GET,POST" after a PUT or a POST request).
+   * @ingroup REST
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginSendMethodNotAllowed(
+    OrthancPluginContext*    context,
+    OrthancPluginRestOutput* output,
+    const char*              allowedMethods)
+  {
+    _OrthancPluginOutputPlusArgument params;
+    params.output = output;
+    params.argument = allowedMethods;
+    context->InvokeService(context, _OrthancPluginService_SendMethodNotAllowed, &params);
+  }
+
+
+  typedef struct
+  {
+    OrthancPluginRestOutput* output;
+    const char*              key;
+    const char*              value;
+  } _OrthancPluginSetHttpHeader;
+
+  /**
+   * @brief Set a cookie.
+   *
+   * This function sets a cookie in the HTTP client.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param output The HTTP connection to the client application.
+   * @param cookie The cookie to be set.
+   * @param value The value of the cookie.
+   * @ingroup REST
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginSetCookie(
+    OrthancPluginContext*    context,
+    OrthancPluginRestOutput* output,
+    const char*              cookie,
+    const char*              value)
+  {
+    _OrthancPluginSetHttpHeader params;
+    params.output = output;
+    params.key = cookie;
+    params.value = value;
+    context->InvokeService(context, _OrthancPluginService_SetCookie, &params);
+  }
+
+
+  /**
+   * @brief Set some HTTP header.
+   *
+   * This function sets a HTTP header in the HTTP answer.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param output The HTTP connection to the client application.
+   * @param key The HTTP header to be set.
+   * @param value The value of the HTTP header.
+   * @ingroup REST
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginSetHttpHeader(
+    OrthancPluginContext*    context,
+    OrthancPluginRestOutput* output,
+    const char*              key,
+    const char*              value)
+  {
+    _OrthancPluginSetHttpHeader params;
+    params.output = output;
+    params.key = key;
+    params.value = value;
+    context->InvokeService(context, _OrthancPluginService_SetHttpHeader, &params);
+  }
+
+
+  typedef struct
+  {
+    char**                      resultStringToFree;
+    const char**                resultString;
+    int64_t*                    resultInt64;
+    const char*                 key;
+    OrthancPluginDicomInstance* instance;
+  } _OrthancPluginAccessDicomInstance;
+
+
+  /**
+   * @brief Get the AET of a DICOM instance.
+   *
+   * This function returns the Application Entity Title (AET) of the
+   * DICOM modality from which a DICOM instance originates.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param instance The instance of interest.
+   * @return The AET if success, NULL if error.
+   * @ingroup Callbacks
+   **/
+  ORTHANC_PLUGIN_INLINE const char* OrthancPluginGetInstanceRemoteAet(
+    OrthancPluginContext*        context,
+    OrthancPluginDicomInstance*  instance)
+  {
+    const char* result;
+
+    _OrthancPluginAccessDicomInstance params;
+    memset(&params, 0, sizeof(params));
+    params.resultString = &result;
+    params.instance = instance;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetInstanceRemoteAet, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+  /**
+   * @brief Get the size of a DICOM file.
+   *
+   * This function returns the number of bytes of the given DICOM instance.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param instance The instance of interest.
+   * @return The size of the file, -1 in case of error.
+   * @ingroup Callbacks
+   **/
+  ORTHANC_PLUGIN_INLINE int64_t OrthancPluginGetInstanceSize(
+    OrthancPluginContext*       context,
+    OrthancPluginDicomInstance* instance)
+  {
+    int64_t size;
+
+    _OrthancPluginAccessDicomInstance params;
+    memset(&params, 0, sizeof(params));
+    params.resultInt64 = &size;
+    params.instance = instance;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetInstanceSize, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return -1;
+    }
+    else
+    {
+      return size;
+    }
+  }
+
+
+  /**
+   * @brief Get the data of a DICOM file.
+   *
+   * This function returns a pointer to the content of the given DICOM instance.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param instance The instance of interest.
+   * @return The pointer to the DICOM data, NULL in case of error.
+   * @ingroup Callbacks
+   **/
+  ORTHANC_PLUGIN_INLINE const char* OrthancPluginGetInstanceData(
+    OrthancPluginContext*        context,
+    OrthancPluginDicomInstance*  instance)
+  {
+    const char* result;
+
+    _OrthancPluginAccessDicomInstance params;
+    memset(&params, 0, sizeof(params));
+    params.resultString = &result;
+    params.instance = instance;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetInstanceData, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+  /**
+   * @brief Get the DICOM tag hierarchy as a JSON file.
+   *
+   * This function returns a pointer to a newly created string
+   * containing a JSON file. This JSON file encodes the tag hierarchy
+   * of the given DICOM instance.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param instance The instance of interest.
+   * @return The NULL value in case of error, or a string containing the JSON file.
+   * This string must be freed by OrthancPluginFreeString().
+   * @ingroup Callbacks
+   **/
+  ORTHANC_PLUGIN_INLINE char* OrthancPluginGetInstanceJson(
+    OrthancPluginContext*        context,
+    OrthancPluginDicomInstance*  instance)
+  {
+    char* result;
+
+    _OrthancPluginAccessDicomInstance params;
+    memset(&params, 0, sizeof(params));
+    params.resultStringToFree = &result;
+    params.instance = instance;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetInstanceJson, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+  /**
+   * @brief Get the DICOM tag hierarchy as a JSON file (with simplification).
+   *
+   * This function returns a pointer to a newly created string
+   * containing a JSON file. This JSON file encodes the tag hierarchy
+   * of the given DICOM instance. In contrast with
+   * ::OrthancPluginGetInstanceJson(), the returned JSON file is in
+   * its simplified version.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param instance The instance of interest.
+   * @return The NULL value in case of error, or a string containing the JSON file.
+   * This string must be freed by OrthancPluginFreeString().
+   * @ingroup Callbacks
+   **/
+  ORTHANC_PLUGIN_INLINE char* OrthancPluginGetInstanceSimplifiedJson(
+    OrthancPluginContext*        context,
+    OrthancPluginDicomInstance*  instance)
+  {
+    char* result;
+
+    _OrthancPluginAccessDicomInstance params;
+    memset(&params, 0, sizeof(params));
+    params.resultStringToFree = &result;
+    params.instance = instance;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetInstanceSimplifiedJson, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+  /**
+   * @brief Check whether a DICOM instance is associated with some metadata.
+   *
+   * This function checks whether the DICOM instance of interest is
+   * associated with some metadata. As of Orthanc 0.8.1, in the
+   * callbacks registered by
+   * ::OrthancPluginRegisterOnStoredInstanceCallback(), the only
+   * possibly available metadata are "ReceptionDate", "RemoteAET" and
+   * "IndexInSeries".
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param instance The instance of interest.
+   * @param metadata The metadata of interest.
+   * @return 1 if the metadata is present, 0 if it is absent, -1 in case of error.
+   * @ingroup Callbacks
+   **/
+  ORTHANC_PLUGIN_INLINE int  OrthancPluginHasInstanceMetadata(
+    OrthancPluginContext*        context,
+    OrthancPluginDicomInstance*  instance,
+    const char*                  metadata)
+  {
+    int64_t result;
+
+    _OrthancPluginAccessDicomInstance params;
+    memset(&params, 0, sizeof(params));
+    params.resultInt64 = &result;
+    params.instance = instance;
+    params.key = metadata;
+
+    if (context->InvokeService(context, _OrthancPluginService_HasInstanceMetadata, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return -1;
+    }
+    else
+    {
+      return (result != 0);
+    }
+  }
+
+
+  /**
+   * @brief Get the value of some metadata associated with a given DICOM instance.
+   *
+   * This functions returns the value of some metadata that is associated with the DICOM instance of interest.
+   * Before calling this function, the existence of the metadata must have been checked with
+   * ::OrthancPluginHasInstanceMetadata().
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param instance The instance of interest.
+   * @param metadata The metadata of interest.
+   * @return The metadata value if success, NULL if error.
+   * @ingroup Callbacks
+   **/
+  ORTHANC_PLUGIN_INLINE const char* OrthancPluginGetInstanceMetadata(
+    OrthancPluginContext*        context,
+    OrthancPluginDicomInstance*  instance,
+    const char*                  metadata)
+  {
+    const char* result;
+
+    _OrthancPluginAccessDicomInstance params;
+    memset(&params, 0, sizeof(params));
+    params.resultString = &result;
+    params.instance = instance;
+    params.key = metadata;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetInstanceMetadata, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+
+  typedef struct
+  {
+    OrthancPluginStorageCreate  create;
+    OrthancPluginStorageRead    read;
+    OrthancPluginStorageRemove  remove;
+    OrthancPluginFree           free;
+  } _OrthancPluginRegisterStorageArea;
+
+  /**
+   * @brief Register a custom storage area.
+   *
+   * This function registers a custom storage area, to replace the
+   * built-in way Orthanc stores its files on the filesystem. This
+   * function must be called during the initialization of the plugin,
+   * i.e. inside the OrthancPluginInitialize() public function.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param create The callback function to store a file on the custom storage area.
+   * @param read The callback function to read a file from the custom storage area.
+   * @param remove The callback function to remove a file from the custom storage area.
+   * @ingroup Callbacks
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterStorageArea(
+    OrthancPluginContext*       context,
+    OrthancPluginStorageCreate  create,
+    OrthancPluginStorageRead    read,
+    OrthancPluginStorageRemove  remove)
+  {
+    _OrthancPluginRegisterStorageArea params;
+    params.create = create;
+    params.read = read;
+    params.remove = remove;
+
+#ifdef  __cplusplus
+    params.free = ::free;
+#else
+    params.free = free;
+#endif
+
+    context->InvokeService(context, _OrthancPluginService_RegisterStorageArea, &params);
+  }
+
+
+
+  /**
+   * @brief Return the path to the Orthanc executable.
+   *
+   * This function returns the path to the Orthanc executable.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @return NULL in the case of an error, or a newly allocated string
+   * containing the path. This string must be freed by
+   * OrthancPluginFreeString().
+   **/
+  ORTHANC_PLUGIN_INLINE char *OrthancPluginGetOrthancPath(OrthancPluginContext* context)
+  {
+    char* result;
+
+    _OrthancPluginRetrieveDynamicString params;
+    params.result = &result;
+    params.argument = NULL;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetOrthancPath, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+  /**
+   * @brief Return the directory containing the Orthanc.
+   *
+   * This function returns the path to the directory containing the Orthanc executable.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @return NULL in the case of an error, or a newly allocated string
+   * containing the path. This string must be freed by
+   * OrthancPluginFreeString().
+   **/
+  ORTHANC_PLUGIN_INLINE char *OrthancPluginGetOrthancDirectory(OrthancPluginContext* context)
+  {
+    char* result;
+
+    _OrthancPluginRetrieveDynamicString params;
+    params.result = &result;
+    params.argument = NULL;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetOrthancDirectory, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+  /**
+   * @brief Return the path to the configuration file(s).
+   *
+   * This function returns the path to the configuration file(s) that
+   * was specified when starting Orthanc. Since version 0.9.1, this
+   * path can refer to a folder that stores a set of configuration
+   * files. This function is deprecated in favor of
+   * OrthancPluginGetConfiguration().
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @return NULL in the case of an error, or a newly allocated string
+   * containing the path. This string must be freed by
+   * OrthancPluginFreeString().
+   * @see OrthancPluginGetConfiguration()
+   **/
+  ORTHANC_PLUGIN_INLINE char *OrthancPluginGetConfigurationPath(OrthancPluginContext* context)
+  {
+    char* result;
+
+    _OrthancPluginRetrieveDynamicString params;
+    params.result = &result;
+    params.argument = NULL;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetConfigurationPath, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+
+  typedef struct
+  {
+    OrthancPluginOnChangeCallback callback;
+  } _OrthancPluginOnChangeCallback;
+
+  /**
+   * @brief Register a callback to monitor changes.
+   *
+   * This function registers a callback function that is called
+   * whenever a change happens to some DICOM resource.
+   *
+   * @warning If your change callback has to call the REST API of
+   * Orthanc, you should make these calls in a separate thread (with
+   * the events passing through a message queue). Otherwise, this
+   * could result in deadlocks in the presence of other plugins or Lua
+   * script.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param callback The callback function.
+   * @ingroup Callbacks
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterOnChangeCallback(
+    OrthancPluginContext*          context,
+    OrthancPluginOnChangeCallback  callback)
+  {
+    _OrthancPluginOnChangeCallback params;
+    params.callback = callback;
+
+    context->InvokeService(context, _OrthancPluginService_RegisterOnChangeCallback, &params);
+  }
+
+
+
+  typedef struct
+  {
+    const char* plugin;
+    _OrthancPluginProperty property;
+    const char* value;
+  } _OrthancPluginSetPluginProperty;
+
+
+  /**
+   * @brief Set the URI where the plugin provides its Web interface.
+   *
+   * For plugins that come with a Web interface, this function
+   * declares the entry path where to find this interface. This
+   * information is notably used in the "Plugins" page of Orthanc
+   * Explorer.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param uri The root URI for this plugin.
+   **/ 
+  ORTHANC_PLUGIN_INLINE void OrthancPluginSetRootUri(
+    OrthancPluginContext*  context,
+    const char*            uri)
+  {
+    _OrthancPluginSetPluginProperty params;
+    params.plugin = OrthancPluginGetName();
+    params.property = _OrthancPluginProperty_RootUri;
+    params.value = uri;
+
+    context->InvokeService(context, _OrthancPluginService_SetPluginProperty, &params);
+  }
+
+
+  /**
+   * @brief Set a description for this plugin.
+   *
+   * Set a description for this plugin. It is displayed in the
+   * "Plugins" page of Orthanc Explorer.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param description The description.
+   **/ 
+  ORTHANC_PLUGIN_INLINE void OrthancPluginSetDescription(
+    OrthancPluginContext*  context,
+    const char*            description)
+  {
+    _OrthancPluginSetPluginProperty params;
+    params.plugin = OrthancPluginGetName();
+    params.property = _OrthancPluginProperty_Description;
+    params.value = description;
+
+    context->InvokeService(context, _OrthancPluginService_SetPluginProperty, &params);
+  }
+
+
+  /**
+   * @brief Extend the JavaScript code of Orthanc Explorer.
+   *
+   * Add JavaScript code to customize the default behavior of Orthanc
+   * Explorer. This can for instance be used to add new buttons.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param javascript The custom JavaScript code.
+   **/ 
+  ORTHANC_PLUGIN_INLINE void OrthancPluginExtendOrthancExplorer(
+    OrthancPluginContext*  context,
+    const char*            javascript)
+  {
+    _OrthancPluginSetPluginProperty params;
+    params.plugin = OrthancPluginGetName();
+    params.property = _OrthancPluginProperty_OrthancExplorer;
+    params.value = javascript;
+
+    context->InvokeService(context, _OrthancPluginService_SetPluginProperty, &params);
+  }
+
+
+  typedef struct
+  {
+    char**       result;
+    int32_t      property;
+    const char*  value;
+  } _OrthancPluginGlobalProperty;
+
+
+  /**
+   * @brief Get the value of a global property.
+   *
+   * Get the value of a global property that is stored in the Orthanc database. Global
+   * properties whose index is below 1024 are reserved by Orthanc.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param property The global property of interest.
+   * @param defaultValue The value to return, if the global property is unset.
+   * @return The value of the global property, or NULL in the case of an error. This
+   * string must be freed by OrthancPluginFreeString().
+   * @ingroup Orthanc
+   **/
+  ORTHANC_PLUGIN_INLINE char* OrthancPluginGetGlobalProperty(
+    OrthancPluginContext*  context,
+    int32_t                property,
+    const char*            defaultValue)
+  {
+    char* result;
+
+    _OrthancPluginGlobalProperty params;
+    params.result = &result;
+    params.property = property;
+    params.value = defaultValue;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetGlobalProperty, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+  /**
+   * @brief Set the value of a global property.
+   *
+   * Set the value of a global property into the Orthanc
+   * database. Setting a global property can be used by plugins to
+   * save their internal parameters. Plugins are only allowed to set
+   * properties whose index are above or equal to 1024 (properties
+   * below 1024 are read-only and reserved by Orthanc).
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param property The global property of interest.
+   * @param value The value to be set in the global property.
+   * @return 0 if success, or the error code if failure.
+   * @ingroup Orthanc
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginSetGlobalProperty(
+    OrthancPluginContext*  context,
+    int32_t                property,
+    const char*            value)
+  {
+    _OrthancPluginGlobalProperty params;
+    params.result = NULL;
+    params.property = property;
+    params.value = value;
+
+    return context->InvokeService(context, _OrthancPluginService_SetGlobalProperty, &params);
+  }
+
+
+
+  typedef struct
+  {
+    int32_t   *resultInt32;
+    uint32_t  *resultUint32;
+    int64_t   *resultInt64;
+    uint64_t  *resultUint64;
+  } _OrthancPluginReturnSingleValue;
+
+  /**
+   * @brief Get the number of command-line arguments.
+   *
+   * Retrieve the number of command-line arguments that were used to launch Orthanc.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @return The number of arguments.
+   **/
+  ORTHANC_PLUGIN_INLINE uint32_t OrthancPluginGetCommandLineArgumentsCount(
+    OrthancPluginContext*  context)
+  {
+    uint32_t count = 0;
+
+    _OrthancPluginReturnSingleValue params;
+    memset(&params, 0, sizeof(params));
+    params.resultUint32 = &count;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetCommandLineArgumentsCount, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return 0;
+    }
+    else
+    {
+      return count;
+    }
+  }
+
+
+
+  /**
+   * @brief Get the value of a command-line argument.
+   *
+   * Get the value of one of the command-line arguments that were used
+   * to launch Orthanc. The number of available arguments can be
+   * retrieved by OrthancPluginGetCommandLineArgumentsCount().
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param argument The index of the argument.
+   * @return The value of the argument, or NULL in the case of an error. This
+   * string must be freed by OrthancPluginFreeString().
+   **/
+  ORTHANC_PLUGIN_INLINE char* OrthancPluginGetCommandLineArgument(
+    OrthancPluginContext*  context,
+    uint32_t               argument)
+  {
+    char* result;
+
+    _OrthancPluginGlobalProperty params;
+    params.result = &result;
+    params.property = (int32_t) argument;
+    params.value = NULL;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetCommandLineArgument, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+  /**
+   * @brief Get the expected version of the database schema.
+   *
+   * Retrieve the expected version of the database schema.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @return The version.
+   * @ingroup Callbacks
+   * @deprecated Please instead use IDatabaseBackend::UpgradeDatabase()
+   **/
+  ORTHANC_PLUGIN_INLINE uint32_t OrthancPluginGetExpectedDatabaseVersion(
+    OrthancPluginContext*  context)
+  {
+    uint32_t count = 0;
+
+    _OrthancPluginReturnSingleValue params;
+    memset(&params, 0, sizeof(params));
+    params.resultUint32 = &count;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetExpectedDatabaseVersion, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return 0;
+    }
+    else
+    {
+      return count;
+    }
+  }
+
+
+
+  /**
+   * @brief Return the content of the configuration file(s).
+   *
+   * This function returns the content of the configuration that is
+   * used by Orthanc, formatted as a JSON string.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @return NULL in the case of an error, or a newly allocated string
+   * containing the configuration. This string must be freed by
+   * OrthancPluginFreeString().
+   **/
+  ORTHANC_PLUGIN_INLINE char *OrthancPluginGetConfiguration(OrthancPluginContext* context)
+  {
+    char* result;
+
+    _OrthancPluginRetrieveDynamicString params;
+    params.result = &result;
+    params.argument = NULL;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetConfiguration, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+
+  typedef struct
+  {
+    OrthancPluginRestOutput* output;
+    const char*              subType;
+    const char*              contentType;
+  } _OrthancPluginStartMultipartAnswer;
+
+  /**
+   * @brief Start an HTTP multipart answer.
+   *
+   * Initiates a HTTP multipart answer, as the result of a REST request.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param output The HTTP connection to the client application.
+   * @param subType The sub-type of the multipart answer ("mixed" or "related").
+   * @param contentType The MIME type of the items in the multipart answer.
+   * @return 0 if success, or the error code if failure.
+   * @see OrthancPluginSendMultipartItem()
+   * @ingroup REST
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginStartMultipartAnswer(
+    OrthancPluginContext*    context,
+    OrthancPluginRestOutput* output,
+    const char*              subType,
+    const char*              contentType)
+  {
+    _OrthancPluginStartMultipartAnswer params;
+    params.output = output;
+    params.subType = subType;
+    params.contentType = contentType;
+    return context->InvokeService(context, _OrthancPluginService_StartMultipartAnswer, &params);
+  }
+
+
+  /**
+   * @brief Send an item as a part of some HTTP multipart answer.
+   *
+   * This function sends an item as a part of some HTTP multipart
+   * answer that was initiated by OrthancPluginStartMultipartAnswer().
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param output The HTTP connection to the client application.
+   * @param answer Pointer to the memory buffer containing the item.
+   * @param answerSize Number of bytes of the item.
+   * @return 0 if success, or the error code if failure (this notably happens
+   * if the connection is closed by the client).
+   * @ingroup REST
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginSendMultipartItem(
+    OrthancPluginContext*    context,
+    OrthancPluginRestOutput* output,
+    const char*              answer,
+    uint32_t                 answerSize)
+  {
+    _OrthancPluginAnswerBuffer params;
+    params.output = output;
+    params.answer = answer;
+    params.answerSize = answerSize;
+    params.mimeType = NULL;
+    return context->InvokeService(context, _OrthancPluginService_SendMultipartItem, &params);
+  }
+
+
+
+  typedef struct
+  {
+    OrthancPluginMemoryBuffer*    target;
+    const void*                   source;
+    uint32_t                      size;
+    OrthancPluginCompressionType  compression;
+    uint8_t                       uncompress;
+  } _OrthancPluginBufferCompression;
+
+
+  /**
+   * @brief Compress or decompress a buffer.
+   *
+   * This function compresses or decompresses a buffer, using the
+   * version of the zlib library that is used by the Orthanc core.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param target The target memory buffer.
+   * @param source The source buffer.
+   * @param size The size in bytes of the source buffer.
+   * @param compression The compression algorithm.
+   * @param uncompress If set to "0", the buffer must be compressed. 
+   * If set to "1", the buffer must be uncompressed.
+   * @return 0 if success, or the error code if failure.
+   * @ingroup Images
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginBufferCompression(
+    OrthancPluginContext*         context,
+    OrthancPluginMemoryBuffer*    target,
+    const void*                   source,
+    uint32_t                      size,
+    OrthancPluginCompressionType  compression,
+    uint8_t                       uncompress)
+  {
+    _OrthancPluginBufferCompression params;
+    params.target = target;
+    params.source = source;
+    params.size = size;
+    params.compression = compression;
+    params.uncompress = uncompress;
+
+    return context->InvokeService(context, _OrthancPluginService_BufferCompression, &params);
+  }
+
+
+
+  typedef struct
+  {
+    OrthancPluginMemoryBuffer*  target;
+    const char*                 path;
+  } _OrthancPluginReadFile;
+
+  /**
+   * @brief Read a file.
+   * 
+   * Read the content of a file on the filesystem, and returns it into
+   * a newly allocated memory buffer.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param target The target memory buffer.
+   * @param path The path of the file to be read.
+   * @return 0 if success, or the error code if failure.
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginReadFile(
+    OrthancPluginContext*       context,
+    OrthancPluginMemoryBuffer*  target,
+    const char*                 path)
+  {
+    _OrthancPluginReadFile params;
+    params.target = target;
+    params.path = path;
+    return context->InvokeService(context, _OrthancPluginService_ReadFile, &params);
+  }
+
+
+
+  typedef struct
+  {
+    const char*  path;
+    const void*  data;
+    uint32_t     size;
+  } _OrthancPluginWriteFile;
+
+  /**
+   * @brief Write a file.
+   * 
+   * Write the content of a memory buffer to the filesystem.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param path The path of the file to be written.
+   * @param data The content of the memory buffer.
+   * @param size The size of the memory buffer.
+   * @return 0 if success, or the error code if failure.
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginWriteFile(
+    OrthancPluginContext*  context,
+    const char*            path,
+    const void*            data,
+    uint32_t               size)
+  {
+    _OrthancPluginWriteFile params;
+    params.path = path;
+    params.data = data;
+    params.size = size;
+    return context->InvokeService(context, _OrthancPluginService_WriteFile, &params);
+  }
+
+
+
+  typedef struct
+  {
+    const char**            target;
+    OrthancPluginErrorCode  error;
+  } _OrthancPluginGetErrorDescription;
+
+  /**
+   * @brief Get the description of a given error code.
+   *
+   * This function returns the description of a given error code.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param error The error code of interest.
+   * @return The error description. This is a statically-allocated
+   * string, do not free it.
+   **/
+  ORTHANC_PLUGIN_INLINE const char* OrthancPluginGetErrorDescription(
+    OrthancPluginContext*    context,
+    OrthancPluginErrorCode   error)
+  {
+    const char* result = NULL;
+
+    _OrthancPluginGetErrorDescription params;
+    params.target = &result;
+    params.error = error;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetErrorDescription, &params) != OrthancPluginErrorCode_Success ||
+        result == NULL)
+    {
+      return "Unknown error code";
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+
+  typedef struct
+  {
+    OrthancPluginRestOutput* output;
+    uint16_t                 status;
+    const char*              body;
+    uint32_t                 bodySize;
+  } _OrthancPluginSendHttpStatus;
+
+  /**
+   * @brief Send a HTTP status, with a custom body.
+   *
+   * This function answers to a HTTP request by sending a HTTP status
+   * code (such as "400 - Bad Request"), together with a body
+   * describing the error. The body will only be returned if the
+   * configuration option "HttpDescribeErrors" of Orthanc is set to "true".
+   * 
+   * Note that:
+   * - Successful requests (status 200) must use ::OrthancPluginAnswerBuffer().
+   * - Redirections (status 301) must use ::OrthancPluginRedirect().
+   * - Unauthorized access (status 401) must use ::OrthancPluginSendUnauthorized().
+   * - Methods not allowed (status 405) must use ::OrthancPluginSendMethodNotAllowed().
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param output The HTTP connection to the client application.
+   * @param status The HTTP status code to be sent.
+   * @param body The body of the answer.
+   * @param bodySize The size of the body.
+   * @see OrthancPluginSendHttpStatusCode()
+   * @ingroup REST
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginSendHttpStatus(
+    OrthancPluginContext*    context,
+    OrthancPluginRestOutput* output,
+    uint16_t                 status,
+    const char*              body,
+    uint32_t                 bodySize)
+  {
+    _OrthancPluginSendHttpStatus params;
+    params.output = output;
+    params.status = status;
+    params.body = body;
+    params.bodySize = bodySize;
+    context->InvokeService(context, _OrthancPluginService_SendHttpStatus, &params);
+  }
+
+
+
+  typedef struct
+  {
+    const OrthancPluginImage*  image;
+    uint32_t*                  resultUint32;
+    OrthancPluginPixelFormat*  resultPixelFormat;
+    const void**               resultBuffer;
+  } _OrthancPluginGetImageInfo;
+
+
+  /**
+   * @brief Return the pixel format of an image.
+   *
+   * This function returns the type of memory layout for the pixels of the given image.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param image The image of interest.
+   * @return The pixel format.
+   * @ingroup Images
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginPixelFormat  OrthancPluginGetImagePixelFormat(
+    OrthancPluginContext*      context,
+    const OrthancPluginImage*  image)
+  {
+    OrthancPluginPixelFormat target;
+    
+    _OrthancPluginGetImageInfo params;
+    memset(&params, 0, sizeof(params));
+    params.image = image;
+    params.resultPixelFormat = &target;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetImagePixelFormat, &params) != OrthancPluginErrorCode_Success)
+    {
+      return OrthancPluginPixelFormat_Unknown;
+    }
+    else
+    {
+      return (OrthancPluginPixelFormat) target;
+    }
+  }
+
+
+
+  /**
+   * @brief Return the width of an image.
+   *
+   * This function returns the width of the given image.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param image The image of interest.
+   * @return The width.
+   * @ingroup Images
+   **/
+  ORTHANC_PLUGIN_INLINE uint32_t  OrthancPluginGetImageWidth(
+    OrthancPluginContext*      context,
+    const OrthancPluginImage*  image)
+  {
+    uint32_t width;
+    
+    _OrthancPluginGetImageInfo params;
+    memset(&params, 0, sizeof(params));
+    params.image = image;
+    params.resultUint32 = &width;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetImageWidth, &params) != OrthancPluginErrorCode_Success)
+    {
+      return 0;
+    }
+    else
+    {
+      return width;
+    }
+  }
+
+
+
+  /**
+   * @brief Return the height of an image.
+   *
+   * This function returns the height of the given image.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param image The image of interest.
+   * @return The height.
+   * @ingroup Images
+   **/
+  ORTHANC_PLUGIN_INLINE uint32_t  OrthancPluginGetImageHeight(
+    OrthancPluginContext*      context,
+    const OrthancPluginImage*  image)
+  {
+    uint32_t height;
+    
+    _OrthancPluginGetImageInfo params;
+    memset(&params, 0, sizeof(params));
+    params.image = image;
+    params.resultUint32 = &height;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetImageHeight, &params) != OrthancPluginErrorCode_Success)
+    {
+      return 0;
+    }
+    else
+    {
+      return height;
+    }
+  }
+
+
+
+  /**
+   * @brief Return the pitch of an image.
+   *
+   * This function returns the pitch of the given image. The pitch is
+   * defined as the number of bytes between 2 successive lines of the
+   * image in the memory buffer.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param image The image of interest.
+   * @return The pitch.
+   * @ingroup Images
+   **/
+  ORTHANC_PLUGIN_INLINE uint32_t  OrthancPluginGetImagePitch(
+    OrthancPluginContext*      context,
+    const OrthancPluginImage*  image)
+  {
+    uint32_t pitch;
+    
+    _OrthancPluginGetImageInfo params;
+    memset(&params, 0, sizeof(params));
+    params.image = image;
+    params.resultUint32 = &pitch;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetImagePitch, &params) != OrthancPluginErrorCode_Success)
+    {
+      return 0;
+    }
+    else
+    {
+      return pitch;
+    }
+  }
+
+
+
+  /**
+   * @brief Return a pointer to the content of an image.
+   *
+   * This function returns a pointer to the memory buffer that
+   * contains the pixels of the image.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param image The image of interest.
+   * @return The pointer.
+   * @ingroup Images
+   **/
+  ORTHANC_PLUGIN_INLINE const void*  OrthancPluginGetImageBuffer(
+    OrthancPluginContext*      context,
+    const OrthancPluginImage*  image)
+  {
+    const void* target = NULL;
+
+    _OrthancPluginGetImageInfo params;
+    memset(&params, 0, sizeof(params));
+    params.resultBuffer = &target;
+    params.image = image;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetImageBuffer, &params) != OrthancPluginErrorCode_Success)
+    {
+      return NULL;
+    }
+    else
+    {
+      return target;
+    }
+  }
+
+
+  typedef struct
+  {
+    OrthancPluginImage**       target;
+    const void*                data;
+    uint32_t                   size;
+    OrthancPluginImageFormat   format;
+  } _OrthancPluginUncompressImage;
+
+
+  /**
+   * @brief Decode a compressed image.
+   *
+   * This function decodes a compressed image from a memory buffer.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param data Pointer to a memory buffer containing the compressed image.
+   * @param size Size of the memory buffer containing the compressed image.
+   * @param format The file format of the compressed image.
+   * @return The uncompressed image. It must be freed with OrthancPluginFreeImage().
+   * @ingroup Images
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginImage *OrthancPluginUncompressImage(
+    OrthancPluginContext*      context,
+    const void*                data,
+    uint32_t                   size,
+    OrthancPluginImageFormat   format)
+  {
+    OrthancPluginImage* target = NULL;
+
+    _OrthancPluginUncompressImage params;
+    memset(&params, 0, sizeof(params));
+    params.target = &target;
+    params.data = data;
+    params.size = size;
+    params.format = format;
+
+    if (context->InvokeService(context, _OrthancPluginService_UncompressImage, &params) != OrthancPluginErrorCode_Success)
+    {
+      return NULL;
+    }
+    else
+    {
+      return target;
+    }
+  }
+
+
+
+
+  typedef struct
+  {
+    OrthancPluginImage*   image;
+  } _OrthancPluginFreeImage;
+
+  /**
+   * @brief Free an image.
+   *
+   * This function frees an image that was decoded with OrthancPluginUncompressImage().
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param image The image.
+   * @ingroup Images
+   **/
+  ORTHANC_PLUGIN_INLINE void  OrthancPluginFreeImage(
+    OrthancPluginContext* context, 
+    OrthancPluginImage*   image)
+  {
+    _OrthancPluginFreeImage params;
+    params.image = image;
+
+    context->InvokeService(context, _OrthancPluginService_FreeImage, &params);
+  }
+
+
+
+
+  typedef struct
+  {
+    OrthancPluginMemoryBuffer* target;
+    OrthancPluginImageFormat   imageFormat;
+    OrthancPluginPixelFormat   pixelFormat;
+    uint32_t                   width;
+    uint32_t                   height;
+    uint32_t                   pitch;
+    const void*                buffer;
+    uint8_t                    quality;
+  } _OrthancPluginCompressImage;
+
+
+  /**
+   * @brief Encode a PNG image.
+   *
+   * This function compresses the given memory buffer containing an
+   * image using the PNG specification, and stores the result of the
+   * compression into a newly allocated memory buffer.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param target The target memory buffer. It must be freed with OrthancPluginFreeMemoryBuffer().
+   * @param format The memory layout of the uncompressed image.
+   * @param width The width of the image.
+   * @param height The height of the image.
+   * @param pitch The pitch of the image (i.e. the number of bytes
+   * between 2 successive lines of the image in the memory buffer).
+   * @param buffer The memory buffer containing the uncompressed image.
+   * @return 0 if success, or the error code if failure.
+   * @see OrthancPluginCompressAndAnswerPngImage()
+   * @ingroup Images
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginCompressPngImage(
+    OrthancPluginContext*         context,
+    OrthancPluginMemoryBuffer*    target,
+    OrthancPluginPixelFormat      format,
+    uint32_t                      width,
+    uint32_t                      height,
+    uint32_t                      pitch,
+    const void*                   buffer)
+  {
+    _OrthancPluginCompressImage params;
+    memset(&params, 0, sizeof(params));
+    params.target = target;
+    params.imageFormat = OrthancPluginImageFormat_Png;
+    params.pixelFormat = format;
+    params.width = width;
+    params.height = height;
+    params.pitch = pitch;
+    params.buffer = buffer;
+    params.quality = 0;  /* Unused for PNG */
+
+    return context->InvokeService(context, _OrthancPluginService_CompressImage, &params);
+  }
+
+
+  /**
+   * @brief Encode a JPEG image.
+   *
+   * This function compresses the given memory buffer containing an
+   * image using the JPEG specification, and stores the result of the
+   * compression into a newly allocated memory buffer.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param target The target memory buffer. It must be freed with OrthancPluginFreeMemoryBuffer().
+   * @param format The memory layout of the uncompressed image.
+   * @param width The width of the image.
+   * @param height The height of the image.
+   * @param pitch The pitch of the image (i.e. the number of bytes
+   * between 2 successive lines of the image in the memory buffer).
+   * @param buffer The memory buffer containing the uncompressed image.
+   * @param quality The quality of the JPEG encoding, between 1 (worst
+   * quality, best compression) and 100 (best quality, worst
+   * compression).
+   * @return 0 if success, or the error code if failure.
+   * @ingroup Images
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginCompressJpegImage(
+    OrthancPluginContext*         context,
+    OrthancPluginMemoryBuffer*    target,
+    OrthancPluginPixelFormat      format,
+    uint32_t                      width,
+    uint32_t                      height,
+    uint32_t                      pitch,
+    const void*                   buffer,
+    uint8_t                       quality)
+  {
+    _OrthancPluginCompressImage params;
+    memset(&params, 0, sizeof(params));
+    params.target = target;
+    params.imageFormat = OrthancPluginImageFormat_Jpeg;
+    params.pixelFormat = format;
+    params.width = width;
+    params.height = height;
+    params.pitch = pitch;
+    params.buffer = buffer;
+    params.quality = quality;
+
+    return context->InvokeService(context, _OrthancPluginService_CompressImage, &params);
+  }
+
+
+
+  /**
+   * @brief Answer to a REST request with a JPEG image.
+   *
+   * This function answers to a REST request with a JPEG image. The
+   * parameters of this function describe a memory buffer that
+   * contains an uncompressed image. The image will be automatically compressed
+   * as a JPEG image by the core system of Orthanc.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param output The HTTP connection to the client application.
+   * @param format The memory layout of the uncompressed image.
+   * @param width The width of the image.
+   * @param height The height of the image.
+   * @param pitch The pitch of the image (i.e. the number of bytes
+   * between 2 successive lines of the image in the memory buffer).
+   * @param buffer The memory buffer containing the uncompressed image.
+   * @param quality The quality of the JPEG encoding, between 1 (worst
+   * quality, best compression) and 100 (best quality, worst
+   * compression).
+   * @ingroup REST
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginCompressAndAnswerJpegImage(
+    OrthancPluginContext*     context,
+    OrthancPluginRestOutput*  output,
+    OrthancPluginPixelFormat  format,
+    uint32_t                  width,
+    uint32_t                  height,
+    uint32_t                  pitch,
+    const void*               buffer,
+    uint8_t                   quality)
+  {
+    _OrthancPluginCompressAndAnswerImage params;
+    params.output = output;
+    params.imageFormat = OrthancPluginImageFormat_Jpeg;
+    params.pixelFormat = format;
+    params.width = width;
+    params.height = height;
+    params.pitch = pitch;
+    params.buffer = buffer;
+    params.quality = quality;
+    context->InvokeService(context, _OrthancPluginService_CompressAndAnswerImage, &params);
+  }
+
+
+
+
+  typedef struct
+  {
+    OrthancPluginMemoryBuffer*  target;
+    OrthancPluginHttpMethod     method;
+    const char*                 url;
+    const char*                 username;
+    const char*                 password;
+    const char*                 body;
+    uint32_t                    bodySize;
+  } _OrthancPluginCallHttpClient;
+
+
+  /**
+   * @brief Issue a HTTP GET call.
+   * 
+   * Make a HTTP GET call to the given URL. The result to the query is
+   * stored into a newly allocated memory buffer. Favor
+   * OrthancPluginRestApiGet() if calling the built-in REST API of the
+   * Orthanc instance that hosts this plugin.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param target The target memory buffer.
+   * @param url The URL of interest.
+   * @param username The username (can be <tt>NULL</tt> if no password protection).
+   * @param password The password (can be <tt>NULL</tt> if no password protection).
+   * @return 0 if success, or the error code if failure.
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginHttpGet(
+    OrthancPluginContext*       context,
+    OrthancPluginMemoryBuffer*  target,
+    const char*                 url,
+    const char*                 username,
+    const char*                 password)
+  {
+    _OrthancPluginCallHttpClient params;
+    memset(&params, 0, sizeof(params));
+
+    params.target = target;
+    params.method = OrthancPluginHttpMethod_Get;
+    params.url = url;
+    params.username = username;
+    params.password = password;
+
+    return context->InvokeService(context, _OrthancPluginService_CallHttpClient, &params);
+  }
+
+
+  /**
+   * @brief Issue a HTTP POST call.
+   * 
+   * Make a HTTP POST call to the given URL. The result to the query
+   * is stored into a newly allocated memory buffer. Favor
+   * OrthancPluginRestApiPost() if calling the built-in REST API of
+   * the Orthanc instance that hosts this plugin.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param target The target memory buffer.
+   * @param url The URL of interest.
+   * @param body The content of the body of the request.
+   * @param bodySize The size of the body of the request.
+   * @param username The username (can be <tt>NULL</tt> if no password protection).
+   * @param password The password (can be <tt>NULL</tt> if no password protection).
+   * @return 0 if success, or the error code if failure.
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginHttpPost(
+    OrthancPluginContext*       context,
+    OrthancPluginMemoryBuffer*  target,
+    const char*                 url,
+    const char*                 body,
+    uint32_t                    bodySize,
+    const char*                 username,
+    const char*                 password)
+  {
+    _OrthancPluginCallHttpClient params;
+    memset(&params, 0, sizeof(params));
+
+    params.target = target;
+    params.method = OrthancPluginHttpMethod_Post;
+    params.url = url;
+    params.body = body;
+    params.bodySize = bodySize;
+    params.username = username;
+    params.password = password;
+
+    return context->InvokeService(context, _OrthancPluginService_CallHttpClient, &params);
+  }
+
+
+  /**
+   * @brief Issue a HTTP PUT call.
+   * 
+   * Make a HTTP PUT call to the given URL. The result to the query is
+   * stored into a newly allocated memory buffer. Favor
+   * OrthancPluginRestApiPut() if calling the built-in REST API of the
+   * Orthanc instance that hosts this plugin.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param target The target memory buffer.
+   * @param url The URL of interest.
+   * @param body The content of the body of the request.
+   * @param bodySize The size of the body of the request.
+   * @param username The username (can be <tt>NULL</tt> if no password protection).
+   * @param password The password (can be <tt>NULL</tt> if no password protection).
+   * @return 0 if success, or the error code if failure.
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginHttpPut(
+    OrthancPluginContext*       context,
+    OrthancPluginMemoryBuffer*  target,
+    const char*                 url,
+    const char*                 body,
+    uint32_t                    bodySize,
+    const char*                 username,
+    const char*                 password)
+  {
+    _OrthancPluginCallHttpClient params;
+    memset(&params, 0, sizeof(params));
+
+    params.target = target;
+    params.method = OrthancPluginHttpMethod_Put;
+    params.url = url;
+    params.body = body;
+    params.bodySize = bodySize;
+    params.username = username;
+    params.password = password;
+
+    return context->InvokeService(context, _OrthancPluginService_CallHttpClient, &params);
+  }
+
+
+  /**
+   * @brief Issue a HTTP DELETE call.
+   * 
+   * Make a HTTP DELETE call to the given URL. Favor
+   * OrthancPluginRestApiDelete() if calling the built-in REST API of
+   * the Orthanc instance that hosts this plugin.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param url The URL of interest.
+   * @param username The username (can be <tt>NULL</tt> if no password protection).
+   * @param password The password (can be <tt>NULL</tt> if no password protection).
+   * @return 0 if success, or the error code if failure.
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginHttpDelete(
+    OrthancPluginContext*       context,
+    const char*                 url,
+    const char*                 username,
+    const char*                 password)
+  {
+    _OrthancPluginCallHttpClient params;
+    memset(&params, 0, sizeof(params));
+
+    params.method = OrthancPluginHttpMethod_Delete;
+    params.url = url;
+    params.username = username;
+    params.password = password;
+
+    return context->InvokeService(context, _OrthancPluginService_CallHttpClient, &params);
+  }
+
+
+
+  typedef struct
+  {
+    OrthancPluginImage**       target;
+    const OrthancPluginImage*  source;
+    OrthancPluginPixelFormat   targetFormat;
+  } _OrthancPluginConvertPixelFormat;
+
+
+  /**
+   * @brief Change the pixel format of an image.
+   *
+   * This function creates a new image, changing the memory layout of the pixels.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param source The source image.
+   * @param targetFormat The target pixel format.
+   * @return The resulting image. It must be freed with OrthancPluginFreeImage().
+   * @ingroup Images
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginImage *OrthancPluginConvertPixelFormat(
+    OrthancPluginContext*      context,
+    const OrthancPluginImage*  source,
+    OrthancPluginPixelFormat   targetFormat)
+  {
+    OrthancPluginImage* target = NULL;
+
+    _OrthancPluginConvertPixelFormat params;
+    params.target = &target;
+    params.source = source;
+    params.targetFormat = targetFormat;
+
+    if (context->InvokeService(context, _OrthancPluginService_ConvertPixelFormat, &params) != OrthancPluginErrorCode_Success)
+    {
+      return NULL;
+    }
+    else
+    {
+      return target;
+    }
+  }
+
+
+
+  /**
+   * @brief Return the number of available fonts.
+   *
+   * This function returns the number of fonts that are built in the
+   * Orthanc core. These fonts can be used to draw texts on images
+   * through OrthancPluginDrawText().
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @return The number of fonts.
+   * @ingroup Images
+   **/
+  ORTHANC_PLUGIN_INLINE uint32_t OrthancPluginGetFontsCount(
+    OrthancPluginContext*  context)
+  {
+    uint32_t count = 0;
+
+    _OrthancPluginReturnSingleValue params;
+    memset(&params, 0, sizeof(params));
+    params.resultUint32 = &count;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetFontsCount, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return 0;
+    }
+    else
+    {
+      return count;
+    }
+  }
+
+
+
+
+  typedef struct
+  {
+    uint32_t      fontIndex; /* in */
+    const char**  name; /* out */
+    uint32_t*     size; /* out */
+  } _OrthancPluginGetFontInfo;
+
+  /**
+   * @brief Return the name of a font.
+   *
+   * This function returns the name of a font that is built in the Orthanc core.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param fontIndex The index of the font. This value must be less than OrthancPluginGetFontsCount().
+   * @return The font name. This is a statically-allocated string, do not free it.
+   * @ingroup Images
+   **/
+  ORTHANC_PLUGIN_INLINE const char* OrthancPluginGetFontName(
+    OrthancPluginContext*  context,
+    uint32_t               fontIndex)
+  {
+    const char* result = NULL;
+
+    _OrthancPluginGetFontInfo params;
+    memset(&params, 0, sizeof(params));
+    params.name = &result;
+    params.fontIndex = fontIndex;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetFontInfo, &params) != OrthancPluginErrorCode_Success)
+    {
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+  /**
+   * @brief Return the size of a font.
+   *
+   * This function returns the size of a font that is built in the Orthanc core.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param fontIndex The index of the font. This value must be less than OrthancPluginGetFontsCount().
+   * @return The font size.
+   * @ingroup Images
+   **/
+  ORTHANC_PLUGIN_INLINE uint32_t OrthancPluginGetFontSize(
+    OrthancPluginContext*  context,
+    uint32_t               fontIndex)
+  {
+    uint32_t result;
+
+    _OrthancPluginGetFontInfo params;
+    memset(&params, 0, sizeof(params));
+    params.size = &result;
+    params.fontIndex = fontIndex;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetFontInfo, &params) != OrthancPluginErrorCode_Success)
+    {
+      return 0;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+
+  typedef struct
+  {
+    OrthancPluginImage*   image;
+    uint32_t              fontIndex;
+    const char*           utf8Text;
+    int32_t               x;
+    int32_t               y;
+    uint8_t               r;
+    uint8_t               g;
+    uint8_t               b;
+  } _OrthancPluginDrawText;
+
+
+  /**
+   * @brief Draw text on an image.
+   *
+   * This function draws some text on some image.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param image The image upon which to draw the text.
+   * @param fontIndex The index of the font. This value must be less than OrthancPluginGetFontsCount().
+   * @param utf8Text The text to be drawn, encoded as an UTF-8 zero-terminated string.
+   * @param x The X position of the text over the image.
+   * @param y The Y position of the text over the image.
+   * @param r The value of the red color channel of the text.
+   * @param g The value of the green color channel of the text.
+   * @param b The value of the blue color channel of the text.
+   * @return 0 if success, other value if error.
+   * @ingroup Images
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginDrawText(
+    OrthancPluginContext*  context,
+    OrthancPluginImage*    image,
+    uint32_t               fontIndex,
+    const char*            utf8Text,
+    int32_t                x,
+    int32_t                y,
+    uint8_t                r,
+    uint8_t                g,
+    uint8_t                b)
+  {
+    _OrthancPluginDrawText params;
+    memset(&params, 0, sizeof(params));
+    params.image = image;
+    params.fontIndex = fontIndex;
+    params.utf8Text = utf8Text;
+    params.x = x;
+    params.y = y;
+    params.r = r;
+    params.g = g;
+    params.b = b;
+
+    return context->InvokeService(context, _OrthancPluginService_DrawText, &params);
+  }
+
+
+
+  typedef struct
+  {
+    OrthancPluginStorageArea*   storageArea;
+    const char*                 uuid;
+    const void*                 content;
+    uint64_t                    size;
+    OrthancPluginContentType    type;
+  } _OrthancPluginStorageAreaCreate;
+
+
+  /**
+   * @brief Create a file inside the storage area.
+   *
+   * This function creates a new file inside the storage area that is
+   * currently used by Orthanc.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param storageArea The storage area.
+   * @param uuid The identifier of the file to be created.
+   * @param content The content to store in the newly created file.
+   * @param size The size of the content.
+   * @param type The type of the file content.
+   * @return 0 if success, other value if error.
+   * @ingroup Callbacks
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginStorageAreaCreate(
+    OrthancPluginContext*       context,
+    OrthancPluginStorageArea*   storageArea,
+    const char*                 uuid,
+    const void*                 content,
+    uint64_t                    size,
+    OrthancPluginContentType    type)
+  {
+    _OrthancPluginStorageAreaCreate params;
+    params.storageArea = storageArea;
+    params.uuid = uuid;
+    params.content = content;
+    params.size = size;
+    params.type = type;
+
+    return context->InvokeService(context, _OrthancPluginService_StorageAreaCreate, &params);
+  }
+
+
+  typedef struct
+  {
+    OrthancPluginMemoryBuffer*  target;
+    OrthancPluginStorageArea*   storageArea;
+    const char*                 uuid;
+    OrthancPluginContentType    type;
+  } _OrthancPluginStorageAreaRead;
+
+
+  /**
+   * @brief Read a file from the storage area.
+   *
+   * This function reads the content of a given file from the storage
+   * area that is currently used by Orthanc.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param target The target memory buffer. It must be freed with OrthancPluginFreeMemoryBuffer().
+   * @param storageArea The storage area.
+   * @param uuid The identifier of the file to be read.
+   * @param type The type of the file content.
+   * @return 0 if success, other value if error.
+   * @ingroup Callbacks
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginStorageAreaRead(
+    OrthancPluginContext*       context,
+    OrthancPluginMemoryBuffer*  target,
+    OrthancPluginStorageArea*   storageArea,
+    const char*                 uuid,
+    OrthancPluginContentType    type)
+  {
+    _OrthancPluginStorageAreaRead params;
+    params.target = target;
+    params.storageArea = storageArea;
+    params.uuid = uuid;
+    params.type = type;
+
+    return context->InvokeService(context, _OrthancPluginService_StorageAreaRead, &params);
+  }
+
+
+  typedef struct
+  {
+    OrthancPluginStorageArea*   storageArea;
+    const char*                 uuid;
+    OrthancPluginContentType    type;
+  } _OrthancPluginStorageAreaRemove;
+
+  /**
+   * @brief Remove a file from the storage area.
+   *
+   * This function removes a given file from the storage area that is
+   * currently used by Orthanc.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param storageArea The storage area.
+   * @param uuid The identifier of the file to be removed.
+   * @param type The type of the file content.
+   * @return 0 if success, other value if error.
+   * @ingroup Callbacks
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginStorageAreaRemove(
+    OrthancPluginContext*       context,
+    OrthancPluginStorageArea*   storageArea,
+    const char*                 uuid,
+    OrthancPluginContentType    type)
+  {
+    _OrthancPluginStorageAreaRemove params;
+    params.storageArea = storageArea;
+    params.uuid = uuid;
+    params.type = type;
+
+    return context->InvokeService(context, _OrthancPluginService_StorageAreaRemove, &params);
+  }
+
+
+
+  typedef struct
+  {
+    OrthancPluginErrorCode*  target;
+    int32_t                  code;
+    uint16_t                 httpStatus;
+    const char*              message;
+  } _OrthancPluginRegisterErrorCode;
+  
+  /**
+   * @brief Declare a custom error code for this plugin.
+   *
+   * This function declares a custom error code that can be generated
+   * by this plugin. This declaration is used to enrich the body of
+   * the HTTP answer in the case of an error, and to set the proper
+   * HTTP status code.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param code The error code that is internal to this plugin.
+   * @param httpStatus The HTTP status corresponding to this error.
+   * @param message The description of the error.
+   * @return The error code that has been assigned inside the Orthanc core.
+   * @ingroup Toolbox
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginRegisterErrorCode(
+    OrthancPluginContext*    context,
+    int32_t                  code,
+    uint16_t                 httpStatus,
+    const char*              message)
+  {
+    OrthancPluginErrorCode target;
+
+    _OrthancPluginRegisterErrorCode params;
+    params.target = &target;
+    params.code = code;
+    params.httpStatus = httpStatus;
+    params.message = message;
+
+    if (context->InvokeService(context, _OrthancPluginService_RegisterErrorCode, &params) == OrthancPluginErrorCode_Success)
+    {
+      return target;
+    }
+    else
+    {
+      /* There was an error while assigned the error. Use a generic code. */
+      return OrthancPluginErrorCode_Plugin;
+    }
+  }
+
+
+
+  typedef struct
+  {
+    uint16_t                          group;
+    uint16_t                          element;
+    OrthancPluginValueRepresentation  vr;
+    const char*                       name;
+    uint32_t                          minMultiplicity;
+    uint32_t                          maxMultiplicity;
+  } _OrthancPluginRegisterDictionaryTag;
+  
+  /**
+   * @brief Register a new tag into the DICOM dictionary.
+   *
+   * This function declares a new tag in the dictionary of DICOM tags
+   * that is known to Orthanc. This function should be used in the
+   * OrthancPluginInitialize() callback.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param group The group of the tag.
+   * @param element The element of the tag.
+   * @param vr The value representation of the tag.
+   * @param name The nickname of the tag.
+   * @param minMultiplicity The minimum multiplicity of the tag (must be above 0).
+   * @param maxMultiplicity The maximum multiplicity of the tag. A value of 0 means
+   * an arbitrary multiplicity ("<tt>n</tt>").
+   * @return 0 if success, other value if error.
+   * @ingroup Toolbox
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginRegisterDictionaryTag(
+    OrthancPluginContext*             context,
+    uint16_t                          group,
+    uint16_t                          element,
+    OrthancPluginValueRepresentation  vr,
+    const char*                       name,
+    uint32_t                          minMultiplicity,
+    uint32_t                          maxMultiplicity)
+  {
+    _OrthancPluginRegisterDictionaryTag params;
+    params.group = group;
+    params.element = element;
+    params.vr = vr;
+    params.name = name;
+    params.minMultiplicity = minMultiplicity;
+    params.maxMultiplicity = maxMultiplicity;
+
+    return context->InvokeService(context, _OrthancPluginService_RegisterDictionaryTag, &params);
+  }
+
+
+#ifdef  __cplusplus
+}
+#endif
+
+
+/** @} */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Include/orthanc/OrthancCppDatabasePlugin.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,1860 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+
+#pragma once
+
+#include "OrthancCDatabasePlugin.h"
+
+#include <stdexcept>
+#include <list>
+#include <string>
+
+namespace OrthancPlugins
+{
+//! @cond Doxygen_Suppress
+  // This class mimics "boost::noncopyable"
+  class NonCopyable
+  {
+  private:
+    NonCopyable(const NonCopyable&);
+
+    NonCopyable& operator= (const NonCopyable&);
+
+  protected:
+    NonCopyable()
+    {
+    }
+
+    ~NonCopyable()
+    {
+    }
+  };
+//! @endcond
+
+
+  /**
+   * @ingroup Callbacks
+   **/
+  class DatabaseException
+  {
+  private:
+    OrthancPluginErrorCode  code_;
+
+  public:
+    DatabaseException() : code_(OrthancPluginErrorCode_DatabasePlugin)
+    {
+    }
+
+    DatabaseException(OrthancPluginErrorCode code) : code_(code)
+    {
+    }
+
+    OrthancPluginErrorCode  GetErrorCode() const
+    {
+      return code_;
+    }
+  };
+
+
+  /**
+   * @ingroup Callbacks
+   **/
+  class DatabaseBackendOutput : public NonCopyable
+  {
+    friend class DatabaseBackendAdapter;
+
+  private:
+    enum AllowedAnswers
+    {
+      AllowedAnswers_All,
+      AllowedAnswers_None,
+      AllowedAnswers_Attachment,
+      AllowedAnswers_Change,
+      AllowedAnswers_DicomTag,
+      AllowedAnswers_ExportedResource
+    };
+
+    OrthancPluginContext*         context_;
+    OrthancPluginDatabaseContext* database_;
+    AllowedAnswers                allowedAnswers_;
+
+    void SetAllowedAnswers(AllowedAnswers allowed)
+    {
+      allowedAnswers_ = allowed;
+    }
+
+  public:
+    DatabaseBackendOutput(OrthancPluginContext*         context,
+                          OrthancPluginDatabaseContext* database) :
+      context_(context),
+      database_(database),
+      allowedAnswers_(AllowedAnswers_All /* for unit tests */)
+    {
+    }
+
+    void LogError(const std::string& message)
+    {
+      OrthancPluginLogError(context_, message.c_str());
+    }
+
+    void LogWarning(const std::string& message)
+    {
+      OrthancPluginLogWarning(context_, message.c_str());
+    }
+
+    void LogInfo(const std::string& message)
+    {
+      OrthancPluginLogInfo(context_, message.c_str());
+    }
+
+    void SignalDeletedAttachment(const std::string& uuid,
+                                 int32_t            contentType,
+                                 uint64_t           uncompressedSize,
+                                 const std::string& uncompressedHash,
+                                 int32_t            compressionType,
+                                 uint64_t           compressedSize,
+                                 const std::string& compressedHash)
+    {
+      OrthancPluginAttachment attachment;
+      attachment.uuid = uuid.c_str();
+      attachment.contentType = contentType;
+      attachment.uncompressedSize = uncompressedSize;
+      attachment.uncompressedHash = uncompressedHash.c_str();
+      attachment.compressionType = compressionType;
+      attachment.compressedSize = compressedSize;
+      attachment.compressedHash = compressedHash.c_str();
+
+      OrthancPluginDatabaseSignalDeletedAttachment(context_, database_, &attachment);
+    }
+
+    void SignalDeletedResource(const std::string& publicId,
+                               OrthancPluginResourceType resourceType)
+    {
+      OrthancPluginDatabaseSignalDeletedResource(context_, database_, publicId.c_str(), resourceType);
+    }
+
+    void SignalRemainingAncestor(const std::string& ancestorId,
+                                 OrthancPluginResourceType ancestorType)
+    {
+      OrthancPluginDatabaseSignalRemainingAncestor(context_, database_, ancestorId.c_str(), ancestorType);
+    }
+
+    void AnswerAttachment(const std::string& uuid,
+                          int32_t            contentType,
+                          uint64_t           uncompressedSize,
+                          const std::string& uncompressedHash,
+                          int32_t            compressionType,
+                          uint64_t           compressedSize,
+                          const std::string& compressedHash)
+    {
+      if (allowedAnswers_ != AllowedAnswers_All &&
+          allowedAnswers_ != AllowedAnswers_Attachment)
+      {
+        throw std::runtime_error("Cannot answer with an attachment in the current state");
+      }
+
+      OrthancPluginAttachment attachment;
+      attachment.uuid = uuid.c_str();
+      attachment.contentType = contentType;
+      attachment.uncompressedSize = uncompressedSize;
+      attachment.uncompressedHash = uncompressedHash.c_str();
+      attachment.compressionType = compressionType;
+      attachment.compressedSize = compressedSize;
+      attachment.compressedHash = compressedHash.c_str();
+
+      OrthancPluginDatabaseAnswerAttachment(context_, database_, &attachment);
+    }
+
+    void AnswerChange(int64_t                    seq,
+                      int32_t                    changeType,
+                      OrthancPluginResourceType  resourceType,
+                      const std::string&         publicId,
+                      const std::string&         date)
+    {
+      if (allowedAnswers_ != AllowedAnswers_All &&
+          allowedAnswers_ != AllowedAnswers_Change)
+      {
+        throw std::runtime_error("Cannot answer with a change in the current state");
+      }
+
+      OrthancPluginChange change;
+      change.seq = seq;
+      change.changeType = changeType;
+      change.resourceType = resourceType;
+      change.publicId = publicId.c_str();
+      change.date = date.c_str();
+
+      OrthancPluginDatabaseAnswerChange(context_, database_, &change);
+    }
+
+    void AnswerDicomTag(uint16_t group,
+                        uint16_t element,
+                        const std::string& value)
+    {
+      if (allowedAnswers_ != AllowedAnswers_All &&
+          allowedAnswers_ != AllowedAnswers_DicomTag)
+      {
+        throw std::runtime_error("Cannot answer with a DICOM tag in the current state");
+      }
+
+      OrthancPluginDicomTag tag;
+      tag.group = group;
+      tag.element = element;
+      tag.value = value.c_str();
+
+      OrthancPluginDatabaseAnswerDicomTag(context_, database_, &tag);
+    }
+
+    void AnswerExportedResource(int64_t                    seq,
+                                OrthancPluginResourceType  resourceType,
+                                const std::string&         publicId,
+                                const std::string&         modality,
+                                const std::string&         date,
+                                const std::string&         patientId,
+                                const std::string&         studyInstanceUid,
+                                const std::string&         seriesInstanceUid,
+                                const std::string&         sopInstanceUid)
+    {
+      if (allowedAnswers_ != AllowedAnswers_All &&
+          allowedAnswers_ != AllowedAnswers_ExportedResource)
+      {
+        throw std::runtime_error("Cannot answer with an exported resource in the current state");
+      }
+
+      OrthancPluginExportedResource exported;
+      exported.seq = seq;
+      exported.resourceType = resourceType;
+      exported.publicId = publicId.c_str();
+      exported.modality = modality.c_str();
+      exported.date = date.c_str();
+      exported.patientId = patientId.c_str();
+      exported.studyInstanceUid = studyInstanceUid.c_str();
+      exported.seriesInstanceUid = seriesInstanceUid.c_str();
+      exported.sopInstanceUid = sopInstanceUid.c_str();
+
+      OrthancPluginDatabaseAnswerExportedResource(context_, database_, &exported);
+    }
+  };
+
+
+  /**
+   * @ingroup Callbacks
+   **/
+  class IDatabaseBackend : public NonCopyable
+  {
+    friend class DatabaseBackendAdapter;
+
+  private:
+    DatabaseBackendOutput*  output_;
+
+    void Finalize()
+    {
+      if (output_ != NULL)
+      {
+        delete output_;
+        output_ = NULL;
+      }
+    }
+
+  protected:
+    DatabaseBackendOutput& GetOutput()
+    {
+      return *output_;
+    }
+
+  public:
+    IDatabaseBackend() : output_(NULL)
+    {
+    }
+
+    virtual ~IDatabaseBackend()
+    {
+      Finalize();
+    }
+
+    // This takes the ownership
+    void RegisterOutput(DatabaseBackendOutput* output)
+    {
+      Finalize();
+      output_ = output;
+    }
+
+    virtual void Open() = 0;
+
+    virtual void Close() = 0;
+
+    virtual void AddAttachment(int64_t id,
+                               const OrthancPluginAttachment& attachment) = 0;
+
+    virtual void AttachChild(int64_t parent,
+                             int64_t child) = 0;
+
+    virtual void ClearChanges() = 0;
+
+    virtual void ClearExportedResources() = 0;
+
+    virtual int64_t CreateResource(const char* publicId,
+                                   OrthancPluginResourceType type) = 0;
+
+    virtual void DeleteAttachment(int64_t id,
+                                  int32_t attachment) = 0;
+
+    virtual void DeleteMetadata(int64_t id,
+                                int32_t metadataType) = 0;
+
+    virtual void DeleteResource(int64_t id) = 0;
+
+    virtual void GetAllPublicIds(std::list<std::string>& target,
+                                 OrthancPluginResourceType resourceType) = 0;
+
+    virtual void GetAllPublicIds(std::list<std::string>& target,
+                                 OrthancPluginResourceType resourceType,
+                                 uint64_t since,
+                                 uint64_t limit) = 0;
+
+    /* Use GetOutput().AnswerChange() */
+    virtual void GetChanges(bool& done /*out*/,
+                            int64_t since,
+                            uint32_t maxResults) = 0;
+
+    virtual void GetChildrenInternalId(std::list<int64_t>& target /*out*/,
+                                       int64_t id) = 0;
+
+    virtual void GetChildrenPublicId(std::list<std::string>& target /*out*/,
+                                     int64_t id) = 0;
+
+    /* Use GetOutput().AnswerExportedResource() */
+    virtual void GetExportedResources(bool& done /*out*/,
+                                      int64_t since,
+                                      uint32_t maxResults) = 0;
+
+    /* Use GetOutput().AnswerChange() */
+    virtual void GetLastChange() = 0;
+
+    /* Use GetOutput().AnswerExportedResource() */
+    virtual void GetLastExportedResource() = 0;
+
+    /* Use GetOutput().AnswerDicomTag() */
+    virtual void GetMainDicomTags(int64_t id) = 0;
+
+    virtual std::string GetPublicId(int64_t resourceId) = 0;
+
+    virtual uint64_t GetResourceCount(OrthancPluginResourceType resourceType) = 0;
+
+    virtual OrthancPluginResourceType GetResourceType(int64_t resourceId) = 0;
+
+    virtual uint64_t GetTotalCompressedSize() = 0;
+    
+    virtual uint64_t GetTotalUncompressedSize() = 0;
+
+    virtual bool IsExistingResource(int64_t internalId) = 0;
+
+    virtual bool IsProtectedPatient(int64_t internalId) = 0;
+
+    virtual void ListAvailableMetadata(std::list<int32_t>& target /*out*/,
+                                       int64_t id) = 0;
+
+    virtual void ListAvailableAttachments(std::list<int32_t>& target /*out*/,
+                                          int64_t id) = 0;
+
+    virtual void LogChange(const OrthancPluginChange& change) = 0;
+
+    virtual void LogExportedResource(const OrthancPluginExportedResource& resource) = 0;
+    
+    /* Use GetOutput().AnswerAttachment() */
+    virtual bool LookupAttachment(int64_t id,
+                                  int32_t contentType) = 0;
+
+    virtual bool LookupGlobalProperty(std::string& target /*out*/,
+                                      int32_t property) = 0;
+
+    /**
+     * "Identifiers" are necessarily one of the following tags:
+     * PatientID (0x0010, 0x0020), StudyInstanceUID (0x0020, 0x000d),
+     * SeriesInstanceUID (0x0020, 0x000e), SOPInstanceUID (0x0008,
+     * 0x0018) or AccessionNumber (0x0008, 0x0050).
+     **/
+    virtual void LookupIdentifier(std::list<int64_t>& target /*out*/,
+                                  uint16_t group,
+                                  uint16_t element,
+                                  const char* value) = 0;
+
+    virtual void LookupIdentifier(std::list<int64_t>& target /*out*/,
+                                  const char* value) = 0;
+
+    virtual bool LookupMetadata(std::string& target /*out*/,
+                                int64_t id,
+                                int32_t metadataType) = 0;
+
+    virtual bool LookupParent(int64_t& parentId /*out*/,
+                              int64_t resourceId) = 0;
+
+    virtual bool LookupResource(int64_t& id /*out*/,
+                                OrthancPluginResourceType& type /*out*/,
+                                const char* publicId) = 0;
+
+    virtual bool SelectPatientToRecycle(int64_t& internalId /*out*/) = 0;
+
+    virtual bool SelectPatientToRecycle(int64_t& internalId /*out*/,
+                                        int64_t patientIdToAvoid) = 0;
+
+    virtual void SetGlobalProperty(int32_t property,
+                                   const char* value) = 0;
+
+    virtual void SetMainDicomTag(int64_t id,
+                                 uint16_t group,
+                                 uint16_t element,
+                                 const char* value) = 0;
+
+    virtual void SetIdentifierTag(int64_t id,
+                                  uint16_t group,
+                                  uint16_t element,
+                                  const char* value) = 0;
+
+    virtual void SetMetadata(int64_t id,
+                             int32_t metadataType,
+                             const char* value) = 0;
+
+    virtual void SetProtectedPatient(int64_t internalId, 
+                                     bool isProtected) = 0;
+
+    virtual void StartTransaction() = 0;
+
+    virtual void RollbackTransaction() = 0;
+
+    virtual void CommitTransaction() = 0;
+
+    virtual uint32_t GetDatabaseVersion() = 0;
+
+    virtual void UpgradeDatabase(uint32_t  targetVersion,
+                                 OrthancPluginStorageArea* storageArea) = 0;
+  };
+
+
+
+  /**
+   * @brief Bridge between C and C++ database engines.
+   * 
+   * Class creating the bridge between the C low-level primitives for
+   * custom database engines, and the high-level IDatabaseBackend C++
+   * interface.
+   *
+   * @ingroup Callbacks
+   **/
+  class DatabaseBackendAdapter
+  {
+  private:
+    // This class cannot be instantiated
+    DatabaseBackendAdapter()
+    {
+    }
+
+    static void LogError(IDatabaseBackend* backend,
+                         const std::runtime_error& e)
+    {
+      backend->GetOutput().LogError("Exception in database back-end: " + std::string(e.what()));
+    }
+
+
+    static OrthancPluginErrorCode  AddAttachment(void* payload,
+                                                 int64_t id,
+                                                 const OrthancPluginAttachment* attachment)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->AddAttachment(id, *attachment);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+                             
+    static OrthancPluginErrorCode  AttachChild(void* payload,
+                                               int64_t parent,
+                                               int64_t child)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->AttachChild(parent, child);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+          
+                   
+    static OrthancPluginErrorCode  ClearChanges(void* payload)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->ClearChanges();
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+                             
+
+    static OrthancPluginErrorCode  ClearExportedResources(void* payload)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->ClearExportedResources();
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  CreateResource(int64_t* id, 
+                                                  void* payload,
+                                                  const char* publicId,
+                                                  OrthancPluginResourceType resourceType)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        *id = backend->CreateResource(publicId, resourceType);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+          
+         
+    static OrthancPluginErrorCode  DeleteAttachment(void* payload,
+                                                    int64_t id,
+                                                    int32_t contentType)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->DeleteAttachment(id, contentType);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+   
+
+    static OrthancPluginErrorCode  DeleteMetadata(void* payload,
+                                                  int64_t id,
+                                                  int32_t metadataType)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->DeleteMetadata(id, metadataType);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+   
+
+    static OrthancPluginErrorCode  DeleteResource(void* payload,
+                                                  int64_t id)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->DeleteResource(id);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  GetAllPublicIds(OrthancPluginDatabaseContext* context,
+                                                   void* payload,
+                                                   OrthancPluginResourceType resourceType)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        std::list<std::string> ids;
+        backend->GetAllPublicIds(ids, resourceType);
+
+        for (std::list<std::string>::const_iterator
+               it = ids.begin(); it != ids.end(); ++it)
+        {
+          OrthancPluginDatabaseAnswerString(backend->GetOutput().context_,
+                                            backend->GetOutput().database_,
+                                            it->c_str());
+        }
+
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  GetAllPublicIdsWithLimit(OrthancPluginDatabaseContext* context,
+                                                            void* payload,
+                                                            OrthancPluginResourceType resourceType,
+                                                            uint64_t since,
+                                                            uint64_t limit)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        std::list<std::string> ids;
+        backend->GetAllPublicIds(ids, resourceType, since, limit);
+
+        for (std::list<std::string>::const_iterator
+               it = ids.begin(); it != ids.end(); ++it)
+        {
+          OrthancPluginDatabaseAnswerString(backend->GetOutput().context_,
+                                            backend->GetOutput().database_,
+                                            it->c_str());
+        }
+
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  GetChanges(OrthancPluginDatabaseContext* context,
+                                              void* payload,
+                                              int64_t since,
+                                              uint32_t maxResult)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_Change);
+
+      try
+      {
+        bool done;
+        backend->GetChanges(done, since, maxResult);
+        
+        if (done)
+        {
+          OrthancPluginDatabaseAnswerChangesDone(backend->GetOutput().context_,
+                                                 backend->GetOutput().database_);
+        }
+
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  GetChildrenInternalId(OrthancPluginDatabaseContext* context,
+                                                         void* payload,
+                                                         int64_t id)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        std::list<int64_t> target;
+        backend->GetChildrenInternalId(target, id);
+
+        for (std::list<int64_t>::const_iterator
+               it = target.begin(); it != target.end(); ++it)
+        {
+          OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_,
+                                           backend->GetOutput().database_, *it);
+        }
+
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+          
+         
+    static OrthancPluginErrorCode  GetChildrenPublicId(OrthancPluginDatabaseContext* context,
+                                                       void* payload,
+                                                       int64_t id)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        std::list<std::string> ids;
+        backend->GetChildrenPublicId(ids, id);
+
+        for (std::list<std::string>::const_iterator
+               it = ids.begin(); it != ids.end(); ++it)
+        {
+          OrthancPluginDatabaseAnswerString(backend->GetOutput().context_,
+                                            backend->GetOutput().database_,
+                                            it->c_str());
+        }
+
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  GetExportedResources(OrthancPluginDatabaseContext* context,
+                                                        void* payload,
+                                                        int64_t  since,
+                                                        uint32_t  maxResult)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_ExportedResource);
+
+      try
+      {
+        bool done;
+        backend->GetExportedResources(done, since, maxResult);
+
+        if (done)
+        {
+          OrthancPluginDatabaseAnswerExportedResourcesDone(backend->GetOutput().context_,
+                                                           backend->GetOutput().database_);
+        }
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+          
+         
+    static OrthancPluginErrorCode  GetLastChange(OrthancPluginDatabaseContext* context,
+                                                 void* payload)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_Change);
+
+      try
+      {
+        backend->GetLastChange();
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  GetLastExportedResource(OrthancPluginDatabaseContext* context,
+                                                           void* payload)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_ExportedResource);
+
+      try
+      {
+        backend->GetLastExportedResource();
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+    
+               
+    static OrthancPluginErrorCode  GetMainDicomTags(OrthancPluginDatabaseContext* context,
+                                                    void* payload,
+                                                    int64_t id)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_DicomTag);
+
+      try
+      {
+        backend->GetMainDicomTags(id);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+          
+         
+    static OrthancPluginErrorCode  GetPublicId(OrthancPluginDatabaseContext* context,
+                                               void* payload,
+                                               int64_t id)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        std::string s = backend->GetPublicId(id);
+        OrthancPluginDatabaseAnswerString(backend->GetOutput().context_,
+                                          backend->GetOutput().database_,
+                                          s.c_str());
+
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  GetResourceCount(uint64_t* target,
+                                                    void* payload,
+                                                    OrthancPluginResourceType  resourceType)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        *target = backend->GetResourceCount(resourceType);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+                   
+
+    static OrthancPluginErrorCode  GetResourceType(OrthancPluginResourceType* resourceType,
+                                                   void* payload,
+                                                   int64_t id)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        *resourceType = backend->GetResourceType(id);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  GetTotalCompressedSize(uint64_t* target,
+                                                          void* payload)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        *target = backend->GetTotalCompressedSize();
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+          
+         
+    static OrthancPluginErrorCode  GetTotalUncompressedSize(uint64_t* target,
+                                                            void* payload)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        *target = backend->GetTotalUncompressedSize();
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+                   
+
+    static OrthancPluginErrorCode  IsExistingResource(int32_t* existing,
+                                                      void* payload,
+                                                      int64_t id)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        *existing = backend->IsExistingResource(id);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  IsProtectedPatient(int32_t* isProtected,
+                                                      void* payload,
+                                                      int64_t id)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        *isProtected = backend->IsProtectedPatient(id);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  ListAvailableMetadata(OrthancPluginDatabaseContext* context,
+                                                         void* payload,
+                                                         int64_t id)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        std::list<int32_t> target;
+        backend->ListAvailableMetadata(target, id);
+
+        for (std::list<int32_t>::const_iterator
+               it = target.begin(); it != target.end(); ++it)
+        {
+          OrthancPluginDatabaseAnswerInt32(backend->GetOutput().context_,
+                                           backend->GetOutput().database_,
+                                           *it);
+        }
+
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+          
+         
+    static OrthancPluginErrorCode  ListAvailableAttachments(OrthancPluginDatabaseContext* context,
+                                                            void* payload,
+                                                            int64_t id)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        std::list<int32_t> target;
+        backend->ListAvailableAttachments(target, id);
+
+        for (std::list<int32_t>::const_iterator
+               it = target.begin(); it != target.end(); ++it)
+        {
+          OrthancPluginDatabaseAnswerInt32(backend->GetOutput().context_,
+                                           backend->GetOutput().database_,
+                                           *it);
+        }
+
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  LogChange(void* payload,
+                                             const OrthancPluginChange* change)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->LogChange(*change);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+          
+         
+    static OrthancPluginErrorCode  LogExportedResource(void* payload,
+                                                       const OrthancPluginExportedResource* exported)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->LogExportedResource(*exported);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+          
+         
+    static OrthancPluginErrorCode  LookupAttachment(OrthancPluginDatabaseContext* context,
+                                                    void* payload,
+                                                    int64_t id,
+                                                    int32_t contentType)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_Attachment);
+
+      try
+      {
+        backend->LookupAttachment(id, contentType);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  LookupGlobalProperty(OrthancPluginDatabaseContext* context,
+                                                        void* payload,
+                                                        int32_t property)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        std::string s;
+        if (backend->LookupGlobalProperty(s, property))
+        {
+          OrthancPluginDatabaseAnswerString(backend->GetOutput().context_,
+                                            backend->GetOutput().database_,
+                                            s.c_str());
+        }
+
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  LookupIdentifier(OrthancPluginDatabaseContext* context,
+                                                    void* payload,
+                                                    const OrthancPluginDicomTag* tag)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        std::list<int64_t> target;
+        backend->LookupIdentifier(target, tag->group, tag->element, tag->value);
+
+        for (std::list<int64_t>::const_iterator
+               it = target.begin(); it != target.end(); ++it)
+        {
+          OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_,
+                                           backend->GetOutput().database_, *it);
+        }
+
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  LookupIdentifier2(OrthancPluginDatabaseContext* context,
+                                                     void* payload,
+                                                     const char* value)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        std::list<int64_t> target;
+        backend->LookupIdentifier(target, value);
+
+        for (std::list<int64_t>::const_iterator
+               it = target.begin(); it != target.end(); ++it)
+        {
+          OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_,
+                                           backend->GetOutput().database_, *it);
+        }
+
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  LookupMetadata(OrthancPluginDatabaseContext* context,
+                                                  void* payload,
+                                                  int64_t id,
+                                                  int32_t metadata)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        std::string s;
+        if (backend->LookupMetadata(s, id, metadata))
+        {
+          OrthancPluginDatabaseAnswerString(backend->GetOutput().context_,
+                                            backend->GetOutput().database_, s.c_str());
+        }
+
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  LookupParent(OrthancPluginDatabaseContext* context,
+                                                void* payload,
+                                                int64_t id)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        int64_t parent;
+        if (backend->LookupParent(parent, id))
+        {
+          OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_,
+                                           backend->GetOutput().database_, parent);
+        }
+
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  LookupResource(OrthancPluginDatabaseContext* context,
+                                                  void* payload,
+                                                  const char* publicId)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        int64_t id;
+        OrthancPluginResourceType type;
+        if (backend->LookupResource(id, type, publicId))
+        {
+          OrthancPluginDatabaseAnswerResource(backend->GetOutput().context_,
+                                              backend->GetOutput().database_, 
+                                              id, type);
+        }
+
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  SelectPatientToRecycle(OrthancPluginDatabaseContext* context,
+                                                          void* payload)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        int64_t id;
+        if (backend->SelectPatientToRecycle(id))
+        {
+          OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_,
+                                           backend->GetOutput().database_, id);
+        }
+
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  SelectPatientToRecycle2(OrthancPluginDatabaseContext* context,
+                                                           void* payload,
+                                                           int64_t patientIdToAvoid)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        int64_t id;
+        if (backend->SelectPatientToRecycle(id, patientIdToAvoid))
+        {
+          OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_,
+                                           backend->GetOutput().database_, id);
+        }
+
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  SetGlobalProperty(void* payload,
+                                                     int32_t property,
+                                                     const char* value)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->SetGlobalProperty(property, value);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  SetMainDicomTag(void* payload,
+                                                   int64_t id,
+                                                   const OrthancPluginDicomTag* tag)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->SetMainDicomTag(id, tag->group, tag->element, tag->value);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  SetIdentifierTag(void* payload,
+                                                    int64_t id,
+                                                    const OrthancPluginDicomTag* tag)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->SetIdentifierTag(id, tag->group, tag->element, tag->value);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  SetMetadata(void* payload,
+                                               int64_t id,
+                                               int32_t metadata,
+                                               const char* value)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->SetMetadata(id, metadata, value);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode  SetProtectedPatient(void* payload,
+                                                       int64_t id,
+                                                       int32_t isProtected)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->SetProtectedPatient(id, (isProtected != 0));
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode StartTransaction(void* payload)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->StartTransaction();
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode RollbackTransaction(void* payload)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->RollbackTransaction();
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode CommitTransaction(void* payload)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->CommitTransaction();
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode Open(void* payload)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->Open();
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode Close(void* payload)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
+
+      try
+      {
+        backend->Close();
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode GetDatabaseVersion(uint32_t* version,
+                                                     void* payload)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      
+      try
+      {
+        *version = backend->GetDatabaseVersion();
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+
+    static OrthancPluginErrorCode UpgradeDatabase(void* payload,
+                                                  uint32_t  targetVersion,
+                                                  OrthancPluginStorageArea* storageArea)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      
+      try
+      {
+        backend->UpgradeDatabase(targetVersion, storageArea);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (std::runtime_error& e)
+      {
+        LogError(backend, e);
+        return OrthancPluginErrorCode_DatabasePlugin;
+      }
+      catch (DatabaseException& e)
+      {
+        return e.GetErrorCode();
+      }
+    }
+
+    
+  public:
+    /**
+     * Register a custom database back-end written in C++.
+     *
+     * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+     * @param backend Your custom database engine.
+     **/
+
+    static void Register(OrthancPluginContext* context,
+                         IDatabaseBackend& backend)
+    {
+      OrthancPluginDatabaseBackend  params;
+      memset(&params, 0, sizeof(params));
+
+      OrthancPluginDatabaseExtensions  extensions;
+      memset(&extensions, 0, sizeof(extensions));
+
+      params.addAttachment = AddAttachment;
+      params.attachChild = AttachChild;
+      params.clearChanges = ClearChanges;
+      params.clearExportedResources = ClearExportedResources;
+      params.createResource = CreateResource;
+      params.deleteAttachment = DeleteAttachment;
+      params.deleteMetadata = DeleteMetadata;
+      params.deleteResource = DeleteResource;
+      params.getAllPublicIds = GetAllPublicIds;
+      params.getChanges = GetChanges;
+      params.getChildrenInternalId = GetChildrenInternalId;
+      params.getChildrenPublicId = GetChildrenPublicId;
+      params.getExportedResources = GetExportedResources;
+      params.getLastChange = GetLastChange;
+      params.getLastExportedResource = GetLastExportedResource;
+      params.getMainDicomTags = GetMainDicomTags;
+      params.getPublicId = GetPublicId;
+      params.getResourceCount = GetResourceCount;
+      params.getResourceType = GetResourceType;
+      params.getTotalCompressedSize = GetTotalCompressedSize;
+      params.getTotalUncompressedSize = GetTotalUncompressedSize;
+      params.isExistingResource = IsExistingResource;
+      params.isProtectedPatient = IsProtectedPatient;
+      params.listAvailableMetadata = ListAvailableMetadata;
+      params.listAvailableAttachments = ListAvailableAttachments;
+      params.logChange = LogChange;
+      params.logExportedResource = LogExportedResource;
+      params.lookupAttachment = LookupAttachment;
+      params.lookupGlobalProperty = LookupGlobalProperty;
+      params.lookupIdentifier = LookupIdentifier;
+      params.lookupIdentifier2 = LookupIdentifier2;
+      params.lookupMetadata = LookupMetadata;
+      params.lookupParent = LookupParent;
+      params.lookupResource = LookupResource;
+      params.selectPatientToRecycle = SelectPatientToRecycle;
+      params.selectPatientToRecycle2 = SelectPatientToRecycle2;
+      params.setGlobalProperty = SetGlobalProperty;
+      params.setMainDicomTag = SetMainDicomTag;
+      params.setIdentifierTag = SetIdentifierTag;
+      params.setMetadata = SetMetadata;
+      params.setProtectedPatient = SetProtectedPatient;
+      params.startTransaction = StartTransaction;
+      params.rollbackTransaction = RollbackTransaction;
+      params.commitTransaction = CommitTransaction;
+      params.open = Open;
+      params.close = Close;
+
+      extensions.getAllPublicIdsWithLimit = GetAllPublicIdsWithLimit;
+      extensions.getDatabaseVersion = GetDatabaseVersion;
+      extensions.upgradeDatabase = UpgradeDatabase;
+
+      OrthancPluginDatabaseContext* database = OrthancPluginRegisterDatabaseBackendV2(context, &params, &extensions, &backend);
+      if (!context)
+      {
+        throw std::runtime_error("Unable to register the database backend");
+      }
+
+      backend.RegisterOutput(new DatabaseBackendOutput(context, database));
+    }
+  };
+}
--- a/Plugins/Samples/Basic/CMakeLists.txt	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Samples/Basic/CMakeLists.txt	Wed Sep 30 13:23:31 2015 +0200
@@ -2,16 +2,7 @@
 
 project(Basic)
 
-if (${CMAKE_COMPILER_IS_GNUCXX})
-  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic -Werror")
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Werror")
-endif()
+set(SAMPLES_ROOT ${CMAKE_SOURCE_DIR}/..)
+include(${SAMPLES_ROOT}/Common/OrthancPlugins.cmake)
 
-include_directories(${CMAKE_SOURCE_DIR}/../../OrthancCPlugin/)
 add_library(PluginTest SHARED Plugin.c)
-
-if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
-  # Linking with "pthread" is necessary, otherwise the software crashes
-  # http://sourceware.org/bugzilla/show_bug.cgi?id=10652#c17
-  target_link_libraries(PluginTest pthread dl)
-endif()
--- a/Plugins/Samples/Basic/Plugin.c	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Samples/Basic/Plugin.c	Wed Sep 30 13:23:31 2015 +0200
@@ -3,35 +3,30 @@
  * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
  * Department, University Hospital of Liege, Belgium
  *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU 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
+ * General Public License for more details.
  *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
 
 
-#include <OrthancCPlugin.h>
+#include <orthanc/OrthancCPlugin.h>
 
 #include <string.h>
 #include <stdio.h>
 
 static OrthancPluginContext* context = NULL;
 
+static OrthancPluginErrorCode customError;
+
 
 ORTHANC_PLUGINS_API int32_t Callback1(OrthancPluginRestOutput* output,
                                       const char* url,
@@ -217,9 +212,9 @@
 }
 
 
-ORTHANC_PLUGINS_API int32_t CallbackCreateDicom(OrthancPluginRestOutput* output,
-                                                const char* url,
-                                                const OrthancPluginHttpRequest* request)
+ORTHANC_PLUGINS_API OrthancPluginErrorCode CallbackCreateDicom(OrthancPluginRestOutput* output,
+                                                               const char* url,
+                                                               const OrthancPluginHttpRequest* request)
 {
   const char* pathLocator = "\"Path\" : \"";
   char info[1024];
@@ -257,12 +252,12 @@
     OrthancPluginAnswerBuffer(context, output, "OK\n", 3, "text/plain");
   }
 
-  return 0;
+  return OrthancPluginErrorCode_Success;
 }
 
 
-ORTHANC_PLUGINS_API int32_t OnStoredCallback(OrthancPluginDicomInstance* instance,
-                                             const char* instanceId)
+ORTHANC_PLUGINS_API OrthancPluginErrorCode OnStoredCallback(OrthancPluginDicomInstance* instance,
+                                                            const char* instanceId)
 {
   char buffer[256];
   FILE* fp;
@@ -298,13 +293,13 @@
     OrthancPluginLogError(context, "Instance has no reception date, should never happen!");
   }
 
-  return 0;
+  return OrthancPluginErrorCode_Success;
 }
 
 
-ORTHANC_PLUGINS_API int32_t OnChangeCallback(OrthancPluginChangeType changeType,
-                                             OrthancPluginResourceType resourceType,
-                                             const char* resourceId)
+ORTHANC_PLUGINS_API OrthancPluginErrorCode OnChangeCallback(OrthancPluginChangeType changeType,
+                                                            OrthancPluginResourceType resourceType,
+                                                            const char* resourceId)
 {
   char info[1024];
   OrthancPluginMemoryBuffer tmp;
@@ -324,7 +319,7 @@
     }
   }
 
-  return 0;
+  return OrthancPluginErrorCode_Success;
 }
 
 
@@ -363,9 +358,10 @@
   OrthancPluginLogWarning(context, info);
   OrthancPluginFreeString(context, s);
 
-  s = OrthancPluginGetConfigurationPath(context);
-  sprintf(info, "  Path to configuration file: %s", s);
+  s = OrthancPluginGetConfiguration(context);
+  sprintf(info, "  Content of the configuration file:\n");
   OrthancPluginLogWarning(context, info);
+  OrthancPluginLogWarning(context, s);
   OrthancPluginFreeString(context, s);
 
   /* Print the command-line arguments of Orthanc */
@@ -397,6 +393,7 @@
   OrthancPluginExtendOrthancExplorer(context, "alert('Hello Orthanc! From sample plugin with love.');");
 
   /* Make REST requests to the built-in Orthanc API */
+  memset(&tmp, 0, sizeof(tmp));
   OrthancPluginRestApiGet(context, &tmp, "/changes");
   OrthancPluginFreeMemoryBuffer(context, &tmp);
   OrthancPluginRestApiGet(context, &tmp, "/changes?limit=1");
@@ -406,16 +403,10 @@
   sprintf(info, "[ \"STORESCP\", \"localhost\", 2000 ]");
   OrthancPluginRestApiPut(context, &tmp, "/modalities/demo", info, strlen(info));
 
-  /* Play with global properties: A global counter is incremented 
-     each time the plugin starts. */
-  s = OrthancPluginGetGlobalProperty(context, 1024, "0");
-  sscanf(s, "%d", &counter);
-  sprintf(info, "Number of times this plugin was started: %d", counter);
-  OrthancPluginLogWarning(context, info);
-  counter++;
-  sprintf(info, "%d", counter);
-  OrthancPluginSetGlobalProperty(context, 1024, info);
-  OrthancPluginFreeString(context, s);
+  customError = OrthancPluginRegisterErrorCode(context, 4, 402, "Hello world");
+
+  OrthancPluginRegisterDictionaryTag(context, 0x0014, 0x1020, OrthancPluginValueRepresentation_DA,
+                                     "ValidationExpiryDate", 1, 1);
 
   return 0;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Samples/Common/ExportedSymbols.list	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,7 @@
+# This is the list of the symbols that must be exported by Orthanc
+# plugins, if targeting OS X
+
+_OrthancPluginInitialize
+_OrthancPluginFinalize
+_OrthancPluginGetName
+_OrthancPluginGetVersion
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Samples/Common/OrthancPlugins.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,50 @@
+include(CheckIncludeFiles)
+include(CheckLibraryExists)
+
+
+if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+  link_libraries(uuid)
+  SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread")
+  SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -pthread")
+
+elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
+  link_libraries(rpcrt4 ws2_32 secur32)
+  if (CMAKE_COMPILER_IS_GNUCXX)
+    SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libgcc -static-libstdc++")
+  endif()
+
+  CHECK_LIBRARY_EXISTS(winpthread pthread_create "" HAVE_WIN_PTHREAD)
+  if (HAVE_WIN_PTHREAD)
+    # This line is necessary to compile with recent versions of MinGW,
+    # otherwise "libwinpthread-1.dll" is not statically linked.
+    SET(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} -Wl,-Bstatic -lstdc++ -lpthread -Wl,-Bdynamic")
+  endif()
+endif ()
+
+
+if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
+    ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD" OR
+    ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
+  SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--version-script=${SAMPLES_ROOT}/Common/VersionScript.map -Wl,--no-undefined")
+elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
+  SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -exported_symbols_list ${CMAKE_SOURCE_DIR}/Plugins/Samples/Common/ExportedSymbols.list")
+endif()
+
+
+if (CMAKE_COMPILER_IS_GNUCXX)
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic")
+endif()
+
+
+if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+  # Linking with "pthread" is necessary, otherwise the software crashes
+  # http://sourceware.org/bugzilla/show_bug.cgi?id=10652#c17
+  link_libraries(dl rt)
+endif()
+
+include_directories(${SAMPLES_ROOT}/../Include/)
+
+if (MSVC)
+  include_directories(${SAMPLES_ROOT}/../../Resources/ThirdParty/VisualStudio/)
+endif()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Samples/Common/VersionScript.map	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,12 @@
+# This is a version-script for Orthanc plugins
+
+{
+global:
+  OrthancPluginInitialize;
+  OrthancPluginFinalize;
+  OrthancPluginGetName;
+  OrthancPluginGetVersion;
+
+local:
+  *;
+};
--- a/Plugins/Samples/GdcmDecoding/CMakeLists.txt	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Samples/GdcmDecoding/CMakeLists.txt	Wed Sep 30 13:23:31 2015 +0200
@@ -2,21 +2,20 @@
 
 project(GdcmDecoding)
 
-SET(ALLOW_DOWNLOADS ON CACHE BOOL "Allow CMake to download packages")
+SET(ALLOW_DOWNLOADS OFF CACHE BOOL "Allow CMake to download packages")
 SET(USE_SYSTEM_BOOST ON CACHE BOOL "Use the system version of Boost")
-SET(USE_SYSTEM_GOOGLE_LOG OFF CACHE BOOL "Use the system version of Google Log")
+SET(USE_SYSTEM_GOOGLE_LOG ON CACHE BOOL "Use the system version of Google Log")
 
 set(ORTHANC_ROOT ${CMAKE_SOURCE_DIR}/../../..)
+set(SAMPLES_ROOT ${CMAKE_SOURCE_DIR}/..)
 
-if (${CMAKE_COMPILER_IS_GNUCXX})
-  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
-  set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
-endif()
-
+include(CheckIncludeFiles)
+include(CheckIncludeFileCXX)
+include(${CMAKE_SOURCE_DIR}/../Common/OrthancPlugins.cmake)
 include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake)
 include(${ORTHANC_ROOT}/Resources/CMake/BoostConfiguration.cmake)
 include(${ORTHANC_ROOT}/Resources/CMake/GoogleLogConfiguration.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/JsonCppConfiguration.cmake)
 
 find_package(GDCM REQUIRED)
 if (GDCM_FOUND)
@@ -26,11 +25,6 @@
   message(FATAL_ERROR "Cannot find GDCM, did you set GDCM_DIR?")
 endif(GDCM_FOUND)
 
-include_directories(
-  ${ORTHANC_ROOT}/Plugins/OrthancCPlugin/
-  ${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/Laaw
-  )
-
 add_library(GdcmDecoding SHARED
   Plugin.cpp
   OrthancContext.cpp
@@ -39,18 +33,14 @@
   ${GOOGLE_LOG_SOURCES}
   ${ORTHANC_ROOT}/Core/ChunkedBuffer.cpp
   ${ORTHANC_ROOT}/Core/Enumerations.cpp
-  ${ORTHANC_ROOT}/Core/ImageFormats/ImageAccessor.cpp
-  ${ORTHANC_ROOT}/Core/ImageFormats/ImageBuffer.cpp
-  ${ORTHANC_ROOT}/Core/ImageFormats/ImageProcessing.cpp
-  ${ORTHANC_ROOT}/Core/OrthancException.cpp
+  ${ORTHANC_ROOT}/Core/Images/ImageAccessor.cpp
+  ${ORTHANC_ROOT}/Core/Images/ImageBuffer.cpp
+  ${ORTHANC_ROOT}/Core/Images/ImageProcessing.cpp
   ${ORTHANC_ROOT}/Core/Toolbox.cpp
   ${ORTHANC_ROOT}/Resources/ThirdParty/base64/base64.cpp
   ${ORTHANC_ROOT}/Resources/ThirdParty/md5/md5.c
+  ${JSONCPP_SOURCES}
   ${THIRD_PARTY_SOURCES}
   )
 
 target_link_libraries(GdcmDecoding ${GDCM_LIBRARIES})
-
-if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
-  target_link_libraries(GdcmDecoding pthread dl rt)
-endif()
--- a/Plugins/Samples/GdcmDecoding/OrthancContext.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Samples/GdcmDecoding/OrthancContext.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -3,25 +3,18 @@
  * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
  * Department, University Hospital of Liege, Belgium
  *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU 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
+ * General Public License for more details.
  *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
 
 
--- a/Plugins/Samples/GdcmDecoding/OrthancContext.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Samples/GdcmDecoding/OrthancContext.h	Wed Sep 30 13:23:31 2015 +0200
@@ -3,33 +3,26 @@
  * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
  * Department, University Hospital of Liege, Belgium
  *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU 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
+ * General Public License for more details.
  *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
 
 
 #pragma once
 
-#include <OrthancCPlugin.h>
+#include <orthanc/OrthancCPlugin.h>
 
-#include "../../../Core/ImageFormats/ImageBuffer.h"
+#include "../../../Core/Images/ImageBuffer.h"
 
 #include <map>
 #include <string>
--- a/Plugins/Samples/GdcmDecoding/Plugin.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Samples/GdcmDecoding/Plugin.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -3,25 +3,18 @@
  * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
  * Department, University Hospital of Liege, Belgium
  *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU 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
+ * General Public License for more details.
  *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
 
 
@@ -32,7 +25,7 @@
 #include <boost/lexical_cast.hpp>
 
 #include "OrthancContext.h"
-#include "../../../Core/ImageFormats/ImageProcessing.h"
+#include "../../../Core/Images/ImageProcessing.h"
 
 #include <gdcmReader.h>
 #include <gdcmImageReader.h>
@@ -92,9 +85,9 @@
 }
 
 
-ORTHANC_PLUGINS_API int32_t DecodeImage(OrthancPluginRestOutput* output,
-                                        const char* url,
-                                        const OrthancPluginHttpRequest* request)
+ORTHANC_PLUGINS_API OrthancPluginErrorCode DecodeImage(OrthancPluginRestOutput* output,
+                                                       const char* url,
+                                                       const OrthancPluginHttpRequest* request)
 {
   std::string instance(request->groups[0]);
   std::string outputFormat(request->groups[1]);
@@ -114,7 +107,7 @@
   {
     OrthancContext::GetInstance().LogError("GDCM cannot extract an image from this DICOM instance");
     AnswerUnsupportedImage(output);
-    return 0;
+    return OrthancPluginErrorCode_Success;
   }
 
   gdcm::Image& image = imageReader.GetImage();
@@ -145,7 +138,7 @@
   {
     OrthancContext::GetInstance().LogError("This sample plugin does not support this image format");
     AnswerUnsupportedImage(output);
-    return 0;
+    return OrthancPluginErrorCode_Success;
   }
 
   Orthanc::ImageAccessor decodedImage;
@@ -197,7 +190,7 @@
     {
       // Do not convert color images to grayscale values (this is Orthanc convention)
       AnswerUnsupportedImage(output);
-      return 0;
+      return OrthancPluginErrorCode_Success;
     }
 
     if (outputFormat == "image-uint8")
@@ -216,7 +209,7 @@
     {
       OrthancContext::GetInstance().LogError("Unknown output format: " + outputFormat);
       AnswerUnsupportedImage(output);
-      return 0;
+      return OrthancPluginErrorCode_Success;
     }
   }
 
@@ -226,7 +219,7 @@
   // Compress the converted image as a PNG file
   OrthancContext::GetInstance().CompressAndAnswerPngImage(output, convertedAccessor);
 
-  return 0;  // Success
+  return OrthancPluginErrorCode_Success;  // Success
 }
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Samples/ServeFolders/CMakeLists.txt	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,39 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(ServeFolders)
+
+SET(SERVE_FOLDERS_VERSION "0.0" CACHE STRING "Version of the plugin")
+SET(STATIC_BUILD OFF CACHE BOOL "Static build of the third-party libraries (necessary for Windows)")
+SET(ALLOW_DOWNLOADS OFF CACHE BOOL "Allow CMake to download packages")
+SET(USE_SYSTEM_JSONCPP ON CACHE BOOL "Use the system version of JsonCpp")
+SET(USE_SYSTEM_BOOST ON CACHE BOOL "Use the system version of boost")
+
+set(ORTHANC_ROOT ${CMAKE_SOURCE_DIR}/../../../)
+set(SAMPLES_ROOT ${CMAKE_SOURCE_DIR}/..)
+
+include(CheckIncludeFiles)
+include(CheckIncludeFileCXX)
+include(${CMAKE_SOURCE_DIR}/../Common/OrthancPlugins.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/JsonCppConfiguration.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/BoostConfiguration.cmake)
+
+add_library(ServeFolders SHARED 
+  Plugin.cpp
+  ${JSONCPP_SOURCES}
+  ${BOOST_SOURCES}
+  )
+
+
+message("Setting the version of the plugin to ${SERVE_FOLDERS_VERSION}")
+add_definitions(-DSERVE_FOLDERS_VERSION="${SERVE_FOLDERS_VERSION}")
+
+set_target_properties(ServeFolders PROPERTIES 
+  VERSION ${SERVE_FOLDERS_VERSION} 
+  SOVERSION ${SERVE_FOLDERS_VERSION})
+
+install(
+  TARGETS ServeFolders
+  RUNTIME DESTINATION lib    # Destination for Windows
+  LIBRARY DESTINATION share/orthanc/plugins    # Destination for Linux
+  )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Samples/ServeFolders/Plugin.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,385 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include <orthanc/OrthancCPlugin.h>
+
+#include <json/reader.h>
+#include <json/value.h>
+#include <boost/filesystem.hpp>
+
+
+static OrthancPluginContext* context_ = NULL;
+static std::map<std::string, std::string> folders_;
+static const char* INDEX_URI = "/app/plugin-serve-folders.html";
+
+
+static const char* GetMimeType(const std::string& path)
+{
+  size_t dot = path.find_last_of('.');
+
+  std::string extension = (dot == std::string::npos) ? "" : path.substr(dot);
+  std::transform(extension.begin(), extension.end(), extension.begin(), tolower);
+
+  if (extension == ".html")
+  {
+    return "text/html";
+  }
+  else if (extension == ".css")
+  {
+    return "text/css";
+  }
+  else if (extension == ".js")
+  {
+    return "application/javascript";
+  }
+  else if (extension == ".gif")
+  {
+    return "image/gif";
+  }
+  else if (extension == ".svg")
+  {
+    return "image/svg+xml";
+  }
+  else if (extension == ".json")
+  {
+    return "application/json";
+  }
+  else if (extension == ".xml")
+  {
+    return "application/xml";
+  }
+  else if (extension == ".png")
+  {
+    return "image/png";
+  }
+  else if (extension == ".jpg" || extension == ".jpeg")
+  {
+    return "image/jpeg";
+  }
+  else if (extension == ".woff")
+  {
+    return "application/x-font-woff";
+  }
+  else
+  {
+    std::string s = "Unknown MIME type for extension: " + extension;
+    OrthancPluginLogWarning(context_, s.c_str());
+    return "application/octet-stream";
+  }
+}
+
+
+static bool ReadFile(std::string& target,
+                     const std::string& path)
+{
+  OrthancPluginMemoryBuffer buffer;
+  if (OrthancPluginReadFile(context_, &buffer, path.c_str()))
+  {
+    return false;
+  }
+  else
+  {
+    target.assign(reinterpret_cast<const char*>(buffer.data), buffer.size);
+    OrthancPluginFreeMemoryBuffer(context_, &buffer);
+    return true;
+  }
+}
+
+
+static bool ReadConfiguration(Json::Value& configuration,
+                              OrthancPluginContext* context)
+{
+  std::string s;
+
+  {
+    char* tmp = OrthancPluginGetConfiguration(context);
+    if (tmp == NULL)
+    {
+      OrthancPluginLogError(context, "Error while retrieving the configuration from Orthanc");
+      return false;
+    }
+
+    s.assign(tmp);
+    OrthancPluginFreeString(context, tmp);      
+  }
+
+  Json::Reader reader;
+  if (reader.parse(s, configuration))
+  {
+    return true;
+  }
+  else
+  {
+    OrthancPluginLogError(context, "Unable to parse the configuration");
+    return false;
+  }
+}
+
+
+
+static bool LookupFolder(std::string& folder,
+                         OrthancPluginRestOutput* output,
+                         const OrthancPluginHttpRequest* request)
+{
+  const std::string uri = request->groups[0];
+
+  std::map<std::string, std::string>::const_iterator found = folders_.find(uri);
+  if (found == folders_.end())
+  {
+    std::string s = "Unknown URI in plugin server-folders: " + uri;
+    OrthancPluginLogError(context_, s.c_str());
+    OrthancPluginSendHttpStatusCode(context_, output, 404);
+    return false;
+  }
+  else
+  {
+    folder = found->second;
+    return true;
+  }
+}
+
+
+static OrthancPluginErrorCode FolderCallback(OrthancPluginRestOutput* output,
+                                             const char* url,
+                                             const OrthancPluginHttpRequest* request)
+{
+  namespace fs = boost::filesystem;  
+
+  if (request->method != OrthancPluginHttpMethod_Get)
+  {
+    OrthancPluginSendMethodNotAllowed(context_, output, "GET");
+    return OrthancPluginErrorCode_Success;
+  }
+
+  std::string folder;
+
+  if (LookupFolder(folder, output, request))
+  {
+    const fs::path item(request->groups[1]);
+    const fs::path parent((fs::path(folder) / item).parent_path());
+
+    if (item.filename().string() == "index.html" &&
+        fs::is_directory(parent) &&
+        !fs::is_regular_file(fs::path(folder) / item))
+    {
+      // On-the-fly generation of an "index.html" 
+      std::string s;
+      s += "<html>\n";
+      s += "  <body>\n";
+      s += "    <ul>\n";
+
+      fs::directory_iterator end;
+
+      for (fs::directory_iterator it(parent) ; it != end; ++it)
+      {
+        if (fs::is_directory(it->status()))
+        {
+          std::string f = it->path().filename().string();
+          s += "      <li><a href=\"" + f + "/index.html\">" + f + "/</a></li>\n";
+        }
+      }
+
+      for (fs::directory_iterator it(parent) ; it != end; ++it)
+      {
+        if (fs::is_regular_file(it->status()))
+        {
+          std::string f = it->path().filename().string();
+          s += "      <li><a href=\"" + f + "\">" + f + "</a></li>\n";
+        }
+      }
+
+      s += "    </ul>\n";
+      s += "  </body>\n";
+      s += "</html>\n";
+
+      OrthancPluginAnswerBuffer(context_, output, s.c_str(), s.size(), "text/html");
+    }
+    else
+    {
+      std::string path = folder + "/" + item.string();
+      const char* mime = GetMimeType(path);
+
+      std::string s;
+      if (ReadFile(s, path))
+      {
+        const char* resource = s.size() ? s.c_str() : NULL;
+        OrthancPluginAnswerBuffer(context_, output, resource, s.size(), mime);
+      }
+      else
+      {
+        std::string s = "Inexistent file in served folder: " + path;
+        OrthancPluginLogError(context_, s.c_str());
+        OrthancPluginSendHttpStatusCode(context_, output, 404);
+      }
+    }
+  }
+
+  return OrthancPluginErrorCode_Success;
+}
+
+
+static OrthancPluginErrorCode ListServedFolders(OrthancPluginRestOutput* output,
+                                                const char* url,
+                                                const OrthancPluginHttpRequest* request)
+{
+  if (request->method != OrthancPluginHttpMethod_Get)
+  {
+    OrthancPluginSendMethodNotAllowed(context_, output, "GET");
+    return OrthancPluginErrorCode_Success;
+  }
+
+  std::string s = "<html><body><h1>Additional folders served by Orthanc</h1>\n";
+
+  if (folders_.empty())
+  {
+    s += "<p>Empty section <tt>ServeFolders</tt> in your configuration file: No additional folder is served.</p>\n";
+  }
+  else
+  {
+    s += "<ul>\n";
+    for (std::map<std::string, std::string>::const_iterator
+           it = folders_.begin(); it != folders_.end(); ++it)
+    {
+      // The URI is relative to INDEX_URI ("/app/plugin-serve-folders.html")
+      s += "<li><a href=\"../" + it->first + "/index.html\">" + it->first + "</li>\n";
+    }
+    
+    s += "</ul>\n";
+  }
+
+  s += "</body></html>\n";
+
+  OrthancPluginAnswerBuffer(context_, output, s.c_str(), s.size(), "text/html");
+
+  return OrthancPluginErrorCode_Success;
+}
+
+
+extern "C"
+{
+  ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context)
+  {
+    context_ = context;
+
+    /* Check the version of the Orthanc core */
+    if (OrthancPluginCheckVersion(context_) == 0)
+    {
+      char info[1024];
+      sprintf(info, "Your version of Orthanc (%s) must be above %d.%d.%d to run this plugin",
+              context_->orthancVersion,
+              ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER,
+              ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER,
+              ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER);
+      OrthancPluginLogError(context_, info);
+      return -1;
+    }
+
+    OrthancPluginSetDescription(context_, "Serve additional folders with the HTTP server of Orthanc.");
+
+    Json::Value configuration;
+    if (!ReadConfiguration(configuration, context_))
+    {
+      return -1;
+    }
+
+    if (configuration.isMember("ServeFolders"))
+    {
+      if (configuration["ServeFolders"].type() != Json::objectValue)
+      {
+        OrthancPluginLogError(context_, "The \"ServeFolders\" configuration section is badly formatted (must be a JSON object)");
+        return -1;
+      }
+    }
+    else
+    {
+      OrthancPluginLogWarning(context_, "No section \"ServeFolders\" in your configuration file: "
+                              "No additional folder will be served!");
+      configuration["ServeFolders"] = Json::objectValue;
+    }
+
+
+    Json::Value::Members members = configuration["ServeFolders"].getMemberNames();
+
+    // Register the callback for each base URI
+    for (Json::Value::Members::const_iterator 
+           it = members.begin(); it != members.end(); ++it)
+    {
+      std::string baseUri = *it;
+
+      // Remove the heading and trailing slashes in the root URI, if any
+      while (!baseUri.empty() &&
+             *baseUri.begin() == '/')
+      {
+        baseUri = baseUri.substr(1);
+      }
+
+      while (!baseUri.empty() &&
+             *baseUri.rbegin() == '/')
+      {
+        baseUri.resize(baseUri.size() - 1);
+      }
+
+      if (baseUri.empty())
+      {
+        OrthancPluginLogError(context_, "The URI of a folder to be served cannot be empty");
+        return -1;
+      }
+
+      // Check whether the source folder exists and is indeed a directory
+      const std::string folder = configuration["ServeFolders"][*it].asString();
+      if (!boost::filesystem::is_directory(folder))
+      {
+        std::string msg = "Trying and serve an inexistent folder: " + folder;
+        OrthancPluginLogError(context_, msg.c_str());
+        return -1;
+      }
+
+      folders_[baseUri] = folder;
+
+      // Register the callback to serve the folder
+      {
+        const std::string regex = "/(" + baseUri + ")/(.*)";
+        OrthancPluginRegisterRestCallback(context, regex.c_str(), FolderCallback);
+      }
+    }
+
+    OrthancPluginRegisterRestCallback(context, INDEX_URI, ListServedFolders);
+    OrthancPluginSetRootUri(context, INDEX_URI);
+
+    return 0;
+  }
+
+
+  ORTHANC_PLUGINS_API void OrthancPluginFinalize()
+  {
+  }
+
+
+  ORTHANC_PLUGINS_API const char* OrthancPluginGetName()
+  {
+    return "serve-folders";
+  }
+
+
+  ORTHANC_PLUGINS_API const char* OrthancPluginGetVersion()
+  {
+    return SERVE_FOLDERS_VERSION;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Samples/ServeFolders/README	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,52 @@
+Introduction
+============
+
+This sample plugin enables Orthanc to serve additional folders using
+its embedded Web server.
+
+
+Compilation for Linux
+=====================
+
+# mkdir Build
+# cd Build
+# cmake ..
+# make
+
+
+Cross-compilation for Windows using MinGW
+=========================================
+
+# mkdir Build
+# cd Build
+# cmake .. -DCMAKE_TOOLCHAIN_FILE=../../../Resources/MinGWToolchain.cmake
+# make
+
+
+Configuration
+=============
+
+First, generate the default configuration of Orthanc:
+https://code.google.com/p/orthanc/wiki/OrthancConfiguration
+
+Then, modify the "Plugins" option to point to the folder containing
+the built plugins.
+
+Finally, create a section "ServeFolders" in the configuration file to
+specify which folder you want to serve, and at which URI. For
+instance, the following excerpt would load the plugins from the
+working directory, then would branch the content of the folder
+"/home/jodogne/WWW/fosdem" as the URI "http://localhost:8042/fosdem":
+
+{
+  "Name" : "MyOrthanc",
+  [...]
+  "HttpPort" : 8042,
+  [...]
+  "Plugins" : [ 
+    "."
+  ],
+  "ServeFolders" : {
+    "/fosdem" : "/home/jodogne/WWW/fosdem"
+  }
+}
--- a/Plugins/Samples/StorageArea/CMakeLists.txt	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Samples/StorageArea/CMakeLists.txt	Wed Sep 30 13:23:31 2015 +0200
@@ -2,16 +2,7 @@
 
 project(Basic)
 
-if (${CMAKE_COMPILER_IS_GNUCXX})
-  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic -Werror")
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Werror")
-endif()
+set(SAMPLES_ROOT ${CMAKE_SOURCE_DIR}/..)
+include(${SAMPLES_ROOT}/Common/OrthancPlugins.cmake)
 
-include_directories(${CMAKE_SOURCE_DIR}/../../OrthancCPlugin/)
 add_library(PluginTest SHARED Plugin.cpp)
-
-if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
-  # Linking with "pthread" is necessary, otherwise the software crashes
-  # http://sourceware.org/bugzilla/show_bug.cgi?id=10652#c17
-  target_link_libraries(PluginTest pthread dl)
-endif()
--- a/Plugins/Samples/StorageArea/Plugin.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Samples/StorageArea/Plugin.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -3,29 +3,22 @@
  * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
  * Department, University Hospital of Liege, Belgium
  *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU 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
+ * General Public License for more details.
  *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
 
 
-#include <OrthancCPlugin.h>
+#include <orthanc/OrthancCPlugin.h>
 
 #include <string.h>
 #include <stdio.h>
@@ -40,43 +33,43 @@
 }
 
 
-static int32_t StorageCreate(const char* uuid,
-                             const void* content,
-                             int64_t size,
-                             OrthancPluginContentType type)
+static OrthancPluginErrorCode StorageCreate(const char* uuid,
+                                            const void* content,
+                                            int64_t size,
+                                            OrthancPluginContentType type)
 {
   std::string path = GetPath(uuid);
 
   FILE* fp = fopen(path.c_str(), "wb");
   if (!fp)
   {
-    return -1;
+    return OrthancPluginErrorCode_StorageAreaPlugin;
   }
 
   bool ok = fwrite(content, size, 1, fp) == 1;
   fclose(fp);
 
-  return ok ? 0 : -1;
+  return ok ? OrthancPluginErrorCode_Success : OrthancPluginErrorCode_StorageAreaPlugin;
 }
 
 
-static int32_t StorageRead(void** content,
-                           int64_t* size,
-                           const char* uuid,
-                           OrthancPluginContentType type)
+static OrthancPluginErrorCode StorageRead(void** content,
+                                          int64_t* size,
+                                          const char* uuid,
+                                          OrthancPluginContentType type)
 {
   std::string path = GetPath(uuid);
 
   FILE* fp = fopen(path.c_str(), "rb");
   if (!fp)
   {
-    return -1;
+    return OrthancPluginErrorCode_StorageAreaPlugin;
   }
 
   if (fseek(fp, 0, SEEK_END) < 0)
   {
     fclose(fp);
-    return -1;
+    return OrthancPluginErrorCode_StorageAreaPlugin;
   }
 
   *size = ftell(fp);
@@ -84,7 +77,7 @@
   if (fseek(fp, 0, SEEK_SET) < 0)
   {
     fclose(fp);
-    return -1;
+    return OrthancPluginErrorCode_StorageAreaPlugin;
   }
 
   bool ok = true;
@@ -105,15 +98,23 @@
 
   fclose(fp);
 
-  return ok ? 0 : -1;  
+  return ok ? OrthancPluginErrorCode_Success : OrthancPluginErrorCode_StorageAreaPlugin;
 }
 
 
-static int32_t StorageRemove(const char* uuid,
-                             OrthancPluginContentType type)
+static OrthancPluginErrorCode StorageRemove(const char* uuid,
+                                            OrthancPluginContentType type)
 {
   std::string path = GetPath(uuid);
-  return remove(path.c_str());
+
+  if (remove(path.c_str()) == 0)
+  {
+    return OrthancPluginErrorCode_Success;
+  }
+  else
+  {
+    return OrthancPluginErrorCode_StorageAreaPlugin;
+  }
 }
 
 
--- a/Plugins/Samples/WebSkeleton/CMakeLists.txt	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Samples/WebSkeleton/CMakeLists.txt	Wed Sep 30 13:23:31 2015 +0200
@@ -7,7 +7,8 @@
 
 include(Framework/Framework.cmake)
 
-include_directories(${CMAKE_SOURCE_DIR}/../../OrthancCPlugin/)
+set(SAMPLES_ROOT ${CMAKE_SOURCE_DIR}/..)
+include(${SAMPLES_ROOT}/Common/OrthancPlugins.cmake)
 
 add_library(WebSkeleton SHARED 
   ${AUTOGENERATED_SOURCES}
--- a/Plugins/Samples/WebSkeleton/Configuration.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Samples/WebSkeleton/Configuration.h	Wed Sep 30 13:23:31 2015 +0200
@@ -3,25 +3,18 @@
  * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
  * Department, University Hospital of Liege, Belgium
  *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU 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
+ * General Public License for more details.
  *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
 
 
--- a/Plugins/Samples/WebSkeleton/Framework/EmbedResources.py	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Samples/WebSkeleton/Framework/EmbedResources.py	Wed Sep 30 13:23:31 2015 +0200
@@ -7,18 +7,6 @@
 # published by the Free Software Foundation, either version 3 of the
 # License, or (at your option) any later version.
 #
-# In addition, as a special exception, the copyright holders of this
-# program give permission to link the code of its release with the
-# OpenSSL project's "OpenSSL" library (or with modified versions of it
-# that use the same license as the "OpenSSL" library), and distribute
-# the linked executables. You must obey the GNU General Public License
-# in all respects for all of the code used other than "OpenSSL". If you
-# modify file(s) with this exception, you may extend this exception to
-# your version of the file(s), but you are not obligated to do so. If
-# you do not wish to do so, delete this exception statement from your
-# version. If you delete this exception statement from all source files
-# in the program, then also delete it here.
-# 
 # 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
@@ -222,6 +210,10 @@
         cpp.write("0x%02x" % c)
         pos += 1
 
+    # Zero-size array are disallowed, so we put one single void character in it.
+    if pos == 0:
+        cpp.write('  0')
+
     cpp.write('  };\n')
     cpp.write('    static const size_t resource%dSize = %d;\n' % (item['Index'], pos))
 
--- a/Plugins/Samples/WebSkeleton/Framework/Framework.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Samples/WebSkeleton/Framework/Framework.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -6,18 +6,6 @@
 # modify it under the terms of the GNU General Public License as
 # published by the Free Software Foundation, either version 3 of the
 # License, or (at your option) any later version.
-#
-# In addition, as a special exception, the copyright holders of this
-# program give permission to link the code of its release with the
-# OpenSSL project's "OpenSSL" library (or with modified versions of it
-# that use the same license as the "OpenSSL" library), and distribute
-# the linked executables. You must obey the GNU General Public License
-# in all respects for all of the code used other than "OpenSSL". If you
-# modify file(s) with this exception, you may extend this exception to
-# your version of the file(s), but you are not obligated to do so. If
-# you do not wish to do so, delete this exception statement from your
-# version. If you delete this exception statement from all source files
-# in the program, then also delete it here.
 # 
 # This program is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -28,16 +16,6 @@
 # along with this program. If not, see <http://www.gnu.org/licenses/>.
 
 
-if (${CMAKE_COMPILER_IS_GNUCXX})
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Werror -Wno-unused-function")
-endif()
-
-if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
-  # Linking with "pthread" is necessary, otherwise the software might crash
-  # http://sourceware.org/bugzilla/show_bug.cgi?id=10652#c17
-  link_libraries(pthread dl)
-endif()
-
 if (STANDALONE_BUILD)
   add_definitions(-DORTHANC_PLUGIN_STANDALONE=1)
 
@@ -73,4 +51,4 @@
 
 list(APPEND AUTOGENERATED_SOURCES
   "${CMAKE_CURRENT_SOURCE_DIR}/Framework/Plugin.cpp"
-  )
\ No newline at end of file
+  )
--- a/Plugins/Samples/WebSkeleton/Framework/Plugin.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/Plugins/Samples/WebSkeleton/Framework/Plugin.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -3,31 +3,24 @@
  * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
  * Department, University Hospital of Liege, Belgium
  *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU 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
+ * General Public License for more details.
  *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
 
 
 #include "../Configuration.h"
 
-#include <OrthancCPlugin.h>
+#include <orthanc/OrthancCPlugin.h>
 #include <string>
 #include <stdexcept>
 #include <algorithm>
@@ -138,14 +131,14 @@
 
 
 #if ORTHANC_PLUGIN_STANDALONE == 1
-static int32_t ServeStaticResource(OrthancPluginRestOutput* output,
-                                   const char* url,
-                                   const OrthancPluginHttpRequest* request)
+static OrthancPluginErrorCode ServeStaticResource(OrthancPluginRestOutput* output,
+                                                  const char* url,
+                                                  const OrthancPluginHttpRequest* request)
 {
   if (request->method != OrthancPluginHttpMethod_Get)
   {
     OrthancPluginSendMethodNotAllowed(context, output, "GET");
-    return 0;
+    return OrthancPluginErrorCode_Success;
   }
 
   std::string path = "/" + std::string(request->groups[0]);
@@ -159,29 +152,28 @@
 
     const char* resource = s.size() ? s.c_str() : NULL;
     OrthancPluginAnswerBuffer(context, output, resource, s.size(), mime);
-
-    return 0;
   }
   catch (std::runtime_error&)
   {
     std::string s = "Unknown static resource in plugin: " + std::string(request->groups[0]);
     OrthancPluginLogError(context, s.c_str());
     OrthancPluginSendHttpStatusCode(context, output, 404);
-    return 0;
   }
+
+  return OrthancPluginErrorCode_Success;
 }
 #endif
 
 
 #if ORTHANC_PLUGIN_STANDALONE == 0
-static int32_t ServeFolder(OrthancPluginRestOutput* output,
-                           const char* url,
-                           const OrthancPluginHttpRequest* request)
+static OrthancPluginErrorCode ServeFolder(OrthancPluginRestOutput* output,
+                                          const char* url,
+                                          const OrthancPluginHttpRequest* request)
 {
   if (request->method != OrthancPluginHttpMethod_Get)
   {
     OrthancPluginSendMethodNotAllowed(context, output, "GET");
-    return 0;
+    return OrthancPluginErrorCode_Success;
   }
 
   std::string path = ORTHANC_PLUGIN_RESOURCES_ROOT "/" + std::string(request->groups[0]);
@@ -192,23 +184,22 @@
   {
     const char* resource = s.size() ? s.c_str() : NULL;
     OrthancPluginAnswerBuffer(context, output, resource, s.size(), mime);
-
-    return 0;
   }
   else
   {
     std::string s = "Unknown static resource in plugin: " + std::string(request->groups[0]);
     OrthancPluginLogError(context, s.c_str());
     OrthancPluginSendHttpStatusCode(context, output, 404);
-    return 0;
   }
+
+  return OrthancPluginErrorCode_Success;
 }
 #endif
 
 
-static int32_t RedirectRoot(OrthancPluginRestOutput* output,
-                            const char* url,
-                            const OrthancPluginHttpRequest* request)
+static OrthancPluginErrorCode RedirectRoot(OrthancPluginRestOutput* output,
+                                           const char* url,
+                                           const OrthancPluginHttpRequest* request)
 {
   if (request->method != OrthancPluginHttpMethod_Get)
   {
@@ -219,7 +210,7 @@
     OrthancPluginRedirect(context, output, ORTHANC_PLUGIN_WEB_ROOT "index.html");
   }
 
-  return 0;
+  return OrthancPluginErrorCode_Success;
 }
 
 
--- a/README	Wed Feb 11 10:40:08 2015 +0100
+++ b/README	Wed Sep 30 13:23:31 2015 +0200
@@ -75,7 +75,6 @@
 This archive contains the following directories:
 
 * Core/               - The core C++ classes (independent of DCMTK)
-* OrthancCppClient/   - Code of the C++ client
 * OrthancExplorer/    - Code of the Web application (HTML5/Javascript)
 * OrthancServer/      - Code of the Orthanc server (depends on DCMTK)
 * Plugins/            - Code of the plugin framework
--- a/Resources/CMake/AutoGeneratedCode.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/AutoGeneratedCode.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -6,17 +6,28 @@
 
 macro(EmbedResources)
   # Convert a semicolon separated list to a whitespace separated string
+  set(SCRIPT_OPTIONS)
   set(SCRIPT_ARGUMENTS)
   set(DEPENDENCIES)
   set(IS_PATH_NAME false)
+
+  # Loop over the arguments of the function
   foreach(arg ${ARGN})
-    if (${IS_PATH_NAME})
-      list(APPEND SCRIPT_ARGUMENTS "${arg}")
-      list(APPEND DEPENDENCIES "${arg}")
-      set(IS_PATH_NAME false)
+    # Extract the first character of the argument
+    string(SUBSTRING "${arg}" 0 1 FIRST_CHAR)
+    if (${FIRST_CHAR} STREQUAL "-")
+      # If the argument starts with a dash "-", this is an option to
+      # EmbedResources.py
+      list(APPEND SCRIPT_OPTIONS ${arg})
     else()
-      list(APPEND SCRIPT_ARGUMENTS "${arg}")
-      set(IS_PATH_NAME true)
+      if (${IS_PATH_NAME})
+        list(APPEND SCRIPT_ARGUMENTS "${arg}")
+        list(APPEND DEPENDENCIES "${arg}")
+        set(IS_PATH_NAME false)
+      else()
+        list(APPEND SCRIPT_ARGUMENTS "${arg}")
+        set(IS_PATH_NAME true)
+      endif()
     endif()
   endforeach()
 
@@ -26,12 +37,13 @@
     "${TARGET_BASE}.h"
     "${TARGET_BASE}.cpp"
     COMMAND 
-    python
-    "${CMAKE_CURRENT_SOURCE_DIR}/Resources/EmbedResources.py"
+    ${PYTHON_EXECUTABLE}
+    "${ORTHANC_ROOT}/Resources/EmbedResources.py"
+    ${SCRIPT_OPTIONS}
     "${AUTOGENERATED_DIR}/EmbeddedResources"
     ${SCRIPT_ARGUMENTS}
     DEPENDS
-    "${CMAKE_CURRENT_SOURCE_DIR}/Resources/EmbedResources.py"
+    "${ORTHANC_ROOT}/Resources/EmbedResources.py"
     ${DEPENDENCIES}
     )
 
--- a/Resources/CMake/BoostConfiguration.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/BoostConfiguration.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -39,23 +39,21 @@
 
 
 if (BOOST_STATIC)
-  # Parameters for Boost 1.55.0
-  set(BOOST_NAME boost_1_55_0)
-  set(BOOST_BCP_SUFFIX bcpdigest-0.7.4)
-  set(BOOST_MD5 "409f7a0e4fb1f5659d07114f3133b67b")
-  set(BOOST_FILESYSTEM_SOURCES_DIR "${BOOST_NAME}/libs/filesystem/src")
-  
+  # Parameters for Boost 1.58.0
+  set(BOOST_NAME boost_1_58_0)
+  set(BOOST_BCP_SUFFIX bcpdigest-0.9.2)
+  set(BOOST_MD5 "704b110917cbda903e07cb53934b47ac")
+  set(BOOST_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/${BOOST_NAME}_${BOOST_BCP_SUFFIX}.tar.gz")
+  set(BOOST_FILESYSTEM_SOURCES_DIR "${BOOST_NAME}/libs/filesystem/src") 
   set(BOOST_SOURCES_DIR ${CMAKE_BINARY_DIR}/${BOOST_NAME})
-  DownloadPackage(
-    "${BOOST_MD5}"
-    "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/${BOOST_NAME}_${BOOST_BCP_SUFFIX}.tar.gz"
-    "${BOOST_SOURCES_DIR}"
-    )
+
+  DownloadPackage(${BOOST_MD5} ${BOOST_URL} "${BOOST_SOURCES_DIR}")
 
   set(BOOST_SOURCES)
 
   if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
       ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
       ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
     list(APPEND BOOST_SOURCES
       ${BOOST_SOURCES_DIR}/libs/thread/src/pthread/once.cpp
@@ -82,8 +80,11 @@
     # Windows XP seems not to support properly several codepages
     # (notably "Latin3", "Hebrew", and "Arabic").
 
-    # add_definitions(-DBOOST_LOCALE_WITH_WCONV=1)
-    include(${ORTHANC_ROOT}/Resources/CMake/LibIconvConfiguration.cmake)
+    if (USE_BOOST_ICONV)
+      include(${ORTHANC_ROOT}/Resources/CMake/LibIconvConfiguration.cmake)
+    else()
+      add_definitions(-DBOOST_LOCALE_WITH_WCONV=1)
+    endif()
 
   else()
     message(FATAL_ERROR "Support your platform here")
@@ -95,16 +96,6 @@
       )
   endif()
 
-  if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
-    # This is a patch to compile Boost 1.55.0 with Clang 3.4 and later
-    # (including XCode 5.1). Fixes issue 14 of Orthanc.
-    # https://trac.macports.org/ticket/42282#comment:10
-    execute_process(
-      COMMAND patch -p0 -i ${ORTHANC_ROOT}/Resources/Patches/boost-1.55.0-clang-atomic.patch
-      WORKING_DIRECTORY ${BOOST_SOURCES_DIR}
-      )
-  endif()
-
   aux_source_directory(${BOOST_SOURCES_DIR}/libs/regex/src BOOST_REGEX_SOURCES)
 
   list(APPEND BOOST_SOURCES
@@ -118,8 +109,6 @@
     ${BOOST_SOURCES_DIR}/libs/system/src/error_code.cpp
     )
 
-  list(APPEND THIRD_PARTY_SOURCES ${BOOST_SOURCES})
-
   add_definitions(
     # Static build of Boost
     -DBOOST_ALL_NO_LIB 
@@ -134,7 +123,7 @@
     -DBOOST_HAS_FILESYSTEM_V3=1
     )
 
-  if (${CMAKE_COMPILER_IS_GNUCXX})
+  if (CMAKE_COMPILER_IS_GNUCXX)
     add_definitions(-isystem ${BOOST_SOURCES_DIR})
   endif()
 
@@ -148,3 +137,9 @@
     -DBOOST_HAS_LOCALE=1
     )
 endif()
+
+
+add_definitions(
+  -DBOOST_HAS_DATE_TIME=1
+  -DBOOST_HAS_REGEX=1
+  )
--- a/Resources/CMake/BoostConfiguration.sh	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/BoostConfiguration.sh	Wed Sep 30 13:23:31 2015 +0200
@@ -12,22 +12,23 @@
 ##
 ## History:
 ##   - Orthanc between 0.6.2 and 0.7.3: Boost 1.54.0
-##   - Orthanc above 0.7.4: Boost 1.55.0
+##   - Orthanc between 0.7.4 and 0.9.1: Boost 1.55.0
+##   - Orthanc >= 0.9.2: Boost 1.58.0
 
-rm -rf /tmp/boost_1_55_0
-rm -rf /tmp/bcp/boost_1_55_0
+rm -rf /tmp/boost_1_58_0
+rm -rf /tmp/bcp/boost_1_58_0
 
 cd /tmp
-echo "Uncompressing the source of Boost 1.55.0..."
-tar xfz boost_1_55_0.tar.gz 
+echo "Uncompressing the sources of Boost 1.58.0..."
+tar xfz ./boost_1_58_0.tar.gz 
 
 echo "Generating the subset..."
-mkdir -p /tmp/bcp/boost_1_55_0
-bcp --boost=/tmp/boost_1_55_0 thread system locale date_time filesystem math/special_functions algorithm uuid /tmp/bcp/boost_1_55_0
+mkdir -p /tmp/bcp/boost_1_58_0
+bcp --boost=/tmp/boost_1_58_0 thread system locale date_time filesystem math/special_functions algorithm uuid atomic /tmp/bcp/boost_1_58_0
 cd /tmp/bcp
 
 echo "Compressing the subset..."
-tar cfz boost_1_55_0_bcpdigest-0.7.4.tar.gz boost_1_55_0
-ls -l boost_1_55_0_bcpdigest-0.7.4.tar.gz
-md5sum boost_1_55_0_bcpdigest-0.7.4.tar.gz
-readlink -f boost_1_55_0_bcpdigest-0.7.4.tar.gz
+tar cfz boost_1_58_0_bcpdigest-0.9.2.tar.gz boost_1_58_0
+ls -l boost_1_58_0_bcpdigest-0.9.2.tar.gz
+md5sum boost_1_58_0_bcpdigest-0.9.2.tar.gz
+readlink -f boost_1_58_0_bcpdigest-0.9.2.tar.gz
--- a/Resources/CMake/Compiler.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/Compiler.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -1,6 +1,12 @@
 # This file sets all the compiler-related flags
 
-if (${CMAKE_COMPILER_IS_GNUCXX})
+if (CMAKE_CROSSCOMPILING)
+  # Cross-compilation necessarily implies standalone and static build
+  SET(STATIC_BUILD ON)
+  SET(STANDALONE_BUILD ON)
+endif()
+
+if (CMAKE_COMPILER_IS_GNUCXX)
   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-long-long -Wno-implicit-function-declaration")  
   # --std=c99 makes libcurl not to compile
   # -pedantic gives a lot of warnings on OpenSSL 
@@ -11,7 +17,7 @@
     set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> -O coff -I<CMAKE_CURRENT_SOURCE_DIR> <SOURCE> <OBJECT>")
   endif()
 
-elseif (${MSVC})
+elseif (MSVC)
   # Use static runtime under Visual Studio
   # http://www.cmake.org/Wiki/CMake_FAQ#Dynamic_Replace
   # http://stackoverflow.com/a/6510446
@@ -41,39 +47,69 @@
 
 
 if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
-    ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
-  if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
-    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -I${LSB_PATH}/include")
-    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -nostdinc++ -I${LSB_PATH}/include -I${LSB_PATH}/include/c++ -I${LSB_PATH}/include/c++/backward -fpermissive")
-    SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -L${LSB_LIBPATH}")
-  endif()
-
-  add_definitions(
-    -D_LARGEFILE64_SOURCE=1 
-    -D_FILE_OFFSET_BITS=64
-    )
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed")
+    ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD" OR
+    ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
   set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-undefined")
-  set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
+  set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined -Wl,--version-script=${ORTHANC_ROOT}/Plugins/Samples/Common/VersionScript.map")
 
   # Remove the "-rdynamic" option
   # http://www.mail-archive.com/cmake@cmake.org/msg08837.html
   set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
-  link_libraries(uuid pthread rt dl)
+  link_libraries(uuid pthread rt)
+
+  if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
+    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed")
+    set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--as-needed")
+    set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--as-needed")
+    add_definitions(
+      -D_LARGEFILE64_SOURCE=1 
+      -D_FILE_OFFSET_BITS=64
+      )
+    link_libraries(dl)
+  endif()
 
 elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
+  if (MSVC)
+    message("MSVC compiler version = " ${MSVC_VERSION} "\n")
+    # Starting Visual Studio 2013 (version 1800), it is not possible
+    # to target Windows XP anymore
+    if (MSVC_VERSION LESS 1800)
+      add_definitions(
+        -DWINVER=0x0501
+        -D_WIN32_WINNT=0x0501
+        )
+    endif()
+  else()
+    add_definitions(
+      -DWINVER=0x0501
+      -D_WIN32_WINNT=0x0501
+      )
+  endif()
+
   add_definitions(
-    -DWINVER=0x0501
     -D_CRT_SECURE_NO_WARNINGS=1
     )
   link_libraries(rpcrt4 ws2_32)
 
-  if (${CMAKE_COMPILER_IS_GNUCXX})
+  if (CMAKE_COMPILER_IS_GNUCXX)
     # This is a patch for MinGW64
     SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++")
+    SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++")
+
+    CHECK_LIBRARY_EXISTS(winpthread pthread_create "" HAVE_WIN_PTHREAD)
+    if (HAVE_WIN_PTHREAD)
+      # This line is necessary to compile with recent versions of MinGW,
+      # otherwise "libwinpthread-1.dll" is not statically linked.
+      SET(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} -Wl,-Bstatic -lstdc++ -lpthread -Wl,-Bdynamic")
+      add_definitions(-DHAVE_WIN_PTHREAD=1)
+    else()
+      add_definitions(-DHAVE_WIN_PTHREAD=0)
+    endif()
   endif()
 
 elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
+  SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -exported_symbols_list ${ORTHANC_ROOT}/Plugins/Samples/Common/ExportedSymbols.list")
+
   add_definitions(
     -D_XOPEN_SOURCE=1
     )
@@ -82,6 +118,22 @@
 endif()
 
 
+if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
+  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -I${LSB_PATH}/include")
+  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -nostdinc++ -I${LSB_PATH}/include -I${LSB_PATH}/include/c++ -I${LSB_PATH}/include/c++/backward -fpermissive")
+  SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -L${LSB_LIBPATH}")
+endif()
+
+
+if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
+  # In FreeBSD, the "/usr/local/" folder contains the ports and need to be imported
+  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I/usr/local/include")
+  SET(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -I/usr/local/include")
+  SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L/usr/local/lib")
+  SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -L/usr/local/lib")
+endif()
+
+
 if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
   CHECK_INCLUDE_FILES(rpc.h HAVE_UUID_H)
 else()
@@ -92,7 +144,8 @@
   message(FATAL_ERROR "Please install the uuid-dev package")
 endif()
 
-if (${STATIC_BUILD})
+
+if (STATIC_BUILD)
   add_definitions(-DORTHANC_STATIC=1)
 else()
   add_definitions(-DORTHANC_STATIC=0)
--- a/Resources/CMake/DcmtkConfiguration.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/DcmtkConfiguration.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -3,6 +3,7 @@
   find_path(DCMTK_DICTIONARY_DIR_AUTO dicom.dic
     /usr/share/dcmtk
     /usr/share/libdcmtk2
+    /usr/local/share/dcmtk
     )
 
   message("Autodetected path to the DICOM dictionaries: ${DCMTK_DICTIONARY_DIR_AUTO}")
@@ -14,16 +15,23 @@
 
 if (STATIC_BUILD OR NOT USE_SYSTEM_DCMTK)
   SET(DCMTK_VERSION_NUMBER 360)
+  set(DCMTK_PACKAGE_VERSION "3.6.0")
   SET(DCMTK_SOURCES_DIR ${CMAKE_BINARY_DIR}/dcmtk-3.6.0)
-  DownloadPackage(
-    "219ad631b82031806147e4abbfba4fa4"
-    "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/dcmtk-3.6.0.zip" 
-    "${DCMTK_SOURCES_DIR}")
+  SET(DCMTK_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/dcmtk-3.6.0.zip")
+  SET(DCMTK_MD5 "219ad631b82031806147e4abbfba4fa4")
 
-  IF(CMAKE_CROSSCOMPILING)
+  if (IS_DIRECTORY "${DCMTK_SOURCES_DIR}")
+    set(FirstRun OFF)
+  else()
+    set(FirstRun ON)
+  endif()
+
+  DownloadPackage(${DCMTK_MD5} ${DCMTK_URL} "${DCMTK_SOURCES_DIR}")
+
+  IF (CMAKE_CROSSCOMPILING)
     SET(C_CHAR_UNSIGNED 1 CACHE INTERNAL "Whether char is unsigned.")
   ENDIF()
-  SET(DCMTK_SOURCE_DIR ${CMAKE_BINARY_DIR}/dcmtk-3.6.0)
+  SET(DCMTK_SOURCE_DIR ${DCMTK_SOURCES_DIR})
   include(${DCMTK_SOURCES_DIR}/CMake/CheckFunctionWithHeaderExists.cmake)
   include(${DCMTK_SOURCES_DIR}/CMake/GenerateDCMTKConfigure.cmake)
 
@@ -40,9 +48,8 @@
     set(HAVE_PROTOTYPE_GETSOCKNAME 1)
   endif()
 
-  set(DCMTK_PACKAGE_VERSION "3.6.0")
   set(DCMTK_PACKAGE_VERSION_SUFFIX "")
-  set(DCMTK_PACKAGE_VERSION_NUMBER 360)
+  set(DCMTK_PACKAGE_VERSION_NUMBER ${DCMTK_VERSION_NUMBER})
 
   CONFIGURE_FILE(
     ${DCMTK_SOURCES_DIR}/CMake/osconfig.h.in
@@ -92,24 +99,52 @@
   AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/oflog/libsrc DCMTK_SOURCES)
   if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
       ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
       ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
     list(REMOVE_ITEM DCMTK_SOURCES 
       ${DCMTK_SOURCES_DIR}/oflog/libsrc/windebap.cc
       ${DCMTK_SOURCES_DIR}/oflog/libsrc/winsock.cc
       )
+    
+    execute_process(
+      COMMAND ${PATCH_EXECUTABLE} -p0 -N -i ${ORTHANC_ROOT}/Resources/Patches/dcmtk-linux-speed.patch
+      WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+      RESULT_VARIABLE Failure
+      )
+
+    if (Failure AND FirstRun)
+      message(FATAL_ERROR "Error while patching a file")
+    endif()
+
   elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
     list(REMOVE_ITEM DCMTK_SOURCES 
       ${DCMTK_SOURCES_DIR}/oflog/libsrc/unixsock.cc
       )
 
-    if (${CMAKE_COMPILER_IS_GNUCXX})
+    if (CMAKE_COMPILER_IS_GNUCXX)
       # This is a patch for MinGW64
       execute_process(
-        COMMAND patch -p0 -i ${ORTHANC_ROOT}/Resources/Patches/dcmtk-mingw64.patch
+        COMMAND ${PATCH_EXECUTABLE} -p0 -N -i ${ORTHANC_ROOT}/Resources/Patches/dcmtk-mingw64.patch
         WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+        RESULT_VARIABLE Failure
         )
+
+      if (Failure AND FirstRun)
+        message(FATAL_ERROR "Error while patching a file")
+      endif()
     endif()
 
+    # This patch improves speed, even for Windows
+    execute_process(
+      COMMAND ${PATCH_EXECUTABLE} -p0 -N 
+      INPUT_FILE ${ORTHANC_ROOT}/Resources/Patches/dcmtk-linux-speed.patch
+      WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+      RESULT_VARIABLE Failure
+      )
+
+    if (Failure AND FirstRun)
+      message(FATAL_ERROR "Error while patching a file")
+    endif()
   endif()
 
   list(REMOVE_ITEM DCMTK_SOURCES 
@@ -120,13 +155,13 @@
 
   #set_source_files_properties(${DCMTK_SOURCES}
   #  PROPERTIES COMPILE_DEFINITIONS
-  #  "PACKAGE_VERSION=\"3.6.0\";PACKAGE_VERSION_NUMBER=\"360\"")
+  #  "PACKAGE_VERSION=\"${DCMTK_PACKAGE_VERSION}\";PACKAGE_VERSION_NUMBER=\"${DCMTK_VERSION_NUMBER}\"")
 
   # This fixes crashes related to the destruction of the DCMTK OFLogger
   # http://support.dcmtk.org/docs-snapshot/file_macros.html
   add_definitions(
     -DLOG4CPLUS_DISABLE_FATAL=1
-    -DDCMTK_VERSION_NUMBER=360
+    -DDCMTK_VERSION_NUMBER=${DCMTK_VERSION_NUMBER}
     )
 
   include_directories(
@@ -162,7 +197,6 @@
   list(APPEND DCMTK_LIBRARIES "${tmp}")
 
   include_directories(${DCMTK_INCLUDE_DIR})
-  link_libraries(${DCMTK_LIBRARIES})
 
   add_definitions(
     -DHAVE_CONFIG_H=1
--- a/Resources/CMake/DownloadPackage.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/DownloadPackage.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -10,6 +10,22 @@
 endmacro()
 
 
+
+##
+## Setup the patch command-line tool
+##
+
+if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
+  set(PATCH_EXECUTABLE ${CMAKE_SOURCE_DIR}/Resources/ThirdParty/patch/patch.exe)
+else ()
+  find_program(PATCH_EXECUTABLE patch)
+  if (${PATCH_EXECUTABLE} MATCHES "PATCH_EXECUTABLE-NOTFOUND")
+    message(FATAL_ERROR "Please install the 'patch' standard command-line tool")
+  endif()
+endif()
+
+
+
 ##
 ## Check the existence of the required decompression tools
 ##
--- a/Resources/CMake/GoogleLogConfiguration.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/GoogleLogConfiguration.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -1,10 +1,15 @@
 if (STATIC_BUILD OR NOT USE_SYSTEM_GOOGLE_LOG)
   SET(GOOGLE_LOG_SOURCES_DIR ${CMAKE_BINARY_DIR}/glog-0.3.2)
-  DownloadPackage(
-    "897fbff90d91ea2b6d6e78c8cea641cc"
-    "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/glog-0.3.2.tar.gz"
-    "${GOOGLE_LOG_SOURCES_DIR}")
+  SET(GOOGLE_LOG_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/glog-0.3.2.tar.gz")
+  SET(GOOGLE_LOG_MD5 "897fbff90d91ea2b6d6e78c8cea641cc")
 
+  if (IS_DIRECTORY "${GOOGLE_LOG_SOURCES_DIR}")
+    set(FirstRun OFF)
+  else()
+    set(FirstRun ON)
+  endif()
+
+  DownloadPackage(${GOOGLE_LOG_MD5} ${GOOGLE_LOG_URL} "${GOOGLE_LOG_SOURCES_DIR}")
 
   # Glog 0.3.3 fails to build with old versions of MinGW, such as the
   # one installed on our Continuous Integration Server that runs
@@ -30,6 +35,7 @@
 
   if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
       ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
       ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
     set(ac_cv_have_unistd_h 1)
     set(ac_cv_have_stdint_h 1)
@@ -65,26 +71,45 @@
   if (CMAKE_COMPILER_IS_GNUCXX)
     if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
       execute_process(
-        COMMAND patch utilities.cc ${ORTHANC_ROOT}/Resources/Patches/glog-utilities-lsb.diff
+        COMMAND ${PATCH_EXECUTABLE} -N utilities.cc -i ${ORTHANC_ROOT}/Resources/Patches/glog-utilities-lsb.diff
         WORKING_DIRECTORY ${GOOGLE_LOG_SOURCES_DIR}/src
+        RESULT_VARIABLE Failure
         )
     else()
       execute_process(
-        COMMAND patch utilities.cc ${ORTHANC_ROOT}/Resources/Patches/glog-utilities.diff
+        COMMAND ${PATCH_EXECUTABLE} -N utilities.cc -i ${ORTHANC_ROOT}/Resources/Patches/glog-utilities.diff
         WORKING_DIRECTORY ${GOOGLE_LOG_SOURCES_DIR}/src
+        RESULT_VARIABLE Failure
         )
     endif()
 
+    if (Failure AND FirstRun)
+      message(FATAL_ERROR "Error while patching a file")
+    endif()
+
+    # Patches for MinGW
     execute_process(
-      COMMAND patch port.h ${ORTHANC_ROOT}/Resources/Patches/glog-port-h.diff 
+      #COMMAND ${PATCH_EXECUTABLE} -N port.h -i ${ORTHANC_ROOT}/Resources/Patches/glog-port-h.diff 
+      COMMAND ${PATCH_EXECUTABLE} -N port.h -i ${ORTHANC_ROOT}/Resources/Patches/glog-port-h-v2.diff 
       WORKING_DIRECTORY ${GOOGLE_LOG_SOURCES_DIR}/src/windows
-      )
-    execute_process(
-      COMMAND patch port.cc ${ORTHANC_ROOT}/Resources/Patches/glog-port-cc.diff 
-      WORKING_DIRECTORY ${GOOGLE_LOG_SOURCES_DIR}/src/windows
+      RESULT_VARIABLE Failure
       )
 
-  else(${MSVC})
+    if (Failure AND FirstRun)
+      message(FATAL_ERROR "Error while patching a file")
+    endif()
+
+    execute_process(
+      COMMAND ${PATCH_EXECUTABLE} -N port.cc -i ${ORTHANC_ROOT}/Resources/Patches/glog-port-cc.diff 
+      WORKING_DIRECTORY ${GOOGLE_LOG_SOURCES_DIR}/src/windows
+      RESULT_VARIABLE Failure
+      )
+
+    if (Failure AND FirstRun)
+      message(FATAL_ERROR "Error while patching a file")
+    endif()
+
+  elseif (MSVC)
     # https://code.google.com/p/google-glog/issues/detail?id=117
     configure_file(
       ${ORTHANC_ROOT}/Resources/Patches/glog-visual-studio-port.h
@@ -96,6 +121,7 @@
 
   if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
       ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
       ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
     if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
       # Install the specific configuration for LSB SDK
@@ -145,15 +171,12 @@
       -DGOOGLE_GLOG_DLL_DECL=
       )
 
-    if (${CMAKE_COMPILER_IS_GNUCXX})
+    if (CMAKE_COMPILER_IS_GNUCXX)
       # This is a patch for MinGW64
       add_definitions(-D_TIME_H__S=1)
     endif()
   endif()
 
-  add_library(GoogleLog STATIC ${GOOGLE_LOG_SOURCES})
-  set(STATIC_GOOGLE_LOG GoogleLog)
-
 else()
   CHECK_INCLUDE_FILE_CXX(glog/logging.h HAVE_GOOGLE_LOG_H)
   if (NOT HAVE_GOOGLE_LOG_H)
@@ -162,3 +185,13 @@
 
   link_libraries(glog)
 endif()
+
+
+CHECK_INCLUDE_FILES(sec_api/string_s.h HAVE_SECURE_STRING_EXTENSIONS)
+if (HAVE_SECURE_STRING_EXTENSIONS)
+  add_definitions(-DHAVE_SECURE_STRING_EXTENSIONS=1)
+else()
+  add_definitions(-DHAVE_SECURE_STRING_EXTENSIONS=0)
+endif()
+
+add_definitions(-DORTHANC_ENABLE_GOOGLE_LOG=1)
--- a/Resources/CMake/GoogleTestConfiguration.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/GoogleTestConfiguration.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -9,10 +9,10 @@
 
 elseif (STATIC_BUILD OR NOT USE_SYSTEM_GOOGLE_TEST)
   set(GTEST_SOURCES_DIR ${CMAKE_BINARY_DIR}/gtest-1.7.0)
-  DownloadPackage(
-    "2d6ec8ccdf5c46b05ba54a9fd1d130d7"
-    "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/gtest-1.7.0.zip"
-    "${GTEST_SOURCES_DIR}")
+  set(GTEST_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/gtest-1.7.0.zip")
+  set(GTEST_MD5 "2d6ec8ccdf5c46b05ba54a9fd1d130d7")
+
+  DownloadPackage(${GTEST_MD5} ${GTEST_URL} "${GTEST_SOURCES_DIR}")
 
   include_directories(
     ${GTEST_SOURCES_DIR}/include
--- a/Resources/CMake/JsonCppConfiguration.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/JsonCppConfiguration.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -1,11 +1,11 @@
 if (STATIC_BUILD OR NOT USE_SYSTEM_JSONCPP)
-  set(JSONCPP_SOURCES_DIR ${CMAKE_BINARY_DIR}/jsoncpp-src-0.6.0-rc2)
-  DownloadPackage(
-    "363e2f4cbd3aeb63bf4e571f377400fb"
-    "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/jsoncpp-src-0.6.0-rc2.tar.gz"
-    "${JSONCPP_SOURCES_DIR}")
+  set(JSONCPP_SOURCES_DIR ${CMAKE_BINARY_DIR}/jsoncpp-0.10.5)
+  set(JSONCPP_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/jsoncpp-0.10.5.tar.gz")
+  set(JSONCPP_MD5 "db146bac5a126ded9bd728ab7b61ed6b")
 
-  list(APPEND THIRD_PARTY_SOURCES
+  DownloadPackage(${JSONCPP_MD5} ${JSONCPP_URL} "${JSONCPP_SOURCES_DIR}")
+
+  set(JSONCPP_SOURCES
     ${JSONCPP_SOURCES_DIR}/src/lib_json/json_reader.cpp
     ${JSONCPP_SOURCES_DIR}/src/lib_json/json_value.cpp
     ${JSONCPP_SOURCES_DIR}/src/lib_json/json_writer.cpp
@@ -18,12 +18,18 @@
   source_group(ThirdParty\\JsonCpp REGULAR_EXPRESSION ${JSONCPP_SOURCES_DIR}/.*)
 
 else()
-  CHECK_INCLUDE_FILE_CXX(jsoncpp/json/reader.h HAVE_JSONCPP_H)
+  find_path(JSONCPP_INCLUDE_DIR json/reader.h
+    /usr/include/jsoncpp
+    /usr/local/include/jsoncpp
+    )
+
+  message("JsonCpp include dir: ${JSONCPP_INCLUDE_DIR}")
+  include_directories(${JSONCPP_INCLUDE_DIR})
+  link_libraries(jsoncpp)
+
+  CHECK_INCLUDE_FILE_CXX(${JSONCPP_INCLUDE_DIR}/json/reader.h HAVE_JSONCPP_H)
   if (NOT HAVE_JSONCPP_H)
     message(FATAL_ERROR "Please install the libjsoncpp-dev package")
   endif()
 
-  include_directories(/usr/include/jsoncpp)
-  link_libraries(jsoncpp)
-
 endif()
--- a/Resources/CMake/LibCurlConfiguration.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/LibCurlConfiguration.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -1,24 +1,23 @@
 if (STATIC_BUILD OR NOT USE_SYSTEM_CURL)
-  SET(CURL_SOURCES_DIR ${CMAKE_BINARY_DIR}/curl-7.26.0)
-  DownloadPackage(
-    "3fa4d5236f2a36ca5c3af6715e837691"
-    "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/curl-7.26.0.tar.gz"
-    "${CURL_SOURCES_DIR}")
+  SET(CURL_SOURCES_DIR ${CMAKE_BINARY_DIR}/curl-7.44.0)
+  SET(CURL_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/curl-7.44.0.tar.gz")
+  SET(CURL_MD5 "cf46112b5151e2f1a3fd38439bdade23")
+
+  DownloadPackage(${CURL_MD5} ${CURL_URL} "${CURL_SOURCES_DIR}")
 
-  include_directories(${CURL_SOURCES_DIR}/include)
+  include_directories(
+    ${CURL_SOURCES_DIR}/include
+    )
+
   AUX_SOURCE_DIRECTORY(${CURL_SOURCES_DIR}/lib CURL_SOURCES)
+  AUX_SOURCE_DIRECTORY(${CURL_SOURCES_DIR}/lib/vtls CURL_SOURCES)
   source_group(ThirdParty\\LibCurl REGULAR_EXPRESSION ${CURL_SOURCES_DIR}/.*)
 
-  #add_library(Curl STATIC ${CURL_SOURCES})
-  #link_libraries(Curl)  
-
   add_definitions(
+    -DBUILDING_LIBCURL=1
     -DCURL_STATICLIB=1
-    -DBUILDING_LIBCURL=1
     -DCURL_DISABLE_LDAPS=1
     -DCURL_DISABLE_LDAP=1
-    -D_WIN32_WINNT=0x0501
-
     -DCURL_DISABLE_DICT=1
     -DCURL_DISABLE_FILE=1
     -DCURL_DISABLE_FTP=1
@@ -32,7 +31,7 @@
     -DCURL_DISABLE_TFTP=1
     )
 
-  if (${ENABLE_SSL})
+  if (ENABLE_SSL)
     add_definitions(
       #-DHAVE_LIBSSL=1
       -DUSE_OPENSSL=1
@@ -40,8 +39,17 @@
       )
   endif()
 
+  file(WRITE ${CURL_SOURCES_DIR}/lib/curl_config.h "")
+
+  file(GLOB CURL_LIBS_HEADERS ${CURL_SOURCES_DIR}/lib/*.h)
+  foreach (header IN LISTS CURL_LIBS_HEADERS)
+    get_filename_component(filename ${header} NAME)
+    file(WRITE ${CURL_SOURCES_DIR}/lib/vtls/${filename} "#include \"../${filename}\"\n")
+  endforeach()
+
   if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
       ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
       ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
     if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
       SET(TMP_OS "x86_64")
@@ -51,7 +59,8 @@
 
     set_property(
       SOURCE ${CURL_SOURCES}
-      PROPERTY COMPILE_DEFINITIONS "HAVE_TIME_H;HAVE_STRUCT_TIMEVAL;HAVE_SYS_STAT_H;HAVE_SOCKET;HAVE_STRUCT_SOCKADDR_STORAGE;HAVE_SYS_SOCKET_H;HAVE_SOCKET;HAVE_SYS_SOCKET_H;HAVE_NETINET_IN_H;HAVE_NETDB_H;HAVE_FCNTL_O_NONBLOCK;HAVE_FCNTL_H;HAVE_SELECT;HAVE_ERRNO_H;HAVE_SEND;HAVE_RECV;OS=\"${TMP_OS}\"")
+      PROPERTY COMPILE_DEFINITIONS "HAVE_TIME_H;HAVE_STRUCT_TIMEVAL;HAVE_SYS_STAT_H;HAVE_SOCKET;HAVE_STRUCT_SOCKADDR_STORAGE;HAVE_SYS_SOCKET_H;HAVE_SOCKET;HAVE_SYS_SOCKET_H;HAVE_NETINET_IN_H;HAVE_NETDB_H;HAVE_FCNTL_O_NONBLOCK;HAVE_FCNTL_H;HAVE_SELECT;HAVE_ERRNO_H;HAVE_SEND;HAVE_RECV;HAVE_LONGLONG;OS=\"${TMP_OS}\""
+      )
 
     if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
       add_definitions(
--- a/Resources/CMake/LibIconvConfiguration.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/LibIconvConfiguration.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -1,8 +1,8 @@
 set(LIBICONV_SOURCES_DIR ${CMAKE_BINARY_DIR}/libiconv-1.14)
-DownloadPackage(
-  "e34509b1623cec449dfeb73d7ce9c6c6"
-  "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/libiconv-1.14.tar.gz"
-  "${LIBICONV_SOURCES_DIR}")
+set(LIBICONV_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/libiconv-1.14.tar.gz")
+set(LIBICONV_MD5 "e34509b1623cec449dfeb73d7ce9c6c6")
+
+DownloadPackage(${LIBICONV_MD5} ${LIBICONV_URL} "${LIBICONV_SOURCES_DIR}")
 
 # https://groups.google.com/d/msg/android-ndk/AS1nkxnk6m4/EQm09hD1tigJ
 add_definitions(
@@ -35,6 +35,9 @@
 unset(EILSEQ)
 unset(HAVE_WCHAR_T)   
 
+# Create an empty "config.h" for libiconv
+file(WRITE ${LIBICONV_SOURCES_DIR}/include/config.h "")
+
 include_directories(
   ${LIBICONV_SOURCES_DIR}/include
   )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/CMake/LibJpegConfiguration.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,93 @@
+if (STATIC_BUILD OR NOT USE_SYSTEM_LIBJPEG)
+  set(LIBJPEG_SOURCES_DIR ${CMAKE_BINARY_DIR}/jpeg-9a)
+  DownloadPackage(
+    "3353992aecaee1805ef4109aadd433e7"
+    "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/jpegsrc.v9a.tar.gz"
+    "${LIBJPEG_SOURCES_DIR}")
+
+  include_directories(
+    ${LIBJPEG_SOURCES_DIR}
+    )
+
+  list(APPEND LIBJPEG_SOURCES 
+    ${LIBJPEG_SOURCES_DIR}/jaricom.c
+    ${LIBJPEG_SOURCES_DIR}/jcapimin.c
+    ${LIBJPEG_SOURCES_DIR}/jcapistd.c
+    ${LIBJPEG_SOURCES_DIR}/jcarith.c
+    ${LIBJPEG_SOURCES_DIR}/jccoefct.c
+    ${LIBJPEG_SOURCES_DIR}/jccolor.c
+    ${LIBJPEG_SOURCES_DIR}/jcdctmgr.c
+    ${LIBJPEG_SOURCES_DIR}/jchuff.c
+    ${LIBJPEG_SOURCES_DIR}/jcinit.c
+    ${LIBJPEG_SOURCES_DIR}/jcmarker.c
+    ${LIBJPEG_SOURCES_DIR}/jcmaster.c
+    ${LIBJPEG_SOURCES_DIR}/jcomapi.c
+    ${LIBJPEG_SOURCES_DIR}/jcparam.c
+    ${LIBJPEG_SOURCES_DIR}/jcprepct.c
+    ${LIBJPEG_SOURCES_DIR}/jcsample.c
+    ${LIBJPEG_SOURCES_DIR}/jctrans.c
+    ${LIBJPEG_SOURCES_DIR}/jdapimin.c
+    ${LIBJPEG_SOURCES_DIR}/jdapistd.c
+    ${LIBJPEG_SOURCES_DIR}/jdarith.c
+    ${LIBJPEG_SOURCES_DIR}/jdatadst.c
+    ${LIBJPEG_SOURCES_DIR}/jdatasrc.c
+    ${LIBJPEG_SOURCES_DIR}/jdcoefct.c
+    ${LIBJPEG_SOURCES_DIR}/jdcolor.c
+    ${LIBJPEG_SOURCES_DIR}/jddctmgr.c
+    ${LIBJPEG_SOURCES_DIR}/jdhuff.c
+    ${LIBJPEG_SOURCES_DIR}/jdinput.c
+    ${LIBJPEG_SOURCES_DIR}/jcmainct.c
+    ${LIBJPEG_SOURCES_DIR}/jdmainct.c
+    ${LIBJPEG_SOURCES_DIR}/jdmarker.c
+    ${LIBJPEG_SOURCES_DIR}/jdmaster.c
+    ${LIBJPEG_SOURCES_DIR}/jdmerge.c
+    ${LIBJPEG_SOURCES_DIR}/jdpostct.c
+    ${LIBJPEG_SOURCES_DIR}/jdsample.c
+    ${LIBJPEG_SOURCES_DIR}/jdtrans.c
+    ${LIBJPEG_SOURCES_DIR}/jerror.c
+    ${LIBJPEG_SOURCES_DIR}/jfdctflt.c
+    ${LIBJPEG_SOURCES_DIR}/jfdctfst.c
+    ${LIBJPEG_SOURCES_DIR}/jfdctint.c
+    ${LIBJPEG_SOURCES_DIR}/jidctflt.c
+    ${LIBJPEG_SOURCES_DIR}/jidctfst.c
+    ${LIBJPEG_SOURCES_DIR}/jidctint.c
+    #${LIBJPEG_SOURCES_DIR}/jmemansi.c
+    #${LIBJPEG_SOURCES_DIR}/jmemdos.c
+    #${LIBJPEG_SOURCES_DIR}/jmemmac.c
+    ${LIBJPEG_SOURCES_DIR}/jmemmgr.c
+    #${LIBJPEG_SOURCES_DIR}/jmemname.c
+    ${LIBJPEG_SOURCES_DIR}/jmemnobs.c
+    ${LIBJPEG_SOURCES_DIR}/jquant1.c
+    ${LIBJPEG_SOURCES_DIR}/jquant2.c
+    ${LIBJPEG_SOURCES_DIR}/jutils.c
+
+    # ${LIBJPEG_SOURCES_DIR}/rdbmp.c
+    # ${LIBJPEG_SOURCES_DIR}/rdcolmap.c
+    # ${LIBJPEG_SOURCES_DIR}/rdgif.c
+    # ${LIBJPEG_SOURCES_DIR}/rdppm.c
+    # ${LIBJPEG_SOURCES_DIR}/rdrle.c
+    # ${LIBJPEG_SOURCES_DIR}/rdswitch.c
+    # ${LIBJPEG_SOURCES_DIR}/rdtarga.c
+    # ${LIBJPEG_SOURCES_DIR}/transupp.c
+    # ${LIBJPEG_SOURCES_DIR}/wrbmp.c
+    # ${LIBJPEG_SOURCES_DIR}/wrgif.c
+    # ${LIBJPEG_SOURCES_DIR}/wrppm.c
+    # ${LIBJPEG_SOURCES_DIR}/wrrle.c
+    # ${LIBJPEG_SOURCES_DIR}/wrtarga.c
+    )
+
+  configure_file(
+    ${LIBJPEG_SOURCES_DIR}/jconfig.txt
+    ${LIBJPEG_SOURCES_DIR}/jconfig.h COPYONLY
+    )
+
+else()
+  include(FindJPEG)
+
+  if (NOT ${JPEG_FOUND})
+    message(FATAL_ERROR "Unable to find libjpeg")
+  endif()
+
+  include_directories(${JPEG_INCLUDE_DIR})
+  link_libraries(${JPEG_LIBRARIES})
+endif()
--- a/Resources/CMake/LibPngConfiguration.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/LibPngConfiguration.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -1,9 +1,9 @@
 if (STATIC_BUILD OR NOT USE_SYSTEM_LIBPNG)
   SET(LIBPNG_SOURCES_DIR ${CMAKE_BINARY_DIR}/libpng-1.5.12)
-  DownloadPackage(
-    "8ea7f60347a306c5faf70b977fa80e28"
-    "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/libpng-1.5.12.tar.gz"
-    "${LIBPNG_SOURCES_DIR}")
+  SET(LIBPNG_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/libpng-1.5.12.tar.gz")
+  SET(LIBPNG_MD5 "8ea7f60347a306c5faf70b977fa80e28")
+
+  DownloadPackage(${LIBPNG_MD5} ${LIBPNG_URL} "${LIBPNG_SOURCES_DIR}")
 
   include_directories(
     ${LIBPNG_SOURCES_DIR}
@@ -12,7 +12,7 @@
   configure_file(
     ${LIBPNG_SOURCES_DIR}/scripts/pnglibconf.h.prebuilt
     ${LIBPNG_SOURCES_DIR}/pnglibconf.h
-    COPY_ONLY)
+    )
 
   set(LIBPNG_SOURCES
     #${LIBPNG_SOURCES_DIR}/example.c
@@ -38,11 +38,12 @@
   #  SOURCE ${LIBPNG_SOURCES}
   #  PROPERTY COMPILE_FLAGS -UHAVE_CONFIG_H)
 
-  list(APPEND THIRD_PARTY_SOURCES ${LIBPNG_SOURCES})
-
   add_definitions(
     -DPNG_NO_CONSOLE_IO=1
     -DPNG_NO_STDIO=1
+    # The following declaration avoids "__declspec(dllexport)" in
+    # libpng to prevent publicly exposing its symbols by the DLLs
+    -DPNG_IMPEXP=
     )
 
   source_group(ThirdParty\\Libpng REGULAR_EXPRESSION ${LIBPNG_SOURCES_DIR}/.*)
@@ -51,7 +52,7 @@
   include(FindPNG)
 
   if (NOT ${PNG_FOUND})
-    message(FATAL_ERROR "Unable to find LibPNG")
+    message(FATAL_ERROR "Unable to find libpng")
   endif()
 
   include_directories(${PNG_INCLUDE_DIRS})
--- a/Resources/CMake/LuaConfiguration.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/LuaConfiguration.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -1,9 +1,9 @@
 if (STATIC_BUILD OR NOT USE_SYSTEM_LUA)
   SET(LUA_SOURCES_DIR ${CMAKE_BINARY_DIR}/lua-5.1.5)
-  DownloadPackage(
-    "2e115fe26e435e33b0d5c022e4490567"
-    "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/lua-5.1.5.tar.gz"
-    "${LUA_SOURCES_DIR}")
+  SET(LUA_MD5 "2e115fe26e435e33b0d5c022e4490567")
+  SET(LUA_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/lua-5.1.5.tar.gz")
+
+  DownloadPackage(${LUA_MD5} ${LUA_URL} "${LUA_SOURCES_DIR}")
 
   add_definitions(
     #-DLUA_LIB=1
@@ -50,9 +50,6 @@
     ${LUA_SOURCES_DIR}/src/linit.c
     )
 
-  add_library(Lua STATIC ${LUA_SOURCES})
-  set(STATIC_LUA Lua)
-
   source_group(ThirdParty\\Lua REGULAR_EXPRESSION ${LUA_SOURCES_DIR}/.*)
 
 else()
--- a/Resources/CMake/MongooseConfiguration.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/MongooseConfiguration.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -1,6 +1,12 @@
 if (STATIC_BUILD OR NOT USE_SYSTEM_MONGOOSE)
   SET(MONGOOSE_SOURCES_DIR ${CMAKE_BINARY_DIR}/mongoose)
 
+  if (IS_DIRECTORY "${MONGOOSE_SOURCES_DIR}")
+    set(FirstRun OFF)
+  else()
+    set(FirstRun ON)
+  endif()
+
   if (0)
     # Use Mongoose 3.1
     DownloadPackage(
@@ -24,20 +30,26 @@
 
   # Patch mongoose
   execute_process(
-    COMMAND patch mongoose.c ${MONGOOSE_PATCH}
+    COMMAND ${PATCH_EXECUTABLE} -N mongoose.c 
+    INPUT_FILE ${MONGOOSE_PATCH}
     WORKING_DIRECTORY ${MONGOOSE_SOURCES_DIR}
+    RESULT_VARIABLE Failure
     )
 
+  if (Failure AND FirstRun)
+    message(FATAL_ERROR "Error while patching a file")
+  endif()
+
   include_directories(
     ${MONGOOSE_SOURCES_DIR}
     )
 
-  list(APPEND THIRD_PARTY_SOURCES
+  set(MONGOOSE_SOURCES
     ${MONGOOSE_SOURCES_DIR}/mongoose.c
     )
 
 
-  if (${ENABLE_SSL})
+  if (ENABLE_SSL)
     add_definitions(
       -DNO_SSL_DL=1
       )
@@ -54,7 +66,7 @@
 
 
   if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
-    if (${CMAKE_COMPILER_IS_GNUCXX})
+    if (CMAKE_COMPILER_IS_GNUCXX)
       # This is a patch for MinGW64
       add_definitions(-D_TIMESPEC_DEFINED=1)
     endif()
@@ -81,5 +93,3 @@
 
   link_libraries(mongoose)
 endif()
-
-
--- a/Resources/CMake/OpenSslConfiguration.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/OpenSslConfiguration.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -1,25 +1,20 @@
 if (STATIC_BUILD OR NOT USE_SYSTEM_OPENSSL)
-  SET(OPENSSL_SOURCES_DIR ${CMAKE_BINARY_DIR}/openssl-1.0.1g)
-  DownloadPackage(
-    "de62b43dfcd858e66a74bee1c834e959"
-    "www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/openssl-1.0.1g.tar.gz"
-    "${OPENSSL_SOURCES_DIR}")
+  # WARNING - We had to repack the upstream ".tar.gz" file to a ZIP
+  # file, as the upstream distribution ships symbolic links that are
+  # not always properly handled when uncompressing on Windows.
 
-  if (NOT EXISTS "${OPENSSL_SOURCES_DIR}/include/PATCHED")
-    if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
-      message("Patching the symbolic links")
-      # Patch the symbolic links by copying the files
-      file(GLOB headers "${OPENSSL_SOURCES_DIR}/include/openssl/*.h")
-      foreach(header ${headers})
-        message(${header})
-        file(READ "${header}" symbolicLink)
-        message(${symbolicLink})
-        configure_file("${OPENSSL_SOURCES_DIR}/include/openssl/${symbolicLink}" "${header}" COPYONLY)
-      endforeach()
-      file(WRITE "${OPENSSL_SOURCES_DIR}/include/PATCHED")
-    endif()
+  SET(OPENSSL_SOURCES_DIR ${CMAKE_BINARY_DIR}/openssl-1.0.2d)
+  SET(OPENSSL_URL "www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/openssl-1.0.2d.zip")
+  SET(OPENSSL_MD5 "4b2ac15fc6db17f3dadc54482d3eee85")
+
+  if (IS_DIRECTORY "${OPENSSL_SOURCES_DIR}")
+    set(FirstRun OFF)
+  else()
+    set(FirstRun ON)
   endif()
 
+  DownloadPackage(${OPENSSL_MD5} ${OPENSSL_URL} "${OPENSSL_SOURCES_DIR}")
+
   add_definitions(
     -DOPENSSL_THREADS
     -DOPENSSL_IA32_SSE2
@@ -188,15 +183,16 @@
 
   elseif ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
     execute_process(
-      COMMAND patch ui_openssl.c ${ORTHANC_ROOT}/Resources/Patches/openssl-lsb.diff
+      COMMAND ${PATCH_EXECUTABLE} -N ui_openssl.c -i ${ORTHANC_ROOT}/Resources/Patches/openssl-lsb.diff
       WORKING_DIRECTORY ${OPENSSL_SOURCES_DIR}/crypto/ui
+      RESULT_VARIABLE Failure
       )
 
+    if (Failure AND FirstRun)
+      message(FATAL_ERROR "Error while patching a file")
+    endif()
   endif()
 
-  #add_library(OpenSSL STATIC ${OPENSSL_SOURCES})
-  #link_libraries(OpenSSL)
-
 else()
   include(FindOpenSSL)
 
--- a/Resources/CMake/PugixmlConfiguration.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/PugixmlConfiguration.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -3,21 +3,17 @@
 
   if (STATIC_BUILD OR NOT USE_SYSTEM_PUGIXML)
     set(PUGIXML_SOURCES_DIR ${CMAKE_BINARY_DIR}/pugixml-1.4)
+    set(PUGIXML_MD5 "7c56c91cfe3ecdee248a8e4892ef5781")
+    set(PUGIXML_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/pugixml-1.4.tar.gz")
 
-    DownloadPackage(
-      "7c56c91cfe3ecdee248a8e4892ef5781"
-      "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/pugixml-1.4.tar.gz"
-      "${PUGIXML_SOURCES_DIR}")
+    DownloadPackage(${PUGIXML_MD5} ${PUGIXML_URL} "${PUGIXML_SOURCES_DIR}")
 
     include_directories(
       ${PUGIXML_SOURCES_DIR}/src
       )
 
     set(PUGIXML_SOURCES
-      ${PUGIXML_SOURCES_DIR}/src/vlog_is_on.cc
-      )
-
-    list(APPEND THIRD_PARTY_SOURCES 
+      #${PUGIXML_SOURCES_DIR}/src/vlog_is_on.cc
       ${PUGIXML_SOURCES_DIR}/src/pugixml.cpp
       )
 
--- a/Resources/CMake/SQLiteConfiguration.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/SQLiteConfiguration.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -1,11 +1,27 @@
-if (STATIC_BUILD OR NOT USE_SYSTEM_SQLITE)
+if (APPLE)
+  # Under OS X, the binaries must always be linked against the
+  # system-wide version of SQLite. Otherwise, if some Orthanc plugin
+  # also uses its own version of SQLite (such as orthanc-webviewer),
+  # this results in a crash in "sqlite3_mutex_enter(db->mutex);" (the
+  # mutex is not initialized), probably because the EXE and the DYNLIB
+  # share the same memory location for this mutex.
+  set(SQLITE_STATIC OFF)
+
+elseif (STATIC_BUILD OR NOT USE_SYSTEM_SQLITE)
+  set(SQLITE_STATIC ON)
+else()
+  set(SQLITE_STATIC OFF)
+endif()
+
+
+if (SQLITE_STATIC)
   SET(SQLITE_SOURCES_DIR ${CMAKE_BINARY_DIR}/sqlite-amalgamation-3071300)
-  DownloadPackage(
-    "5fbeff9645ab035a1f580e90b279a16d"
-    "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/sqlite-amalgamation-3071300.zip"
-    "${SQLITE_SOURCES_DIR}")
+  SET(SQLITE_MD5 "5fbeff9645ab035a1f580e90b279a16d")
+  SET(SQLITE_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/sqlite-amalgamation-3071300.zip")
 
-  list(APPEND THIRD_PARTY_SOURCES
+  DownloadPackage(${SQLITE_MD5} ${SQLITE_URL} "${SQLITE_SOURCES_DIR}")
+
+  set(SQLITE_SOURCES
     ${SQLITE_SOURCES_DIR}/sqlite3.c
     )
 
@@ -28,8 +44,14 @@
     message(FATAL_ERROR "Please install the libsqlite3-dev package")
   endif()
 
+  find_path(SQLITE_INCLUDE_DIR sqlite3.h
+    /usr/include
+    /usr/local/include
+    )
+  message("SQLite include dir: ${SQLITE_INCLUDE_DIR}")
+
   # Autodetection of the version of SQLite
-  file(STRINGS "/usr/include/sqlite3.h" SQLITE_VERSION_NUMBER1 REGEX "#define SQLITE_VERSION_NUMBER.*$")    
+  file(STRINGS "${SQLITE_INCLUDE_DIR}/sqlite3.h" SQLITE_VERSION_NUMBER1 REGEX "#define SQLITE_VERSION_NUMBER.*$")    
   string(REGEX REPLACE "#define SQLITE_VERSION_NUMBER(.*)$" "\\1" SQLITE_VERSION_NUMBER ${SQLITE_VERSION_NUMBER1})
 
   message("Detected version of SQLite: ${SQLITE_VERSION_NUMBER}")
--- a/Resources/CMake/VisualStudioPrecompiledHeaders.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/VisualStudioPrecompiledHeaders.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -1,6 +1,6 @@
 macro(ADD_VISUAL_STUDIO_PRECOMPILED_HEADERS PrecompiledHeaders PrecompiledSource Sources)
   get_filename_component(PrecompiledBasename ${PrecompiledHeaders} NAME_WE)
-  set(PrecompiledBinary "$(IntDir)/${PrecompiledBasename}.pch")
+  set(PrecompiledBinary "${PrecompiledBasename}_$(ConfigurationName).pch")
 
   set_source_files_properties(${PrecompiledSource}
     PROPERTIES COMPILE_FLAGS "/Yc\"${PrecompiledHeaders}\" /Fp\"${PrecompiledBinary}\""
--- a/Resources/CMake/ZlibConfiguration.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/CMake/ZlibConfiguration.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -1,21 +1,15 @@
-# This is the minizip distribution to create ZIP files
-list(APPEND THIRD_PARTY_SOURCES 
-  ${ORTHANC_ROOT}/Resources/ThirdParty/minizip/ioapi.c
-  ${ORTHANC_ROOT}/Resources/ThirdParty/minizip/zip.c
-  )
-
 if (STATIC_BUILD OR NOT USE_SYSTEM_ZLIB)
   SET(ZLIB_SOURCES_DIR ${CMAKE_BINARY_DIR}/zlib-1.2.7)
-  DownloadPackage(
-    "60df6a37c56e7c1366cca812414f7b85"
-    "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/zlib-1.2.7.tar.gz"
-    "${ZLIB_SOURCES_DIR}")
+  SET(ZLIB_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/zlib-1.2.7.tar.gz")
+  SET(ZLIB_MD5 "60df6a37c56e7c1366cca812414f7b85")
+
+  DownloadPackage(${ZLIB_MD5} ${ZLIB_URL} "${ZLIB_SOURCES_DIR}")
 
   include_directories(
     ${ZLIB_SOURCES_DIR}
     )
 
-  list(APPEND THIRD_PARTY_SOURCES 
+  list(APPEND ZLIB_SOURCES 
     ${ZLIB_SOURCES_DIR}/adler32.c
     ${ZLIB_SOURCES_DIR}/compress.c
     ${ZLIB_SOURCES_DIR}/crc32.c 
--- a/Resources/Configuration.json	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/Configuration.json	Wed Sep 30 13:23:31 2015 +0200
@@ -42,19 +42,41 @@
   ],
 
 
+
   /**
    * Configuration of the HTTP server
    **/
 
+  // Enable the HTTP server. If this parameter is set to "false",
+  // Orthanc acts as a pure DICOM server. The REST API and Orthanc
+  // Explorer will not be available.
+  "HttpServerEnabled" : true,
+
   // HTTP port for the REST services and for the GUI
   "HttpPort" : 8042,
 
+  // When the following option is "true", if an error is encountered
+  // while calling the REST API, a JSON message describing the error
+  // is put in the HTTP answer. This feature can be disabled if the
+  // HTTP client does not properly handles such answers.
+  "HttpDescribeErrors" : true,
+
+  // Enable HTTP compression to improve network bandwidth utilization,
+  // at the expense of more computations on the server. Orthanc
+  // supports the "gzip" and "deflate" HTTP encodings.
+  "HttpCompressionEnabled" : true,
+
 
 
   /**
    * Configuration of the DICOM server
    **/
 
+  // Enable the DICOM server. If this parameter is set to "false",
+  // Orthanc acts as a pure REST server. It will not be possible to
+  // receive files or to do query/retrieve through the DICOM protocol.
+  "DicomServerEnabled" : true,
+
   // The DICOM Application Entity Title
   "DicomAet" : "ORTHANC",
 
@@ -67,8 +89,8 @@
   // The default encoding that is assumed for DICOM files without
   // "SpecificCharacterSet" DICOM tag. The allowed values are "Ascii",
   // "Utf8", "Latin1", "Latin2", "Latin3", "Latin4", "Latin5",
-  // "Cyrillic", "Arabic", "Greek", "Hebrew", "Thai", "Japanese",
-  // and "Chinese".
+  // "Cyrillic", "Windows1251", "Arabic", "Greek", "Hebrew", "Thai",
+  // "Japanese", and "Chinese".
   "DefaultEncoding" : "Latin1",
 
   // The transfer syntaxes that are accepted by Orthanc C-Store SCP
@@ -81,6 +103,7 @@
   "RleTransferSyntaxAccepted"          : true,
 
 
+
   /**
    * Security-related options for the HTTP server
    **/
@@ -123,8 +146,8 @@
      * A fourth parameter is available to enable patches for a
      * specific PACS manufacturer. The allowed values are currently
      * "Generic" (default value), "StoreScp" (storescp tool from
-     * DCMTK), "ClearCanvas", "MedInria" and "Dcm4Chee". This
-     * parameter is case-sensitive.
+     * DCMTK), "ClearCanvas", "MedInria", "Dcm4Chee" and
+     * "SyngoVia". This parameter is case-sensitive.
      **/
     // "clearcanvas" : [ "CLEARCANVAS", "192.168.1.1", 104, "ClearCanvas" ]
   },
@@ -146,6 +169,21 @@
   //   "HttpProxy" : "proxyUser:proxyPassword@192.168.0.1:3128"
   "HttpProxy" : "",
 
+  // Set the timeout for HTTP requests issued by Orthanc (in seconds).
+  "HttpTimeout" : 10,
+
+  // Enable the verification of the peers during HTTPS requests.
+  // Reference: http://curl.haxx.se/docs/sslcerts.html
+  "HttpsVerifyPeers" : true,
+
+  // Path to the CA (certification authority) certificates to validate
+  // peers in HTTPS requests. From curl documentation ("--cacert"
+  // option): "Tells curl to use the specified certificate file to
+  // verify the peers. The file may contain multiple CA
+  // certificates. The certificate(s) must be in PEM format."
+  "HttpsCACertificates" : "",
+
+
 
   /**
    * Advanced options
@@ -169,16 +207,6 @@
   // patient, a study or a series is considered as stable.
   "StableAge" : 60,
 
-  // Enable the HTTP server. If this parameter is set to "false",
-  // Orthanc acts as a pure DICOM server. The REST API and Orthanc
-  // Explorer will not be available.
-  "HttpServerEnabled" : true,
-
-  // Enable the DICOM server. If this parameter is set to "false",
-  // Orthanc acts as a pure REST server. It will not be possible to
-  // receive files or to do query/retrieve through the DICOM protocol.
-  "DicomServerEnabled" : true,
-
   // By default, Orthanc compares AET (Application Entity Titles) in a
   // case-insensitive way. Setting this option to "true" will enable
   // case-sensitive matching.
@@ -222,5 +250,15 @@
   // are issued. This option sets the number of seconds of inactivity
   // to wait before automatically closing a DICOM association. If set
   // to 0, the connection is closed immediately.
-  "DicomAssociationCloseDelay" : 5
+  "DicomAssociationCloseDelay" : 5,
+
+  // Maximum number of query/retrieve DICOM requests that are
+  // maintained by Orthanc. The least recently used requests get
+  // deleted as new requests are issued.
+  "QueryRetrieveSize" : 10,
+
+  // When handling a C-Find SCP request, setting this flag to "false"
+  // will enable case-insensitive match for PN value representation
+  // (such as PatientName). By default, the search is case-insensitive.
+  "CaseSensitivePN" : false
 }
--- a/Resources/DicomConformanceStatement.py	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/DicomConformanceStatement.py	Wed Sep 30 13:23:31 2015 +0200
@@ -1,5 +1,37 @@
 #!/usr/bin/python
 
+# Orthanc - A Lightweight, RESTful DICOM Store
+# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, Belgium
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# In addition, as a special exception, the copyright holders of this
+# program give permission to link the code of its release with the
+# OpenSSL project's "OpenSSL" library (or with modified versions of it
+# that use the same license as the "OpenSSL" library), and distribute
+# the linked executables. You must obey the GNU General Public License
+# in all respects for all of the code used other than "OpenSSL". If you
+# modify file(s) with this exception, you may extend this exception to
+# your version of the file(s), but you are not obligated to do so. If
+# you do not wish to do so, delete this exception statement from your
+# version. If you delete this exception statement from all source files
+# in the program, then also delete it here.
+# 
+# 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+
+
 # This file injects the UID information into the DICOM conformance
 # statement of Orthanc
 
--- a/Resources/DicomConformanceStatement.txt	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/DicomConformanceStatement.txt	Wed Sep 30 13:23:31 2015 +0200
@@ -186,7 +186,16 @@
 
   FINDPatientRootQueryRetrieveInformationModel  | 1.2.840.10008.5.1.4.1.2.1.1
   FINDStudyRootQueryRetrieveInformationModel    | 1.2.840.10008.5.1.4.1.2.2.1
-  FINDStudyRootQueryRetrieveInformationModel    | 1.2.840.10008.5.1.4.1.2.2.1
+
+
+--------------------
+Move SCU Conformance
+--------------------
+
+Orthanc supports the following SOP Classes as an SCU for C-Move:
+
+  MOVEPatientRootQueryRetrieveInformationModel  | 1.2.840.10008.5.1.4.1.2.1.2
+  MOVEStudyRootQueryRetrieveInformationModel    | 1.2.840.10008.5.1.4.1.2.2.2
 
 
 -----------------
--- a/Resources/EmbedResources.py	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/EmbedResources.py	Wed Sep 30 13:23:31 2015 +0200
@@ -1,3 +1,5 @@
+#!/usr/bin/python
+
 # Orthanc - A Lightweight, RESTful DICOM Store
 # Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
 # Department, University Hospital of Liege, Belgium
@@ -35,16 +37,29 @@
 import re
 
 UPCASE_CHECK = True
+USE_SYSTEM_EXCEPTION = False
+EXCEPTION_CLASS = 'OrthancException'
+OUT_OF_RANGE_EXCEPTION = 'OrthancException(ErrorCode_ParameterOutOfRange)'
+INEXISTENT_PATH_EXCEPTION = 'OrthancException(ErrorCode_InexistentItem)'
+NAMESPACE = 'Orthanc'
+
 ARGS = []
 for i in range(len(sys.argv)):
     if not sys.argv[i].startswith('--'):
         ARGS.append(sys.argv[i])
     elif sys.argv[i].lower() == '--no-upcase-check':
         UPCASE_CHECK = False
+    elif sys.argv[i].lower() == '--system-exception':
+        USE_SYSTEM_EXCEPTION = True
+        EXCEPTION_CLASS = '::std::runtime_error'
+        OUT_OF_RANGE_EXCEPTION = '%s("Parameter out of range")' % EXCEPTION_CLASS
+        INEXISTENT_PATH_EXCEPTION = '%s("Unknown path in a directory resource")' % EXCEPTION_CLASS
+    elif sys.argv[i].startswith('--namespace='):
+        NAMESPACE = sys.argv[i][sys.argv[i].find('=') + 1 : ]
 
 if len(ARGS) < 2 or len(ARGS) % 2 != 0:
     print ('Usage:')
-    print ('python %s [--no-upcase-check] <TargetBaseFilename> [ <Name> <Source> ]*' % sys.argv[0])
+    print ('python %s [--no-upcase-check] [--system-exception] [--namespace=<Namespace>] <TargetBaseFilename> [ <Name> <Source> ]*' % sys.argv[0])
     exit(-1)
 
 TARGET_BASE_FILENAME = ARGS[1]
@@ -144,13 +159,13 @@
 #include <string>
 #include <list>
 
-namespace Orthanc
+namespace %s
 {
   namespace EmbeddedResources
   {
     enum FileResourceId
     {
-""")
+""" % NAMESPACE)
 
 isFirst = True
 for name in resources:
@@ -229,25 +244,32 @@
         cpp.write("0x%02x" % c)
         pos += 1
 
+    # Zero-size array are disallowed, so we put one single void character in it.
+    if pos == 0:
+        cpp.write('  0')
+
     cpp.write('  };\n')
     cpp.write('    static const size_t resource%dSize = %d;\n' % (item['Index'], pos))
 
 
 cpp = open(TARGET_BASE_FILENAME + '.cpp', 'w')
 
+cpp.write('#include "%s.h"\n' % os.path.basename(TARGET_BASE_FILENAME))
+
+if USE_SYSTEM_EXCEPTION:
+    cpp.write('#include <stdexcept>')
+else:
+    cpp.write('#include "%s/Core/OrthancException.h"' % os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
+
 cpp.write("""
-#include "%s.h"
-#include "%s/Core/OrthancException.h"
-
 #include <stdint.h>
 #include <string.h>
 
-namespace Orthanc
+namespace %s
 {
   namespace EmbeddedResources
   {
-""" % (os.path.basename(TARGET_BASE_FILENAME),
-       os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))))
+""" % NAMESPACE)
 
 
 for name in resources:
@@ -276,7 +298,7 @@
 
 cpp.write("""
       default:
-        throw OrthancException(ErrorCode_ParameterOutOfRange);
+        throw %s;
       }
     }
 
@@ -284,7 +306,7 @@
     {
       switch (id)
       {
-""")
+""" % OUT_OF_RANGE_EXCEPTION)
 
 for name in resources:
     if resources[name]['Type'] == 'File':
@@ -293,10 +315,10 @@
 
 cpp.write("""
       default:
-        throw OrthancException(ErrorCode_ParameterOutOfRange);
+        throw %s;
       }
     }
-""")
+""" % OUT_OF_RANGE_EXCEPTION)
 
 
 
@@ -318,10 +340,10 @@
         for path in resources[name]['Files']:
             cpp.write('        if (!strcmp(path, "%s"))\n' % path)
             cpp.write('          return resource%dBuffer;\n' % resources[name]['Files'][path]['Index'])
-        cpp.write('        throw OrthancException("Unknown path in a directory resource");\n\n')
+        cpp.write('        throw %s;\n\n' % INEXISTENT_PATH_EXCEPTION)
 
 cpp.write("""      default:
-        throw OrthancException(ErrorCode_ParameterOutOfRange);
+        throw %s;
       }
     }
 
@@ -329,7 +351,7 @@
     {
       switch (id)
       {
-""")
+""" % OUT_OF_RANGE_EXCEPTION)
 
 for name in resources:
     if resources[name]['Type'] == 'Directory':
@@ -338,13 +360,13 @@
         for path in resources[name]['Files']:
             cpp.write('        if (!strcmp(path, "%s"))\n' % path)
             cpp.write('          return resource%dSize;\n' % resources[name]['Files'][path]['Index'])
-        cpp.write('        throw OrthancException("Unknown path in a directory resource");\n\n')
+        cpp.write('        throw %s;\n\n' % INEXISTENT_PATH_EXCEPTION)
 
 cpp.write("""      default:
-        throw OrthancException(ErrorCode_ParameterOutOfRange);
+        throw %s;
       }
     }
-""")
+""" % OUT_OF_RANGE_EXCEPTION)
 
 
 
@@ -370,10 +392,10 @@
         cpp.write('        break;\n\n')
 
 cpp.write("""      default:
-        throw OrthancException(ErrorCode_ParameterOutOfRange);
+        throw %s;
       }
     }
-""")
+""" % OUT_OF_RANGE_EXCEPTION)
 
 
 
--- a/Resources/EncodingTests.h	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/EncodingTests.h	Wed Sep 30 13:23:31 2015 +0200
@@ -1,4 +1,4 @@
-static const unsigned int testEncodingsCount = 14;
+static const unsigned int testEncodingsCount = 18;
 static const ::Orthanc::Encoding testEncodings[] = {
   ::Orthanc::Encoding_Latin5,
   ::Orthanc::Encoding_Hebrew,
@@ -13,9 +13,13 @@
   ::Orthanc::Encoding_Thai,
   ::Orthanc::Encoding_Japanese,
   ::Orthanc::Encoding_Ascii,
-  ::Orthanc::Encoding_Chinese
+  ::Orthanc::Encoding_Windows1251,
+  ::Orthanc::Encoding_Chinese,
+  ::Orthanc::Encoding_Windows1251,
+  ::Orthanc::Encoding_Windows1251,
+  ::Orthanc::Encoding_Windows1251
 };
-static const char *testEncodingsEncoded[14] = {
+static const char *testEncodingsEncoded[18] = {
   "\x54\x65\x73\x74\xe9\xe4\xf6\xf2\xdd",
   "\x54\x65\x73\x74\xe3",
   "\x54\x65\x73\x74\xc8",
@@ -29,9 +33,13 @@
   "\x54\x65\x73\x74\xfb",
   "\x54\x65\x73\x74\x84\x44\x83\xa6\xc8",
   "\x54\x65\x73\x74",
-  "\x81\x30\x89\x37\x81\x30\x89\x38\xA8\xA4\xA8\xA2\x81\x30\x89\x39\x81\x30\x8A\x30"
+  "\x54\x65\x73\x74\xc4\x9e",
+  "\x81\x30\x89\x37\x81\x30\x89\x38\xA8\xA4\xA8\xA2\x81\x30\x89\x39\x81\x30\x8A\x30",
+  "\xd0\xe5\xed\xf2\xe3\xe5\xed\xee\xe3\xf0\xe0\xf4\xe8\xff",
+  "\xD2\xE0\xE7",
+  "\xcf\xf0\xff\xec\xe0\xff"
 };
-static const char *testEncodingsExpected[14] = {
+static const char *testEncodingsExpected[18] = {
   "\x54\x65\x73\x74\xc3\xa9\xc3\xa4\xc3\xb6\xc3\xb2\xc4\xb0",
   "\x54\x65\x73\x74\xd7\x93",
   "\x54\x65\x73\x74\xce\x98",
@@ -45,5 +53,9 @@
   "\x54\x65\x73\x74\xe0\xb9\x9b",
   "\x54\x65\x73\x74\xd0\x94\xce\x98\xef\xbe\x88",
   "\x54\x65\x73\x74",
-  "\xc3\x9e\xc3\x9f\xc3\xa0\xc3\xa1\xc3\xa2\xc3\xa3"
+  "\x54\x65\x73\x74\xd0\x94\xd1\x9b",
+  "\xc3\x9e\xc3\x9f\xc3\xa0\xc3\xa1\xc3\xa2\xc3\xa3",
+  "\xd0\xa0\xd0\xb5\xd0\xbd\xd1\x82\xd0\xb3\xd0\xb5\xd0\xbd\xd0\xbe\xd0\xb3\xd1\x80\xd0\xb0\xd1\x84\xd0\xb8\xd1\x8f",
+  "\xd0\xa2\xd0\xb0\xd0\xb7",
+  "\xd0\x9f\xd1\x80\xd1\x8f\xd0\xbc\xd0\xb0\xd1\x8f"
 };
--- a/Resources/EncodingTests.py	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/EncodingTests.py	Wed Sep 30 13:23:31 2015 +0200
@@ -12,6 +12,7 @@
     'ISO-8859-4' : 'Latin4',
     'ISO-8859-9' : 'Latin5',
     'ISO-8859-5' : 'Cyrillic',
+    'WINDOWS-1251' : 'Windows1251',
     'ISO-8859-6' : 'Arabic',
     'ISO-8859-7' : 'Greek',
     'ISO-8859-8' : 'Hebrew',
@@ -49,6 +50,18 @@
 expected.append(ToArray('Þßàáâã'))
 encoded.append('"\\x81\\x30\\x89\\x37\\x81\\x30\\x89\\x38\\xA8\\xA4\\xA8\\xA2\\x81\\x30\\x89\\x39\\x81\\x30\\x8A\\x30"')
 
+# Issue 32
+# "encoded" is the copy/paste from "dcm2xml +Ca cyrillic Issue32.dcm"
+l.append('::Orthanc::Encoding_Windows1251')
+encoded.append('"\\xd0\\xe5\\xed\\xf2\\xe3\\xe5\\xed\\xee\\xe3\\xf0\\xe0\\xf4\\xe8\\xff"')
+expected.append(ToArray('Рентгенография'))
+l.append('::Orthanc::Encoding_Windows1251')
+encoded.append('"\\xD2\\xE0\\xE7"')
+expected.append(ToArray('Таз'))
+l.append('::Orthanc::Encoding_Windows1251')
+encoded.append('"\\xcf\\xf0\\xff\\xec\\xe0\\xff"')
+expected.append(ToArray('Прямая'))
+
 
 if True:
     print 'static const unsigned int testEncodingsCount = %d;' % len(l)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/ErrorCodes.json	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,496 @@
+[
+  /** Generic error codes **/
+
+  {
+    "Code": -1, 
+    "Name": "InternalError", 
+    "Description": "Internal error"
+  }, 
+  {
+    "Code": 0, 
+    "HttpStatus": 200, 
+    "Name": "Success", 
+    "Description": "Success"
+  }, 
+  {
+    "Code": 1, 
+    "Name": "Plugin", 
+    "Description": "Error encountered within the plugin engine"
+  },
+  {
+    "Code": 2, 
+    "Name": "NotImplemented", 
+    "Description": "Not implemented yet"
+  }, 
+  {
+    "Code": 3, 
+    "HttpStatus": 400, 
+    "Name": "ParameterOutOfRange", 
+    "Description": "Parameter out of range",
+    "SQLite": true
+  }, 
+  {
+    "Code": 4, 
+    "Name": "NotEnoughMemory", 
+    "Description": "Not enough memory"
+  }, 
+  {
+    "Code": 5, 
+    "HttpStatus": 400, 
+    "Name": "BadParameterType", 
+    "Description": "Bad type for a parameter",
+    "SQLite": true
+  }, 
+  {
+    "Code": 6, 
+    "Name": "BadSequenceOfCalls", 
+    "Description": "Bad sequence of calls"
+  }, 
+  {
+    "Code": 7, 
+    "HttpStatus": 404, 
+    "Name": "InexistentItem", 
+    "Description": "Accessing an inexistent item"
+  }, 
+  {
+    "Code": 8, 
+    "HttpStatus": 400, 
+    "Name": "BadRequest", 
+    "Description": "Bad request"
+  }, 
+  {
+    "Code": 9, 
+    "Name": "NetworkProtocol", 
+    "Description": "Error in the network protocol"
+  }, 
+  {
+    "Code": 10, 
+    "Name": "SystemCommand", 
+    "Description": "Error while calling a system command"
+  }, 
+  {
+    "Code": 11, 
+    "Name": "Database", 
+    "Description": "Error with the database engine"
+  }, 
+  {
+    "Code": 12, 
+    "HttpStatus": 400, 
+    "Name": "UriSyntax", 
+    "Description": "Badly formatted URI"
+  }, 
+  {
+    "Code": 13, 
+    "HttpStatus": 404, 
+    "Name": "InexistentFile", 
+    "Description": "Inexistent file"
+  }, 
+  {
+    "Code": 14, 
+    "Name": "CannotWriteFile", 
+    "Description": "Cannot write to file"
+  }, 
+  {
+    "Code": 15, 
+    "HttpStatus": 400, 
+    "Name": "BadFileFormat", 
+    "Description": "Bad file format"
+  }, 
+  {
+    "Code": 16, 
+    "Name": "Timeout", 
+    "Description": "Timeout"
+  }, 
+  {
+    "Code": 17, 
+    "HttpStatus": 404, 
+    "Name": "UnknownResource", 
+    "Description": "Unknown resource"
+  }, 
+  {
+    "Code": 18, 
+    "Name": "IncompatibleDatabaseVersion", 
+    "Description": "Incompatible version of the database"
+  }, 
+  {
+    "Code": 19, 
+    "Name": "FullStorage", 
+    "Description": "The file storage is full"
+  }, 
+  {
+    "Code": 20, 
+    "Name": "CorruptedFile", 
+    "Description": "Corrupted file (e.g. inconsistent MD5 hash)"
+  }, 
+  {
+    "Code": 21, 
+    "HttpStatus": 404, 
+    "Name": "InexistentTag", 
+    "Description": "Inexistent tag"
+  }, 
+  {
+    "Code": 22, 
+    "Name": "ReadOnly", 
+    "Description": "Cannot modify a read-only data structure"
+  }, 
+  {
+    "Code": 23, 
+    "Name": "IncompatibleImageFormat", 
+    "Description": "Incompatible format of the images"
+  }, 
+  {
+    "Code": 24, 
+    "Name": "IncompatibleImageSize", 
+    "Description": "Incompatible size of the images"
+  }, 
+  {
+    "Code": 25, 
+    "Name": "SharedLibrary", 
+    "Description": "Error while using a shared library (plugin)"
+  }, 
+  {
+    "Code": 26, 
+    "Name": "UnknownPluginService", 
+    "Description": "Plugin invoking an unknown service"
+  }, 
+  {
+    "Code": 27, 
+    "Name": "UnknownDicomTag", 
+    "Description": "Unknown DICOM tag"
+  }, 
+  {
+    "Code": 28, 
+    "HttpStatus": 400, 
+    "Name": "BadJson", 
+    "Description": "Cannot parse a JSON document"
+  }, 
+  {
+    "Code": 29, 
+    "HttpStatus": 401, 
+    "Name": "Unauthorized", 
+    "Description": "Bad credentials were provided to an HTTP request"
+  }, 
+  {
+    "Code": 30, 
+    "Name": "BadFont", 
+    "Description": "Badly formatted font file"
+  },
+  {
+    "Code": 31, 
+    "Name": "DatabasePlugin", 
+    "Description": "The plugin implementing a custom database back-end does not fulfill the proper interface"
+  }, 
+  {
+    "Code": 32, 
+    "Name": "StorageAreaPlugin", 
+    "Description": "Error in the plugin implementing a custom storage area"
+  },
+
+
+
+  /** SQLite **/
+
+
+  {
+    "Code": 1000, 
+    "Name": "SQLiteNotOpened",
+    "Description": "SQLite: The database is not opened",
+    "SQLite": true
+  },
+  {
+    "Code": 1001, 
+    "Name": "SQLiteAlreadyOpened", 
+    "Description": "SQLite: Connection is already open",
+    "SQLite": true
+  },
+  {
+    "Code": 1002, 
+    "Name": "SQLiteCannotOpen", 
+    "Description": "SQLite: Unable to open the database",
+    "SQLite": true
+  },
+  {
+    "Code": 1003, 
+    "Name": "SQLiteStatementAlreadyUsed", 
+    "Description": "SQLite: This cached statement is already being referred to",
+    "SQLite": true
+  },
+  {
+    "Code": 1004, 
+    "Name": "SQLiteExecute", 
+    "Description": "SQLite: Cannot execute a command",
+    "SQLite": true
+  },
+  {
+    "Code": 1005, 
+    "Name": "SQLiteRollbackWithoutTransaction", 
+    "Description": "SQLite: Rolling back a nonexistent transaction (have you called Begin()?)",
+    "SQLite": true
+  },
+  {
+    "Code": 1006, 
+    "Name": "SQLiteCommitWithoutTransaction", 
+    "Description": "SQLite: Committing a nonexistent transaction",
+    "SQLite": true
+  },
+  {
+    "Code": 1007, 
+    "Name": "SQLiteRegisterFunction", 
+    "Description": "SQLite: Unable to register a function",
+    "SQLite": true
+  },
+  {
+    "Code": 1008, 
+    "Name": "SQLiteFlush", 
+    "Description": "SQLite: Unable to flush the database",
+    "SQLite": true
+  },
+  {
+    "Code": 1009, 
+    "Name": "SQLiteCannotRun", 
+    "Description": "SQLite: Cannot run a cached statement",
+    "SQLite": true
+  },
+  {
+    "Code": 1010, 
+    "Name": "SQLiteCannotStep", 
+    "Description": "SQLite: Cannot step over a cached statement",
+    "SQLite": true
+  },
+  {
+    "Code": 1011, 
+    "Name": "SQLiteBindOutOfRange", 
+    "Description": "SQLite: Bing a value while out of range (serious error)",
+    "SQLite": true
+  },
+  {
+    "Code": 1012, 
+    "Name": "SQLitePrepareStatement", 
+    "Description": "SQLite: Cannot prepare a cached statement",
+    "SQLite": true
+  },
+  {
+    "Code": 1013, 
+    "Name": "SQLiteTransactionAlreadyStarted", 
+    "Description": "SQLite: Beginning the same transaction twice",
+    "SQLite": true
+  },
+  {
+    "Code": 1014, 
+    "Name": "SQLiteTransactionCommit", 
+    "Description": "SQLite: Failure when committing the transaction",
+    "SQLite": true
+  },
+  {
+    "Code": 1015, 
+    "Name": "SQLiteTransactionBegin", 
+    "Description": "SQLite: Cannot start a transaction",
+    "SQLite": true
+  },
+
+
+
+
+
+
+
+
+  /** Specific error codes **/
+
+  
+  {
+    "Code": 2000, 
+    "Name": "DirectoryOverFile", 
+    "Description": "The directory to be created is already occupied by a regular file"
+  },
+  {
+    "Code": 2001, 
+    "Name": "FileStorageCannotWrite", 
+    "Description": "Unable to create a subdirectory or a file in the file storage"
+  },
+  {
+    "Code": 2002, 
+    "Name": "DirectoryExpected", 
+    "Description": "The specified path does not point to a directory"
+  },
+  {
+    "Code": 2003, 
+    "Name": "HttpPortInUse", 
+    "Description": "The TCP port of the HTTP server is already in use"
+  },
+  {
+    "Code": 2004, 
+    "Name": "DicomPortInUse", 
+    "Description": "The TCP port of the DICOM server is already in use"
+  },
+  {
+    "Code": 2005, 
+    "Name": "BadHttpStatusInRest", 
+    "Description": "This HTTP status is not allowed in a REST API"
+  },
+  {
+    "Code": 2006, 
+    "Name": "RegularFileExpected", 
+    "Description": "The specified path does not point to a regular file"
+  },
+  {
+    "Code": 2007, 
+    "Name": "PathToExecutable", 
+    "Description": "Unable to get the path to the executable"
+  },
+  {
+    "Code": 2008, 
+    "Name": "MakeDirectory", 
+    "Description": "Cannot create a directory"
+  },
+  {
+    "Code": 2009, 
+    "Name": "BadApplicationEntityTitle", 
+    "Description": "An application entity title (AET) cannot be empty or be longer than 16 characters"
+  },
+  {
+    "Code": 2010, 
+    "Name": "NoCFindHandler", 
+    "Description": "No request handler factory for DICOM C-FIND SCP"
+  },
+  {
+    "Code": 2011, 
+    "Name": "NoCMoveHandler", 
+    "Description": "No request handler factory for DICOM C-MOVE SCP"
+  },
+  {
+    "Code": 2012, 
+    "Name": "NoCStoreHandler", 
+    "Description": "No request handler factory for DICOM C-STORE SCP"
+  },
+  {
+    "Code": 2013, 
+    "Name": "NoApplicationEntityFilter", 
+    "Description": "No application entity filter"
+  },
+  {
+    "Code": 2014, 
+    "Name": "NoSopClassOrInstance", 
+    "Description": "DicomUserConnection: Unable to find the SOP class and instance"
+  },
+  {
+    "Code": 2015, 
+    "Name": "NoPresentationContext", 
+    "Description": "DicomUserConnection: No acceptable presentation context for modality"
+  },
+  {
+    "Code": 2016, 
+    "Name": "DicomFindUnavailable", 
+    "Description": "DicomUserConnection: The C-FIND command is not supported by the remote SCP"
+  },
+  {
+    "Code": 2017, 
+    "Name": "DicomMoveUnavailable", 
+    "Description": "DicomUserConnection: The C-MOVE command is not supported by the remote SCP"
+  },
+  {
+    "Code": 2018, 
+    "Name": "CannotStoreInstance", 
+    "Description": "Cannot store an instance"
+  },
+  {
+    "Code": 2019, 
+    "Name": "CreateDicomNotString", 
+    "Description": "Only string values are supported when creating DICOM instances"
+  },
+  {
+    "Code": 2020, 
+    "Name": "CreateDicomOverrideTag", 
+    "Description": "Trying to override a value inherited from a parent module"
+  },
+  {
+    "Code": 2021, 
+    "Name": "CreateDicomUseContent", 
+    "Description": "Use \\\"Content\\\" to inject an image into a new DICOM instance"
+  },
+  {
+    "Code": 2022, 
+    "Name": "CreateDicomNoPayload", 
+    "Description": "No payload is present for one instance in the series"
+  },
+  {
+    "Code": 2023, 
+    "Name": "CreateDicomUseDataUriScheme", 
+    "Description": "The payload of the DICOM instance must be specified according to Data URI scheme"
+  },
+  {
+    "Code": 2024, 
+    "Name": "CreateDicomBadParent", 
+    "Description": "Trying to attach a new DICOM instance to an inexistent resource"
+  },
+  {
+    "Code": 2025, 
+    "Name": "CreateDicomParentIsInstance", 
+    "Description": "Trying to attach a new DICOM instance to an instance (must be a series, study or patient)"
+  },
+  {
+    "Code": 2026, 
+    "Name": "CreateDicomParentEncoding", 
+    "Description": "Unable to get the encoding of the parent resource"
+  },
+  {
+    "Code": 2027, 
+    "Name": "UnknownModality", 
+    "Description": "Unknown modality"
+  },
+  {
+    "Code": 2028, 
+    "Name": "BadJobOrdering", 
+    "Description": "Bad ordering of filters in a job"
+  },
+  {
+    "Code": 2029, 
+    "Name": "JsonToLuaTable", 
+    "Description": "Cannot convert the given JSON object to a Lua table"
+  },
+  {
+    "Code": 2030, 
+    "Name": "CannotCreateLua", 
+    "Description": "Cannot create the Lua context"
+  },
+  {
+    "Code": 2031, 
+    "Name": "CannotExecuteLua", 
+    "Description": "Cannot execute a Lua command"
+  },
+  {
+    "Code": 2032, 
+    "Name": "LuaAlreadyExecuted", 
+    "Description": "Arguments cannot be pushed after the Lua function is executed"
+  },
+  {
+    "Code": 2033, 
+    "Name": "LuaBadOutput", 
+    "Description": "The Lua function does not give the expected number of outputs"
+  },
+  {
+    "Code": 2034, 
+    "Name": "NotLuaPredicate", 
+    "Description": "The Lua function is not a predicate (only true/false outputs allowed)"
+  },
+  {
+    "Code": 2035, 
+    "Name": "LuaReturnsNoString", 
+    "Description": "The Lua function does not return a string"
+  },
+  {
+    "Code": 2036,
+    "Name": "StorageAreaAlreadyRegistered",
+    "Description": "Another plugin has already registered a custom storage area"
+  },
+  {
+    "Code": 2037,
+    "Name": "DatabaseBackendAlreadyRegistered",
+    "Description": "Another plugin has already registered a custom database back-end"
+  },
+  {
+    "Code": 2038,
+    "Name": "DatabaseNotInitialized",
+    "Description": "Plugin trying to call the database during its initialization"
+  }
+]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Fonts/GenerateFont.py	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,113 @@
+#!/usr/bin/python
+
+# Orthanc - A Lightweight, RESTful DICOM Store
+# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, Belgium
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# In addition, as a special exception, the copyright holders of this
+# program give permission to link the code of its release with the
+# OpenSSL project's "OpenSSL" library (or with modified versions of it
+# that use the same license as the "OpenSSL" library), and distribute
+# the linked executables. You must obey the GNU General Public License
+# in all respects for all of the code used other than "OpenSSL". If you
+# modify file(s) with this exception, you may extend this exception to
+# your version of the file(s), but you are not obligated to do so. If
+# you do not wish to do so, delete this exception statement from your
+# version. If you delete this exception statement from all source files
+# in the program, then also delete it here.
+# 
+# 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+
+
+# sudo pip install freetype-py
+
+
+
+import freetype
+import json
+import os
+import sys
+import unicodedata
+
+
+if len(sys.argv) != 3:
+    print('Usage: %s <Font> <Size>\n' % sys.argv[0])
+    print('Example: %s /usr/share/fonts/truetype/ubuntu-font-family/UbuntuMono-B.ttf 16\n' % sys.argv[0])
+    sys.exit(-1)
+
+
+
+FONT = sys.argv[1]
+PIXEL_SIZE = int(sys.argv[2])
+CHARSET = 'latin-1'
+
+
+# Load the font
+face = freetype.Face(FONT)
+face.set_char_size(PIXEL_SIZE * 64)
+
+# Generate all the characters between 0 and 255
+characters = ''.join(map(chr, range(0, 256)))
+
+# Interpret the string using the required charset
+characters = characters.decode(CHARSET, 'ignore')
+
+# Keep only non-control characters
+characters = filter(lambda c: unicodedata.category(c)[0] != 'C', characters)
+
+font = {
+    'Name' : os.path.basename(FONT),
+    'Size' : PIXEL_SIZE,
+    'Characters' : {}
+}
+
+
+def PrintCharacter(c):
+    pos = 0
+    for i in range(c['Height']):
+        s = ''
+        for j in range(c['Width']):
+            if c['Bitmap'][pos] > 127:
+                s += '*'
+            else:
+                s += ' '
+            pos += 1
+        print s
+
+
+for c in characters:
+    face.load_char(c)
+
+    info = {
+        'Width' : face.glyph.bitmap.width,
+        'Height' : face.glyph.bitmap.rows,
+        'Advance' : face.glyph.metrics.horiAdvance / 64,
+        'Top' : -face.glyph.metrics.horiBearingY / 64,
+        'Bitmap' : face.glyph.bitmap.buffer,
+    }
+
+    font['Characters'][ord(c)] = info
+
+    #PrintCharacter(info)
+
+minTop = min(map(lambda (k, v): v['Top'], font['Characters'].iteritems()))
+for c in font['Characters']:
+    font['Characters'][c]['Top'] -= minTop
+
+font['MaxAdvance'] = max(map(lambda (k, v): v['Advance'], font['Characters'].iteritems()))
+font['MaxHeight'] = max(map(lambda (k, v): v['Height'], font['Characters'].iteritems()))
+
+print json.dumps(font)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Fonts/README.txt	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,6 @@
+This file contains a precompiled version of several open-source fonts,
+that are used to draw text on images within Orthanc. These fonts are
+encoded as JSON files through the "./GenerateFont.py" script.
+
+- UbuntuMonoBold-16.json is the Ubuntu Mono Bold, size 16, licensed
+  under the Ubuntu Font Licence.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Fonts/UbuntuMonoBold-16.json	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,1 @@
+{"MaxAdvance": 9, "MaxHeight": 15, "Name": "UbuntuMono-B.ttf", "Characters": {"32": {"Width": 0, "Advance": 8, "Bitmap": [], "Top": 14, "Height": 0}, "33": {"Width": 2, "Advance": 8, "Bitmap": [255, 255, 255, 255, 255, 255, 254, 251, 245, 242, 224, 223, 196, 199, 0, 0, 199, 198, 199, 198], "Top": 4, "Height": 10}, "34": {"Width": 5, "Advance": 8, "Bitmap": [255, 255, 0, 255, 255, 248, 247, 0, 248, 247, 226, 226, 0, 226, 226, 197, 196, 0, 197, 196, 160, 160, 0, 160, 162], "Top": 3, "Height": 5}, "35": {"Width": 8, "Advance": 8, "Bitmap": [0, 0, 31, 255, 254, 255, 225, 0, 0, 0, 93, 255, 250, 255, 163, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 234, 255, 255, 255, 21, 0, 0, 25, 255, 253, 255, 231, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 163, 243, 161, 255, 93, 0, 0, 0, 225, 94, 105, 255, 31, 0, 0], "Top": 4, "Height": 10}, "36": {"Width": 6, "Advance": 8, "Bitmap": [0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 8, 0, 33, 198, 255, 255, 248, 169, 190, 255, 255, 255, 255, 202, 246, 255, 46, 8, 44, 66, 216, 255, 231, 148, 27, 0, 71, 243, 255, 255, 239, 59, 0, 23, 115, 205, 255, 205, 82, 56, 10, 42, 255, 246, 211, 255, 255, 255, 255, 209, 120, 227, 255, 255, 245, 72, 0, 0, 255, 255, 15, 0, 0, 0, 255, 255, 0, 0], "Top": 3, "Height": 13}, "37": {"Width": 7, "Advance": 8, "Bitmap": [127, 242, 123, 0, 0, 77, 179, 240, 57, 239, 0, 8, 213, 33, 240, 55, 239, 0, 128, 127, 0, 126, 243, 123, 34, 213, 8, 0, 0, 0, 0, 179, 76, 0, 0, 0, 0, 77, 179, 0, 0, 0, 0, 8, 213, 33, 126, 59, 124, 0, 128, 127, 0, 240, 6, 239, 34, 213, 8, 0, 240, 68, 240, 179, 76, 0, 0, 128, 243, 126], "Top": 4, "Height": 10}, "38": {"Width": 9, "Advance": 8, "Bitmap": [0, 39, 190, 245, 206, 57, 0, 0, 0, 0, 201, 255, 255, 255, 219, 0, 0, 0, 0, 242, 255, 55, 255, 233, 0, 0, 0, 0, 184, 255, 116, 255, 110, 0, 0, 0, 0, 128, 255, 255, 130, 6, 188, 52, 0, 102, 255, 103, 202, 235, 70, 255, 70, 0, 225, 255, 11, 26, 231, 249, 255, 45, 0, 240, 255, 82, 17, 174, 255, 255, 17, 0, 162, 255, 255, 255, 255, 255, 255, 125, 0, 14, 152, 229, 240, 188, 114, 255, 235, 7], "Top": 4, "Height": 10}, "39": {"Width": 2, "Advance": 8, "Bitmap": [255, 255, 251, 250, 230, 230, 200, 199, 161, 163], "Top": 3, "Height": 5}, "40": {"Width": 5, "Advance": 8, "Bitmap": [0, 0, 9, 152, 142, 0, 12, 199, 253, 110, 0, 166, 255, 118, 0, 46, 255, 207, 2, 0, 146, 255, 106, 0, 0, 209, 255, 40, 0, 0, 239, 255, 12, 0, 0, 242, 255, 14, 0, 0, 213, 255, 43, 0, 0, 157, 255, 117, 0, 0, 58, 255, 219, 8, 0, 0, 180, 255, 153, 0, 0, 17, 207, 255, 160, 0, 0, 12, 157, 119], "Top": 3, "Height": 14}, "41": {"Width": 6, "Advance": 8, "Bitmap": [120, 194, 49, 0, 0, 0, 82, 230, 250, 103, 0, 0, 0, 23, 219, 255, 93, 0, 0, 0, 32, 242, 243, 24, 0, 0, 0, 129, 255, 131, 0, 0, 0, 45, 255, 208, 0, 0, 0, 8, 255, 245, 0, 0, 0, 9, 255, 247, 0, 0, 0, 50, 255, 216, 0, 0, 0, 140, 255, 146, 0, 0, 50, 247, 250, 37, 0, 47, 235, 255, 117, 0, 121, 250, 253, 123, 0, 0, 80, 201, 60, 0, 0, 0], "Top": 3, "Height": 14}, "42": {"Width": 7, "Advance": 8, "Bitmap": [0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 136, 189, 68, 255, 66, 183, 136, 213, 255, 255, 255, 255, 255, 214, 0, 7, 161, 248, 162, 8, 0, 0, 174, 255, 132, 255, 176, 0, 0, 67, 187, 4, 185, 63, 0], "Top": 4, "Height": 7}, "43": {"Width": 8, "Advance": 8, "Bitmap": [0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0], "Top": 6, "Height": 8}, "44": {"Width": 4, "Advance": 8, "Bitmap": [0, 140, 243, 125, 0, 246, 255, 240, 0, 166, 255, 234, 0, 40, 255, 177, 49, 203, 240, 42, 204, 174, 39, 0], "Top": 11, "Height": 6}, "45": {"Width": 4, "Advance": 8, "Bitmap": [255, 255, 255, 255, 255, 255, 255, 255], "Top": 9, "Height": 2}, "46": {"Width": 3, "Advance": 9, "Bitmap": [91, 243, 85, 234, 255, 212, 91, 244, 85], "Top": 11, "Height": 3}, "47": {"Width": 7, "Advance": 8, "Bitmap": [0, 0, 0, 0, 47, 255, 217, 0, 0, 0, 0, 124, 255, 139, 0, 0, 0, 0, 201, 255, 62, 0, 0, 0, 24, 254, 237, 3, 0, 0, 0, 99, 255, 164, 0, 0, 0, 0, 176, 255, 87, 0, 0, 0, 8, 245, 250, 15, 0, 0, 0, 75, 255, 189, 0, 0, 0, 0, 152, 255, 111, 0, 0, 0, 0, 228, 255, 34, 0, 0, 0, 50, 255, 213, 0, 0, 0, 0, 127, 255, 136, 0, 0, 0, 0, 204, 255, 59, 0, 0, 0, 26, 254, 235, 2, 0, 0, 0], "Top": 3, "Height": 14}, "48": {"Width": 6, "Advance": 8, "Bitmap": [0, 125, 237, 238, 130, 0, 85, 255, 255, 255, 255, 89, 183, 255, 112, 114, 255, 184, 231, 255, 28, 30, 255, 231, 251, 255, 205, 204, 255, 250, 251, 255, 205, 204, 255, 249, 232, 255, 28, 30, 255, 230, 185, 255, 112, 114, 255, 184, 90, 255, 255, 255, 255, 89, 0, 131, 238, 238, 131, 0], "Top": 4, "Height": 10}, "49": {"Width": 7, "Advance": 8, "Bitmap": [0, 0, 48, 216, 255, 0, 0, 49, 161, 252, 255, 255, 0, 0, 196, 255, 221, 255, 255, 0, 0, 56, 80, 3, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255], "Top": 4, "Height": 10}, "50": {"Width": 7, "Advance": 8, "Bitmap": [0, 31, 166, 239, 236, 162, 19, 0, 206, 255, 255, 255, 255, 177, 0, 93, 125, 21, 76, 255, 247, 0, 0, 0, 0, 22, 255, 200, 0, 0, 0, 5, 182, 247, 54, 0, 0, 9, 184, 244, 73, 0, 0, 3, 184, 233, 49, 0, 0, 0, 117, 255, 60, 0, 0, 0, 0, 227, 255, 255, 255, 255, 255, 0, 253, 255, 255, 255, 255, 255], "Top": 4, "Height": 10}, "51": {"Width": 6, "Advance": 8, "Bitmap": [78, 198, 247, 226, 148, 13, 201, 255, 255, 255, 255, 155, 66, 71, 13, 62, 255, 236, 0, 0, 12, 99, 255, 226, 0, 255, 255, 255, 250, 88, 0, 255, 255, 255, 251, 95, 0, 1, 16, 94, 255, 228, 69, 33, 7, 76, 255, 239, 215, 255, 255, 255, 255, 162, 145, 222, 251, 233, 144, 13], "Top": 4, "Height": 10}, "52": {"Width": 7, "Advance": 8, "Bitmap": [0, 0, 0, 64, 249, 255, 0, 0, 0, 34, 238, 255, 255, 0, 0, 3, 198, 241, 255, 255, 0, 0, 119, 255, 91, 255, 255, 0, 33, 246, 179, 0, 255, 255, 0, 165, 252, 37, 0, 255, 255, 0, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0], "Top": 4, "Height": 10}, "53": {"Width": 6, "Advance": 8, "Bitmap": [0, 171, 255, 255, 255, 255, 0, 184, 255, 255, 255, 255, 0, 198, 236, 0, 0, 0, 0, 221, 255, 215, 118, 2, 0, 244, 255, 255, 255, 118, 0, 4, 34, 154, 255, 218, 0, 0, 0, 12, 255, 246, 60, 25, 8, 96, 255, 216, 213, 255, 255, 255, 255, 108, 156, 231, 251, 222, 106, 1], "Top": 4, "Height": 10}, "54": {"Width": 6, "Advance": 8, "Bitmap": [0, 0, 42, 155, 221, 227, 0, 91, 249, 255, 255, 249, 42, 249, 237, 115, 42, 11, 156, 255, 140, 100, 31, 0, 223, 255, 255, 255, 244, 68, 249, 254, 158, 193, 255, 204, 247, 252, 1, 13, 255, 246, 211, 255, 78, 70, 255, 222, 121, 255, 255, 255, 255, 126, 4, 144, 237, 244, 147, 4], "Top": 4, "Height": 10}, "55": {"Width": 6, "Advance": 8, "Bitmap": [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 77, 255, 161, 0, 0, 3, 218, 241, 18, 0, 0, 89, 255, 135, 0, 0, 0, 205, 253, 26, 0, 0, 45, 255, 193, 0, 0, 0, 132, 255, 111, 0, 0, 0, 201, 255, 52, 0, 0, 0, 242, 255, 14, 0, 0], "Top": 4, "Height": 10}, "56": {"Width": 6, "Advance": 8, "Bitmap": [7, 142, 237, 243, 186, 38, 123, 255, 255, 255, 255, 203, 184, 255, 52, 50, 255, 244, 137, 255, 104, 72, 255, 153, 16, 237, 255, 255, 213, 7, 136, 255, 86, 183, 255, 130, 231, 255, 9, 19, 255, 233, 241, 255, 68, 67, 255, 239, 174, 255, 255, 255, 255, 159, 21, 168, 238, 242, 158, 14], "Top": 4, "Height": 10}, "57": {"Width": 6, "Advance": 8, "Bitmap": [3, 141, 242, 235, 131, 1, 125, 255, 255, 255, 255, 93, 224, 255, 60, 85, 255, 198, 246, 255, 12, 2, 253, 236, 205, 255, 198, 166, 255, 249, 69, 244, 255, 255, 255, 228, 0, 29, 93, 127, 255, 174, 0, 15, 80, 227, 255, 74, 0, 253, 255, 255, 154, 0, 0, 236, 203, 101, 1, 0], "Top": 4, "Height": 10}, "58": {"Width": 3, "Advance": 9, "Bitmap": [91, 243, 85, 234, 255, 212, 91, 244, 85, 0, 0, 0, 0, 0, 0, 91, 243, 85, 234, 255, 212, 91, 244, 85], "Top": 6, "Height": 8}, "59": {"Width": 4, "Advance": 8, "Bitmap": [0, 91, 243, 85, 0, 234, 255, 212, 0, 91, 244, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 243, 125, 0, 246, 255, 240, 0, 166, 255, 234, 0, 40, 255, 177, 49, 203, 240, 42, 204, 174, 39, 0], "Top": 6, "Height": 11}, "60": {"Width": 7, "Advance": 8, "Bitmap": [0, 0, 0, 0, 0, 94, 131, 0, 0, 0, 32, 185, 255, 203, 0, 2, 112, 244, 255, 255, 192, 27, 201, 255, 255, 234, 98, 1, 72, 255, 252, 148, 16, 0, 0, 72, 255, 252, 147, 16, 0, 0, 25, 195, 255, 255, 234, 97, 1, 0, 1, 108, 243, 255, 255, 192, 0, 0, 0, 30, 183, 255, 203, 0, 0, 0, 0, 0, 93, 131], "Top": 3, "Height": 10}, "61": {"Width": 6, "Advance": 8, "Bitmap": [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 8, "Height": 5}, "62": {"Width": 6, "Advance": 8, "Bitmap": [132, 83, 0, 0, 0, 0, 206, 255, 166, 18, 0, 0, 195, 255, 255, 231, 78, 0, 2, 108, 241, 255, 255, 161, 0, 0, 26, 172, 255, 255, 0, 0, 26, 171, 255, 255, 2, 107, 241, 255, 255, 155, 195, 255, 255, 229, 73, 0, 206, 255, 165, 17, 0, 0, 132, 82, 0, 0, 0, 0], "Top": 3, "Height": 10}, "63": {"Width": 6, "Advance": 8, "Bitmap": [98, 200, 241, 241, 170, 22, 200, 255, 255, 255, 255, 174, 68, 61, 11, 67, 255, 243, 0, 0, 0, 65, 255, 196, 0, 1, 119, 250, 232, 40, 0, 132, 255, 220, 35, 0, 0, 245, 255, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 197, 195, 0, 0, 0, 0, 198, 198, 0, 0, 0], "Top": 4, "Height": 10}, "64": {"Width": 7, "Advance": 8, "Bitmap": [0, 30, 175, 244, 229, 135, 6, 10, 211, 81, 14, 124, 255, 132, 111, 139, 0, 0, 10, 255, 224, 190, 59, 31, 209, 247, 255, 252, 232, 20, 172, 255, 247, 255, 255, 249, 5, 235, 255, 47, 255, 255, 248, 4, 251, 255, 2, 255, 255, 229, 21, 231, 255, 45, 255, 255, 179, 70, 152, 255, 253, 255, 255, 88, 176, 14, 159, 237, 229, 129, 1, 181, 163, 45, 7, 0, 0, 0, 5, 120, 209, 246, 254, 216], "Top": 4, "Height": 12}, "65": {"Width": 8, "Advance": 8, "Bitmap": [0, 0, 37, 255, 255, 92, 0, 0, 0, 0, 122, 255, 255, 172, 0, 0, 0, 0, 204, 234, 207, 244, 7, 0, 0, 32, 255, 165, 135, 255, 71, 0, 0, 113, 255, 98, 72, 255, 146, 0, 0, 191, 255, 42, 18, 255, 217, 0, 16, 251, 255, 255, 255, 255, 255, 30, 85, 255, 255, 255, 255, 255, 255, 98, 154, 255, 147, 0, 0, 130, 255, 161, 222, 255, 89, 0, 0, 70, 255, 224], "Top": 4, "Height": 10}, "66": {"Width": 6, "Advance": 8, "Bitmap": [212, 240, 251, 232, 160, 21, 255, 255, 255, 255, 255, 182, 255, 255, 5, 53, 255, 244, 255, 255, 1, 70, 255, 222, 255, 255, 255, 255, 247, 70, 255, 255, 255, 255, 251, 125, 255, 255, 1, 66, 255, 237, 255, 255, 2, 62, 255, 246, 255, 255, 255, 255, 255, 169, 206, 242, 251, 228, 151, 14], "Top": 4, "Height": 10}, "67": {"Width": 7, "Advance": 8, "Bitmap": [0, 15, 140, 225, 248, 209, 91, 10, 208, 255, 255, 255, 255, 223, 124, 255, 216, 58, 9, 72, 133, 210, 255, 64, 0, 0, 0, 0, 247, 255, 8, 0, 0, 0, 0, 249, 255, 15, 0, 0, 0, 0, 219, 255, 67, 0, 0, 0, 0, 148, 255, 211, 51, 8, 64, 134, 27, 235, 255, 255, 255, 255, 225, 0, 37, 171, 238, 249, 205, 87], "Top": 4, "Height": 10}, "68": {"Width": 7, "Advance": 8, "Bitmap": [214, 244, 252, 229, 159, 29, 0, 255, 255, 255, 255, 255, 229, 23, 255, 255, 5, 42, 212, 255, 143, 255, 255, 0, 0, 65, 255, 218, 255, 255, 0, 0, 12, 255, 247, 255, 255, 0, 0, 15, 255, 246, 255, 255, 0, 0, 72, 255, 214, 255, 255, 8, 61, 219, 255, 135, 255, 255, 255, 255, 255, 219, 17, 217, 248, 250, 221, 145, 19, 0], "Top": 4, "Height": 10}, "69": {"Width": 6, "Advance": 8, "Bitmap": [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 4, "Height": 10}, "70": {"Width": 6, "Advance": 8, "Bitmap": [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0], "Top": 4, "Height": 10}, "71": {"Width": 7, "Advance": 8, "Bitmap": [0, 20, 151, 231, 254, 255, 234, 13, 215, 255, 255, 255, 255, 190, 130, 255, 219, 61, 11, 72, 122, 212, 255, 68, 0, 0, 0, 0, 247, 255, 13, 0, 0, 0, 0, 248, 255, 13, 0, 0, 255, 255, 219, 255, 58, 0, 0, 255, 255, 148, 255, 200, 39, 5, 255, 255, 28, 236, 255, 255, 255, 255, 255, 0, 40, 178, 242, 255, 255, 255], "Top": 4, "Height": 10}, "72": {"Width": 7, "Advance": 8, "Bitmap": [255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255], "Top": 4, "Height": 10}, "73": {"Width": 6, "Advance": 8, "Bitmap": [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 4, "Height": 10}, "74": {"Width": 6, "Advance": 8, "Bitmap": [0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 8, 255, 252, 115, 93, 9, 109, 255, 223, 216, 255, 255, 255, 255, 128, 72, 209, 249, 232, 131, 4], "Top": 4, "Height": 10}, "75": {"Width": 7, "Advance": 8, "Bitmap": [255, 255, 0, 0, 74, 255, 182, 255, 255, 0, 12, 223, 245, 35, 255, 255, 0, 163, 255, 107, 0, 255, 255, 114, 255, 172, 0, 0, 255, 255, 253, 236, 11, 0, 0, 255, 255, 216, 255, 127, 0, 0, 255, 255, 30, 235, 253, 62, 0, 255, 255, 0, 77, 255, 217, 3, 255, 255, 0, 0, 179, 255, 97, 255, 255, 0, 0, 56, 255, 205], "Top": 4, "Height": 10}, "76": {"Width": 6, "Advance": 8, "Bitmap": [255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 4, "Height": 10}, "77": {"Width": 7, "Advance": 8, "Bitmap": [119, 255, 43, 16, 255, 255, 17, 142, 255, 131, 44, 255, 255, 53, 163, 228, 217, 72, 255, 227, 88, 180, 191, 230, 149, 236, 183, 120, 195, 200, 158, 246, 164, 193, 151, 209, 210, 85, 255, 89, 204, 177, 221, 220, 0, 0, 0, 216, 199, 232, 230, 0, 0, 0, 227, 219, 242, 240, 0, 0, 0, 239, 234, 251, 250, 0, 0, 0, 250, 248], "Top": 4, "Height": 10}, "78": {"Width": 7, "Advance": 8, "Bitmap": [255, 246, 31, 0, 0, 255, 255, 255, 255, 171, 0, 0, 255, 255, 255, 255, 255, 53, 0, 255, 255, 255, 255, 229, 185, 0, 255, 255, 255, 255, 106, 255, 57, 255, 255, 255, 255, 6, 223, 178, 255, 255, 255, 255, 0, 97, 254, 255, 255, 255, 255, 0, 5, 229, 255, 255, 255, 255, 0, 0, 120, 255, 255, 255, 255, 0, 0, 17, 244, 255], "Top": 4, "Height": 10}, "79": {"Width": 7, "Advance": 8, "Bitmap": [0, 78, 213, 249, 215, 83, 0, 55, 252, 255, 255, 255, 252, 57, 168, 255, 146, 19, 145, 255, 169, 226, 255, 32, 0, 34, 255, 226, 250, 255, 5, 0, 7, 255, 249, 250, 255, 5, 0, 7, 255, 249, 227, 255, 31, 0, 34, 255, 225, 170, 255, 145, 19, 147, 255, 168, 59, 253, 255, 255, 255, 252, 56, 0, 84, 215, 250, 215, 82, 0], "Top": 4, "Height": 10}, "80": {"Width": 7, "Advance": 8, "Bitmap": [255, 255, 255, 236, 199, 95, 1, 255, 255, 255, 255, 255, 255, 124, 255, 255, 5, 12, 120, 255, 226, 255, 255, 0, 0, 12, 255, 250, 255, 255, 0, 15, 122, 255, 223, 255, 255, 255, 255, 255, 255, 118, 255, 255, 255, 234, 195, 89, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0], "Top": 4, "Height": 10}, "81": {"Width": 7, "Advance": 8, "Bitmap": [0, 76, 212, 249, 214, 81, 0, 53, 251, 255, 255, 255, 252, 55, 166, 255, 146, 19, 145, 255, 166, 225, 255, 32, 0, 34, 255, 224, 250, 255, 5, 0, 7, 255, 248, 246, 255, 5, 0, 7, 255, 245, 223, 255, 31, 0, 36, 255, 221, 168, 255, 143, 19, 150, 255, 165, 63, 253, 255, 255, 255, 252, 60, 0, 94, 231, 255, 227, 92, 0, 0, 0, 52, 255, 181, 70, 18, 0, 0, 0, 175, 255, 255, 210, 0, 0, 0, 4, 101, 192, 149], "Top": 4, "Height": 13}, "82": {"Width": 6, "Advance": 8, "Bitmap": [255, 255, 249, 214, 121, 2, 255, 255, 255, 255, 255, 122, 255, 255, 7, 106, 255, 222, 255, 255, 0, 10, 255, 249, 255, 255, 6, 105, 255, 212, 255, 255, 255, 255, 252, 77, 255, 255, 255, 247, 51, 0, 255, 255, 142, 255, 133, 0, 255, 255, 7, 214, 252, 47, 255, 255, 0, 73, 255, 189], "Top": 4, "Height": 10}, "83": {"Width": 6, "Advance": 8, "Bitmap": [11, 142, 225, 248, 211, 92, 153, 255, 255, 255, 255, 216, 234, 255, 58, 7, 74, 128, 241, 255, 144, 31, 0, 0, 139, 255, 255, 253, 157, 10, 3, 118, 233, 255, 255, 149, 0, 0, 5, 111, 255, 240, 135, 69, 7, 61, 255, 238, 223, 255, 255, 255, 255, 167, 97, 212, 249, 231, 160, 19], "Top": 4, "Height": 10}, "84": {"Width": 8, "Advance": 8, "Bitmap": [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0], "Top": 4, "Height": 10}, "85": {"Width": 6, "Advance": 8, "Bitmap": [255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 249, 255, 7, 8, 255, 249, 225, 255, 71, 73, 255, 225, 148, 255, 255, 255, 255, 145, 14, 168, 245, 244, 164, 13], "Top": 4, "Height": 10}, "86": {"Width": 7, "Advance": 8, "Bitmap": [232, 250, 3, 0, 3, 249, 230, 182, 255, 35, 0, 39, 255, 178, 123, 255, 79, 0, 84, 255, 123, 62, 255, 124, 0, 131, 255, 63, 7, 247, 175, 0, 182, 249, 9, 0, 188, 228, 0, 233, 190, 0, 0, 120, 255, 65, 255, 120, 0, 0, 50, 255, 194, 255, 48, 0, 0, 1, 232, 255, 225, 0, 0, 0, 0, 156, 255, 147, 0, 0], "Top": 4, "Height": 10}, "87": {"Width": 7, "Advance": 8, "Bitmap": [252, 248, 0, 0, 0, 253, 251, 245, 234, 0, 0, 0, 246, 241, 236, 221, 0, 0, 0, 237, 231, 225, 209, 32, 120, 29, 223, 221, 215, 196, 120, 255, 109, 210, 210, 201, 182, 189, 241, 177, 197, 197, 186, 182, 248, 108, 241, 191, 181, 170, 237, 227, 2, 230, 237, 165, 148, 255, 149, 0, 153, 255, 146, 124, 255, 71, 0, 72, 255, 126], "Top": 4, "Height": 10}, "88": {"Width": 8, "Advance": 8, "Bitmap": [178, 255, 160, 0, 0, 137, 255, 178, 32, 246, 252, 37, 29, 247, 246, 32, 0, 124, 255, 164, 159, 255, 124, 0, 0, 7, 218, 253, 253, 218, 7, 0, 0, 0, 82, 255, 255, 87, 0, 0, 0, 0, 152, 255, 255, 173, 0, 0, 0, 45, 253, 212, 213, 255, 68, 0, 0, 184, 255, 89, 92, 255, 206, 1, 68, 255, 221, 3, 3, 225, 255, 81, 197, 255, 109, 0, 0, 119, 255, 200], "Top": 4, "Height": 10}, "89": {"Width": 8, "Advance": 8, "Bitmap": [201, 255, 76, 0, 0, 57, 255, 203, 92, 255, 172, 0, 0, 158, 255, 98, 6, 232, 249, 22, 17, 245, 233, 8, 0, 118, 255, 130, 120, 255, 120, 0, 0, 9, 227, 236, 234, 233, 11, 0, 0, 0, 98, 255, 255, 101, 0, 0, 0, 0, 2, 255, 255, 2, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0], "Top": 4, "Height": 10}, "90": {"Width": 6, "Advance": 8, "Bitmap": [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 177, 0, 0, 0, 148, 245, 31, 0, 0, 39, 251, 123, 0, 0, 0, 177, 222, 8, 0, 0, 62, 255, 86, 0, 0, 0, 200, 201, 0, 0, 0, 79, 255, 67, 0, 0, 0, 204, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 4, "Height": 10}, "91": {"Width": 4, "Advance": 8, "Bitmap": [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 3, "Height": 14}, "92": {"Width": 6, "Advance": 8, "Bitmap": [220, 255, 44, 0, 0, 0, 147, 255, 116, 0, 0, 0, 75, 255, 189, 0, 0, 0, 10, 248, 249, 12, 0, 0, 0, 186, 255, 78, 0, 0, 0, 113, 255, 150, 0, 0, 0, 40, 255, 223, 0, 0, 0, 0, 223, 255, 40, 0, 0, 0, 151, 255, 112, 0, 0, 0, 79, 255, 185, 0, 0, 0, 12, 249, 247, 9, 0, 0, 0, 190, 255, 74, 0, 0, 0, 117, 255, 146, 0, 0, 0, 44, 255, 219], "Top": 3, "Height": 14}, "93": {"Width": 4, "Advance": 8, "Bitmap": [255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 3, "Height": 14}, "94": {"Width": 8, "Advance": 8, "Bitmap": [0, 0, 49, 250, 251, 54, 0, 0, 0, 7, 211, 255, 255, 218, 11, 0, 0, 139, 255, 171, 169, 255, 155, 0, 60, 253, 235, 22, 19, 231, 255, 80, 74, 201, 88, 0, 0, 76, 208, 90], "Top": 4, "Height": 5}, "95": {"Width": 8, "Advance": 8, "Bitmap": [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 15, "Height": 2}, "96": {"Width": 4, "Advance": 8, "Bitmap": [64, 187, 34, 0, 175, 255, 245, 113, 0, 65, 179, 136], "Top": 3, "Height": 3}, "97": {"Width": 7, "Advance": 8, "Bitmap": [0, 181, 232, 250, 235, 164, 19, 0, 211, 255, 255, 255, 255, 163, 0, 35, 15, 6, 71, 255, 235, 22, 160, 230, 250, 234, 255, 255, 198, 255, 255, 255, 255, 255, 255, 249, 255, 76, 6, 8, 255, 255, 195, 255, 255, 255, 255, 255, 255, 27, 168, 234, 251, 241, 223, 186], "Top": 6, "Height": 8}, "98": {"Width": 6, "Advance": 8, "Bitmap": [255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 250, 172, 10, 255, 255, 255, 255, 255, 124, 255, 255, 0, 94, 255, 210, 255, 255, 0, 10, 255, 242, 255, 255, 0, 12, 255, 238, 255, 255, 0, 95, 255, 198, 255, 255, 243, 255, 255, 87, 183, 232, 250, 226, 101, 0], "Top": 3, "Height": 11}, "99": {"Width": 6, "Advance": 8, "Bitmap": [0, 71, 198, 246, 240, 172, 60, 253, 255, 255, 255, 201, 187, 255, 160, 26, 10, 32, 236, 255, 19, 0, 0, 0, 239, 255, 20, 0, 0, 0, 197, 255, 157, 23, 7, 34, 84, 255, 255, 255, 255, 218, 0, 86, 206, 248, 240, 182], "Top": 6, "Height": 8}, "100": {"Width": 7, "Advance": 8, "Bitmap": [0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 118, 226, 254, 255, 255, 255, 90, 255, 255, 255, 255, 255, 255, 199, 255, 128, 8, 0, 255, 255, 239, 255, 14, 0, 0, 255, 255, 238, 255, 21, 0, 0, 255, 255, 194, 255, 154, 17, 6, 255, 255, 70, 255, 255, 255, 255, 255, 255, 0, 86, 206, 248, 246, 226, 185], "Top": 3, "Height": 11}, "101": {"Width": 8, "Advance": 8, "Bitmap": [0, 71, 203, 249, 225, 119, 0, 0, 63, 253, 255, 255, 255, 255, 93, 0, 190, 255, 85, 8, 76, 255, 203, 0, 244, 255, 255, 255, 255, 255, 241, 0, 246, 255, 255, 255, 255, 255, 255, 4, 196, 255, 107, 20, 1, 0, 0, 0, 64, 251, 255, 255, 255, 255, 203, 0, 0, 57, 179, 236, 251, 234, 178, 0], "Top": 6, "Height": 8}, "102": {"Width": 8, "Advance": 8, "Bitmap": [0, 0, 19, 159, 232, 248, 223, 147, 0, 0, 186, 255, 255, 255, 255, 208, 0, 0, 250, 255, 50, 6, 34, 54, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0], "Top": 3, "Height": 11}, "103": {"Width": 7, "Advance": 8, "Bitmap": [0, 67, 191, 243, 247, 224, 174, 72, 253, 255, 255, 255, 255, 255, 195, 255, 162, 23, 6, 255, 255, 245, 255, 23, 0, 0, 255, 255, 242, 255, 13, 0, 0, 255, 255, 206, 255, 129, 10, 34, 255, 255, 101, 255, 255, 255, 255, 255, 255, 1, 129, 230, 249, 210, 255, 244, 0, 0, 0, 6, 77, 255, 209, 27, 255, 255, 255, 255, 255, 106, 45, 198, 239, 250, 220, 122, 2], "Top": 6, "Height": 11}, "104": {"Width": 6, "Advance": 8, "Bitmap": [255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 228, 246, 171, 16, 255, 255, 255, 255, 255, 153, 255, 255, 16, 82, 255, 228, 255, 255, 0, 8, 255, 251, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255], "Top": 3, "Height": 11}, "105": {"Width": 7, "Advance": 8, "Bitmap": [0, 201, 199, 0, 0, 0, 0, 0, 201, 199, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 1, 0, 0, 0, 0, 238, 255, 63, 0, 0, 0, 0, 180, 255, 255, 255, 173, 0, 0, 33, 189, 244, 234, 136], "Top": 3, "Height": 11}, "106": {"Width": 6, "Advance": 8, "Bitmap": [0, 0, 0, 0, 201, 199, 0, 0, 0, 0, 201, 199, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 2, 255, 254, 48, 156, 25, 64, 255, 233, 122, 255, 255, 255, 255, 157, 48, 175, 234, 238, 163, 16], "Top": 3, "Height": 14}, "107": {"Width": 7, "Advance": 8, "Bitmap": [255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 1, 182, 255, 141, 255, 255, 0, 129, 255, 158, 1, 255, 255, 94, 255, 161, 2, 0, 255, 255, 252, 216, 2, 0, 0, 255, 255, 179, 255, 136, 0, 0, 255, 255, 9, 205, 255, 100, 0, 255, 255, 0, 34, 241, 248, 49, 255, 255, 0, 0, 97, 196, 148], "Top": 3, "Height": 11}, "108": {"Width": 6, "Advance": 8, "Bitmap": [255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 243, 255, 40, 0, 0, 0, 199, 255, 255, 223, 0, 0, 59, 220, 247, 187], "Top": 3, "Height": 11}, "109": {"Width": 7, "Advance": 8, "Bitmap": [185, 234, 244, 179, 232, 228, 76, 255, 255, 255, 255, 255, 255, 208, 255, 255, 35, 244, 37, 255, 245, 255, 255, 0, 255, 1, 255, 255, 255, 255, 0, 255, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255], "Top": 6, "Height": 8}, "110": {"Width": 7, "Advance": 8, "Bitmap": [175, 217, 238, 251, 229, 144, 8, 255, 255, 255, 255, 255, 255, 143, 255, 255, 7, 9, 115, 255, 222, 255, 255, 0, 0, 12, 255, 250, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255], "Top": 6, "Height": 8}, "111": {"Width": 7, "Advance": 8, "Bitmap": [0, 86, 214, 249, 215, 89, 0, 64, 255, 255, 255, 255, 255, 66, 195, 255, 130, 13, 126, 255, 194, 246, 255, 16, 0, 15, 255, 246, 245, 255, 14, 0, 17, 255, 245, 192, 255, 123, 13, 133, 255, 192, 64, 254, 255, 255, 255, 254, 63, 0, 75, 215, 250, 216, 76, 0], "Top": 6, "Height": 8}, "112": {"Width": 6, "Advance": 8, "Bitmap": [178, 228, 249, 227, 118, 0, 255, 255, 255, 255, 255, 90, 255, 255, 8, 119, 255, 200, 255, 255, 0, 16, 255, 239, 255, 255, 0, 9, 255, 241, 255, 255, 20, 92, 255, 209, 255, 255, 255, 255, 255, 122, 255, 255, 210, 247, 164, 6, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0], "Top": 6, "Height": 11}, "113": {"Width": 6, "Advance": 8, "Bitmap": [0, 98, 225, 251, 232, 181, 85, 255, 255, 255, 255, 255, 197, 255, 117, 8, 255, 255, 239, 255, 15, 0, 255, 255, 243, 255, 9, 0, 255, 255, 212, 255, 92, 0, 255, 255, 126, 255, 255, 255, 255, 255, 7, 168, 250, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255], "Top": 6, "Height": 11}, "114": {"Width": 6, "Advance": 8, "Bitmap": [122, 195, 239, 253, 239, 187, 255, 255, 255, 255, 255, 200, 255, 255, 28, 3, 17, 34, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0], "Top": 6, "Height": 8}, "115": {"Width": 6, "Advance": 8, "Bitmap": [28, 163, 237, 249, 227, 158, 196, 255, 255, 255, 255, 213, 239, 255, 54, 8, 40, 65, 144, 255, 239, 158, 58, 0, 0, 79, 171, 244, 255, 125, 94, 48, 7, 49, 255, 236, 227, 255, 255, 255, 255, 209, 123, 217, 250, 241, 185, 42], "Top": 6, "Height": 8}, "116": {"Width": 7, "Advance": 8, "Bitmap": [0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 250, 255, 48, 7, 48, 0, 0, 209, 255, 255, 255, 221, 0, 0, 62, 210, 249, 237, 170], "Top": 4, "Height": 10}, "117": {"Width": 7, "Advance": 8, "Bitmap": [255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 251, 255, 14, 0, 0, 255, 255, 226, 255, 120, 10, 12, 255, 255, 139, 255, 255, 255, 255, 255, 255, 10, 148, 231, 251, 238, 215, 168], "Top": 6, "Height": 8}, "118": {"Width": 7, "Advance": 8, "Bitmap": [166, 255, 49, 0, 1, 236, 220, 97, 255, 110, 0, 53, 255, 149, 26, 254, 182, 0, 133, 255, 75, 0, 203, 247, 11, 218, 241, 6, 0, 123, 255, 131, 255, 162, 0, 0, 35, 255, 251, 255, 67, 0, 0, 0, 198, 255, 224, 2, 0, 0, 0, 98, 255, 123, 0, 0], "Top": 6, "Height": 8}, "119": {"Width": 8, "Advance": 8, "Bitmap": [245, 255, 0, 0, 0, 0, 255, 244, 224, 255, 0, 0, 0, 0, 255, 221, 201, 255, 64, 255, 116, 0, 255, 195, 174, 255, 113, 255, 217, 1, 255, 164, 143, 255, 166, 235, 255, 75, 255, 130, 105, 255, 227, 84, 242, 199, 255, 89, 63, 255, 245, 8, 123, 255, 255, 45, 13, 253, 179, 0, 8, 222, 246, 3], "Top": 6, "Height": 8}, "120": {"Width": 8, "Advance": 8, "Bitmap": [154, 255, 212, 9, 0, 132, 255, 168, 6, 199, 255, 153, 55, 252, 230, 18, 0, 26, 231, 255, 234, 255, 74, 0, 0, 0, 58, 253, 255, 160, 0, 0, 0, 0, 109, 255, 255, 210, 8, 0, 0, 64, 251, 238, 204, 255, 138, 0, 22, 231, 255, 83, 32, 243, 252, 46, 174, 255, 180, 0, 0, 112, 255, 188], "Top": 6, "Height": 8}, "121": {"Width": 8, "Advance": 8, "Bitmap": [0, 222, 255, 34, 0, 30, 255, 222, 0, 153, 255, 105, 0, 89, 255, 156, 0, 80, 255, 180, 0, 152, 255, 88, 0, 9, 244, 249, 16, 219, 253, 19, 0, 0, 165, 255, 141, 255, 200, 0, 0, 0, 66, 255, 254, 255, 120, 0, 0, 0, 1, 214, 255, 255, 37, 0, 0, 0, 0, 123, 255, 196, 0, 0, 0, 0, 44, 228, 255, 86, 0, 0, 174, 255, 255, 255, 185, 0, 0, 0, 198, 249, 234, 156, 13, 0, 0, 0], "Top": 6, "Height": 11}, "122": {"Width": 6, "Advance": 8, "Bitmap": [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 153, 0, 0, 29, 237, 201, 6, 0, 3, 195, 240, 31, 0, 0, 126, 255, 90, 0, 0, 49, 250, 175, 0, 0, 0, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 6, "Height": 8}, "123": {"Width": 6, "Advance": 8, "Bitmap": [0, 0, 77, 220, 252, 255, 0, 0, 221, 255, 255, 255, 0, 0, 253, 255, 36, 0, 0, 0, 255, 255, 0, 0, 0, 1, 255, 255, 0, 0, 0, 59, 255, 228, 0, 0, 255, 255, 218, 76, 0, 0, 255, 255, 219, 76, 0, 0, 0, 61, 255, 228, 0, 0, 0, 1, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 253, 255, 36, 0, 0, 0, 221, 255, 255, 255, 0, 0, 77, 221, 253, 255], "Top": 3, "Height": 14}, "124": {"Width": 2, "Advance": 8, "Bitmap": [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 3, "Height": 14}, "125": {"Width": 6, "Advance": 8, "Bitmap": [255, 252, 218, 74, 0, 0, 255, 255, 255, 221, 0, 0, 0, 38, 255, 253, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 1, 0, 0, 0, 229, 255, 59, 0, 0, 0, 78, 220, 255, 255, 0, 0, 74, 217, 255, 255, 0, 0, 228, 255, 61, 0, 0, 0, 255, 255, 1, 0, 0, 0, 255, 255, 0, 0, 0, 37, 255, 253, 0, 0, 255, 255, 255, 222, 0, 0, 255, 253, 219, 74, 0, 0], "Top": 3, "Height": 14}, "126": {"Width": 7, "Advance": 8, "Bitmap": [31, 211, 234, 134, 22, 146, 178, 174, 255, 255, 255, 255, 255, 168, 180, 143, 24, 137, 235, 212, 29], "Top": 8, "Height": 3}, "160": {"Width": 0, "Advance": 8, "Bitmap": [], "Top": 14, "Height": 0}, "161": {"Width": 2, "Advance": 8, "Bitmap": [199, 198, 199, 198, 0, 0, 202, 193, 226, 219, 245, 240, 254, 251, 255, 255, 255, 255, 255, 255], "Top": 7, "Height": 10}, "162": {"Width": 6, "Advance": 8, "Bitmap": [0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 2, 0, 78, 206, 255, 255, 217, 70, 252, 255, 255, 255, 188, 191, 255, 151, 21, 0, 0, 240, 255, 18, 0, 0, 0, 240, 255, 19, 0, 0, 0, 193, 255, 154, 22, 7, 33, 73, 253, 255, 255, 255, 219, 0, 80, 207, 255, 255, 227, 0, 0, 0, 255, 255, 3, 0, 0, 0, 255, 255, 0], "Top": 4, "Height": 12}, "163": {"Width": 6, "Advance": 8, "Bitmap": [0, 24, 171, 238, 236, 148, 0, 172, 255, 255, 255, 162, 0, 240, 255, 56, 81, 54, 0, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 0, 0, 255, 249, 0, 0, 0, 0, 255, 224, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255], "Top": 4, "Height": 10}, "164": {"Width": 8, "Advance": 8, "Bitmap": [52, 190, 17, 0, 0, 16, 189, 54, 156, 255, 226, 242, 242, 225, 255, 153, 0, 217, 255, 255, 255, 255, 216, 0, 0, 249, 255, 60, 60, 255, 248, 0, 0, 216, 255, 255, 255, 255, 216, 0, 153, 255, 226, 245, 245, 227, 255, 155, 55, 189, 16, 0, 0, 17, 190, 52], "Top": 6, "Height": 7}, "165": {"Width": 8, "Advance": 8, "Bitmap": [185, 255, 52, 0, 0, 52, 255, 182, 46, 253, 156, 0, 0, 155, 254, 44, 0, 161, 246, 24, 24, 245, 172, 0, 0, 38, 252, 146, 147, 255, 55, 0, 0, 0, 162, 248, 248, 188, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0], "Top": 4, "Height": 10}, "166": {"Width": 2, "Advance": 8, "Bitmap": [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 3, "Height": 14}, "167": {"Width": 6, "Advance": 8, "Bitmap": [27, 167, 232, 247, 218, 148, 190, 255, 255, 255, 255, 154, 241, 255, 74, 1, 0, 0, 162, 255, 255, 224, 113, 2, 183, 255, 255, 255, 255, 148, 250, 255, 47, 133, 255, 241, 240, 255, 125, 44, 255, 228, 149, 255, 255, 255, 255, 121, 4, 119, 231, 255, 255, 175, 133, 91, 14, 82, 255, 243, 218, 255, 255, 255, 255, 202, 105, 207, 247, 241, 188, 41], "Top": 4, "Height": 12}, "168": {"Width": 5, "Advance": 8, "Bitmap": [199, 198, 0, 199, 199, 201, 199, 0, 201, 201], "Top": 3, "Height": 2}, "169": {"Width": 8, "Advance": 8, "Bitmap": [0, 58, 185, 244, 243, 184, 56, 0, 57, 241, 113, 23, 23, 113, 240, 55, 189, 108, 78, 227, 245, 38, 112, 186, 244, 14, 229, 58, 10, 0, 15, 244, 244, 14, 232, 61, 11, 0, 15, 244, 188, 105, 84, 230, 242, 42, 109, 184, 43, 236, 107, 21, 21, 108, 235, 39, 0, 43, 181, 244, 244, 179, 42, 0], "Top": 6, "Height": 8}, "170": {"Width": 5, "Advance": 8, "Bitmap": [0, 213, 249, 225, 83, 0, 21, 53, 255, 226, 7, 72, 90, 255, 254, 191, 255, 184, 255, 255, 240, 255, 22, 255, 255, 100, 224, 250, 238, 205], "Top": 4, "Height": 6}, "171": {"Width": 8, "Advance": 8, "Bitmap": [0, 41, 193, 16, 0, 46, 191, 14, 1, 194, 218, 1, 1, 198, 216, 1, 99, 255, 110, 0, 101, 255, 108, 0, 216, 255, 27, 0, 216, 255, 27, 0, 93, 255, 111, 0, 96, 255, 110, 0, 0, 188, 220, 2, 0, 193, 218, 1, 0, 37, 191, 15, 0, 41, 190, 14], "Top": 6, "Height": 7}, "172": {"Width": 7, "Advance": 8, "Bitmap": [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255], "Top": 8, "Height": 6}, "174": {"Width": 8, "Advance": 8, "Bitmap": [0, 58, 185, 244, 243, 184, 56, 0, 57, 241, 113, 23, 23, 113, 240, 55, 189, 108, 0, 239, 245, 142, 112, 186, 244, 14, 0, 255, 45, 234, 15, 244, 244, 14, 0, 255, 255, 82, 15, 244, 188, 105, 0, 255, 126, 198, 114, 184, 43, 236, 107, 21, 21, 108, 235, 39, 0, 43, 181, 244, 244, 179, 42, 0], "Top": 6, "Height": 8}, "175": {"Width": 5, "Advance": 8, "Bitmap": [255, 255, 255, 255, 255], "Top": 4, "Height": 1}, "176": {"Width": 4, "Advance": 8, "Bitmap": [88, 232, 230, 81, 233, 53, 51, 229, 234, 54, 52, 230, 94, 234, 231, 83], "Top": 3, "Height": 4}, "177": {"Width": 6, "Advance": 8, "Bitmap": [0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 5, "Height": 9}, "178": {"Width": 4, "Advance": 8, "Bitmap": [115, 232, 239, 110, 139, 41, 255, 241, 0, 90, 255, 211, 42, 240, 238, 42, 193, 255, 83, 0, 250, 255, 255, 255], "Top": 4, "Height": 6}, "179": {"Width": 5, "Advance": 8, "Bitmap": [146, 233, 237, 128, 0, 133, 47, 255, 241, 0, 0, 255, 255, 174, 0, 0, 1, 62, 254, 16, 136, 32, 52, 252, 15, 179, 244, 230, 108, 0], "Top": 4, "Height": 6}, "180": {"Width": 4, "Advance": 8, "Bitmap": [0, 35, 187, 63, 113, 245, 255, 174, 136, 179, 64, 0], "Top": 3, "Height": 3}, "181": {"Width": 6, "Advance": 8, "Bitmap": [255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 8, 0, 255, 255, 255, 255, 84, 6, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 230, 248, 225, 172, 255, 246, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0], "Top": 6, "Height": 11}, "182": {"Width": 7, "Advance": 8, "Bitmap": [1, 104, 203, 240, 248, 230, 190, 129, 255, 255, 255, 254, 255, 255, 238, 255, 255, 255, 1, 255, 255, 243, 255, 255, 255, 0, 255, 255, 188, 255, 255, 255, 0, 255, 255, 36, 200, 255, 255, 0, 255, 255, 0, 0, 255, 255, 0, 255, 255, 0, 0, 255, 255, 0, 255, 255, 0, 0, 255, 255, 0, 255, 255, 0, 0, 255, 255, 0, 255, 255, 0, 0, 255, 255, 0, 255, 255, 0, 0, 255, 255, 0, 255, 255, 0, 0, 255, 255, 0, 255, 255], "Top": 4, "Height": 13}, "183": {"Width": 3, "Advance": 9, "Bitmap": [91, 243, 85, 234, 255, 212, 91, 244, 85], "Top": 8, "Height": 3}, "184": {"Width": 3, "Advance": 8, "Bitmap": [0, 176, 122, 0, 61, 235, 207, 247, 150], "Top": 14, "Height": 3}, "185": {"Width": 5, "Advance": 8, "Bitmap": [27, 122, 236, 255, 0, 208, 224, 255, 255, 0, 26, 2, 255, 255, 0, 0, 0, 255, 255, 0, 255, 255, 255, 255, 255], "Top": 4, "Height": 5}, "186": {"Width": 6, "Advance": 8, "Bitmap": [5, 136, 238, 239, 137, 5, 122, 255, 255, 255, 255, 122, 217, 255, 75, 75, 255, 216, 246, 255, 6, 7, 255, 246, 218, 255, 73, 73, 255, 218, 125, 255, 255, 255, 255, 126, 5, 140, 231, 231, 140, 6], "Top": 4, "Height": 7}, "187": {"Width": 7, "Advance": 8, "Bitmap": [165, 77, 0, 159, 90, 0, 0, 153, 233, 21, 145, 237, 25, 0, 32, 250, 176, 22, 243, 181, 0, 0, 187, 255, 55, 162, 255, 56, 31, 250, 179, 21, 242, 183, 0, 152, 235, 24, 144, 240, 28, 0, 164, 84, 0, 159, 97, 0, 0], "Top": 6, "Height": 7}, "188": {"Width": 8, "Advance": 8, "Bitmap": [85, 247, 0, 0, 0, 60, 191, 0, 236, 255, 0, 0, 0, 188, 63, 0, 50, 255, 0, 0, 61, 190, 0, 0, 0, 255, 0, 0, 189, 62, 0, 0, 0, 0, 0, 62, 189, 0, 0, 0, 0, 0, 0, 190, 61, 0, 0, 0, 0, 0, 63, 188, 1, 186, 255, 0, 0, 0, 191, 60, 110, 149, 255, 0, 0, 64, 188, 0, 241, 255, 255, 255, 0, 191, 60, 0, 0, 0, 255, 0], "Top": 4, "Height": 10}, "189": {"Width": 8, "Advance": 8, "Bitmap": [69, 246, 0, 0, 0, 35, 213, 6, 198, 255, 0, 0, 0, 174, 81, 0, 0, 255, 0, 0, 63, 191, 0, 0, 0, 255, 0, 2, 204, 49, 0, 0, 0, 0, 0, 97, 158, 0, 0, 0, 0, 0, 12, 218, 25, 0, 0, 0, 0, 0, 132, 123, 0, 143, 245, 176, 0, 30, 216, 9, 0, 0, 40, 234, 0, 166, 89, 0, 0, 75, 205, 65, 56, 198, 1, 0, 0, 229, 255, 255], "Top": 4, "Height": 10}, "190": {"Width": 8, "Advance": 8, "Bitmap": [156, 246, 126, 0, 0, 3, 206, 46, 11, 91, 199, 0, 0, 102, 153, 0, 25, 255, 236, 0, 14, 219, 22, 0, 209, 250, 169, 0, 136, 119, 0, 0, 0, 0, 0, 33, 215, 7, 0, 0, 0, 0, 0, 171, 85, 0, 0, 0, 0, 0, 60, 194, 0, 171, 255, 88, 0, 1, 201, 52, 94, 164, 240, 88, 0, 94, 161, 0, 225, 255, 255, 219, 10, 217, 27, 0, 0, 0, 240, 88], "Top": 4, "Height": 10}, "191": {"Width": 6, "Advance": 8, "Bitmap": [0, 0, 199, 193, 0, 0, 0, 0, 200, 196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 184, 255, 48, 0, 0, 67, 254, 244, 10, 0, 29, 236, 255, 115, 0, 0, 170, 255, 173, 2, 0, 0, 240, 255, 19, 0, 0, 0, 233, 255, 79, 11, 103, 129, 148, 255, 255, 255, 255, 217, 13, 160, 240, 247, 196, 73], "Top": 6, "Height": 11}, "192": {"Width": 8, "Advance": 8, "Bitmap": [0, 0, 64, 187, 34, 0, 0, 0, 0, 0, 175, 255, 245, 113, 0, 0, 0, 0, 0, 65, 179, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 255, 92, 0, 0, 0, 0, 122, 255, 255, 172, 0, 0, 0, 0, 204, 234, 207, 244, 7, 0, 0, 32, 255, 165, 135, 255, 71, 0, 0, 113, 255, 98, 72, 255, 146, 0, 0, 191, 255, 42, 18, 255, 217, 0, 16, 251, 255, 255, 255, 255, 255, 30, 85, 255, 255, 255, 255, 255, 255, 98, 154, 255, 147, 0, 0, 130, 255, 161, 222, 255, 89, 0, 0, 70, 255, 224], "Top": 0, "Height": 14}, "193": {"Width": 8, "Advance": 8, "Bitmap": [0, 0, 0, 35, 187, 63, 0, 0, 0, 0, 113, 245, 255, 174, 0, 0, 0, 0, 136, 179, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 255, 92, 0, 0, 0, 0, 122, 255, 255, 172, 0, 0, 0, 0, 204, 234, 207, 244, 7, 0, 0, 32, 255, 165, 135, 255, 71, 0, 0, 113, 255, 98, 72, 255, 146, 0, 0, 191, 255, 42, 18, 255, 217, 0, 16, 251, 255, 255, 255, 255, 255, 30, 85, 255, 255, 255, 255, 255, 255, 98, 154, 255, 147, 0, 0, 130, 255, 161, 222, 255, 89, 0, 0, 70, 255, 224], "Top": 0, "Height": 14}, "194": {"Width": 8, "Advance": 8, "Bitmap": [0, 0, 1, 139, 139, 1, 0, 0, 0, 4, 162, 255, 255, 161, 4, 0, 0, 8, 186, 91, 89, 188, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 255, 92, 0, 0, 0, 0, 122, 255, 255, 172, 0, 0, 0, 0, 204, 234, 207, 244, 7, 0, 0, 32, 255, 165, 135, 255, 71, 0, 0, 113, 255, 98, 72, 255, 146, 0, 0, 191, 255, 42, 18, 255, 217, 0, 16, 251, 255, 255, 255, 255, 255, 30, 85, 255, 255, 255, 255, 255, 255, 98, 154, 255, 147, 0, 0, 130, 255, 161, 222, 255, 89, 0, 0, 70, 255, 224], "Top": 0, "Height": 14}, "195": {"Width": 8, "Advance": 8, "Bitmap": [0, 75, 226, 203, 49, 86, 167, 0, 0, 167, 85, 52, 206, 225, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 255, 92, 0, 0, 0, 0, 122, 255, 255, 172, 0, 0, 0, 0, 204, 234, 207, 244, 7, 0, 0, 32, 255, 165, 135, 255, 71, 0, 0, 113, 255, 98, 72, 255, 146, 0, 0, 191, 255, 42, 18, 255, 217, 0, 16, 251, 255, 255, 255, 255, 255, 30, 85, 255, 255, 255, 255, 255, 255, 98, 154, 255, 147, 0, 0, 130, 255, 161, 222, 255, 89, 0, 0, 70, 255, 224], "Top": 1, "Height": 13}, "196": {"Width": 8, "Advance": 8, "Bitmap": [0, 0, 199, 198, 0, 199, 199, 0, 0, 0, 201, 199, 0, 201, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 255, 92, 0, 0, 0, 0, 122, 255, 255, 172, 0, 0, 0, 0, 204, 234, 207, 244, 7, 0, 0, 32, 255, 165, 135, 255, 71, 0, 0, 113, 255, 98, 72, 255, 146, 0, 0, 191, 255, 42, 18, 255, 217, 0, 16, 251, 255, 255, 255, 255, 255, 30, 85, 255, 255, 255, 255, 255, 255, 98, 154, 255, 147, 0, 0, 130, 255, 161, 222, 255, 89, 0, 0, 70, 255, 224], "Top": 1, "Height": 13}, "197": {"Width": 8, "Advance": 8, "Bitmap": [0, 0, 27, 133, 134, 30, 0, 0, 0, 0, 197, 137, 137, 209, 0, 0, 0, 0, 213, 114, 113, 221, 0, 0, 0, 0, 134, 255, 255, 149, 0, 0, 0, 0, 195, 255, 255, 211, 0, 0, 0, 16, 252, 206, 208, 255, 28, 0, 0, 85, 255, 113, 115, 255, 100, 0, 0, 158, 255, 31, 33, 255, 172, 0, 0, 227, 214, 0, 0, 218, 237, 1, 39, 255, 255, 255, 255, 255, 255, 47, 104, 255, 255, 255, 255, 255, 255, 111, 165, 255, 70, 0, 0, 76, 255, 169, 226, 255, 18, 0, 0, 20, 255, 227], "Top": 1, "Height": 13}, "198": {"Width": 8, "Advance": 8, "Bitmap": [0, 0, 45, 255, 255, 255, 255, 255, 0, 0, 135, 243, 255, 255, 255, 255, 0, 0, 222, 183, 255, 255, 0, 0, 0, 52, 255, 118, 255, 255, 0, 0, 0, 134, 255, 57, 255, 255, 255, 0, 0, 210, 247, 6, 255, 255, 255, 0, 28, 255, 255, 255, 255, 255, 0, 0, 98, 255, 255, 255, 255, 255, 0, 0, 163, 255, 104, 0, 255, 255, 255, 255, 225, 255, 48, 0, 255, 255, 255, 255], "Top": 4, "Height": 10}, "199": {"Width": 8, "Advance": 8, "Bitmap": [0, 14, 140, 225, 248, 209, 91, 0, 9, 206, 255, 255, 255, 255, 223, 0, 122, 255, 216, 58, 9, 72, 133, 0, 209, 255, 64, 0, 0, 0, 0, 0, 246, 255, 8, 0, 0, 0, 0, 0, 248, 255, 16, 0, 0, 0, 0, 0, 213, 255, 74, 0, 0, 0, 0, 0, 126, 255, 221, 65, 11, 29, 155, 2, 9, 191, 255, 255, 255, 255, 255, 1, 0, 4, 116, 205, 255, 240, 123, 0, 0, 0, 0, 10, 236, 121, 0, 0, 0, 0, 0, 16, 46, 239, 0, 0, 0, 0, 0, 209, 244, 133, 0, 0], "Top": 4, "Height": 13}, "200": {"Width": 6, "Advance": 8, "Bitmap": [0, 64, 187, 34, 0, 0, 0, 175, 255, 245, 113, 0, 0, 0, 65, 179, 136, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 0, "Height": 14}, "201": {"Width": 6, "Advance": 8, "Bitmap": [0, 0, 35, 187, 63, 0, 0, 113, 245, 255, 174, 0, 0, 136, 179, 64, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 0, "Height": 14}, "202": {"Width": 6, "Advance": 8, "Bitmap": [0, 1, 139, 139, 1, 0, 4, 162, 255, 255, 161, 4, 8, 186, 91, 89, 188, 8, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 0, "Height": 14}, "203": {"Width": 6, "Advance": 8, "Bitmap": [199, 198, 0, 199, 199, 0, 201, 199, 0, 201, 201, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 1, "Height": 13}, "204": {"Width": 6, "Advance": 8, "Bitmap": [0, 64, 187, 34, 0, 0, 0, 175, 255, 245, 113, 0, 0, 0, 65, 179, 136, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 0, "Height": 14}, "205": {"Width": 6, "Advance": 8, "Bitmap": [0, 0, 35, 187, 63, 0, 0, 113, 245, 255, 174, 0, 0, 136, 179, 64, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 0, "Height": 14}, "206": {"Width": 6, "Advance": 8, "Bitmap": [0, 1, 139, 139, 1, 0, 4, 162, 255, 255, 161, 4, 8, 186, 91, 89, 188, 8, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 0, "Height": 14}, "207": {"Width": 6, "Advance": 8, "Bitmap": [0, 199, 198, 0, 199, 199, 0, 201, 199, 0, 201, 201, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], "Top": 1, "Height": 13}, "208": {"Width": 8, "Advance": 8, "Bitmap": [0, 213, 243, 252, 231, 161, 31, 0, 0, 255, 255, 255, 255, 255, 231, 25, 0, 255, 255, 4, 42, 212, 255, 144, 0, 255, 255, 0, 0, 65, 255, 218, 255, 255, 255, 255, 0, 12, 255, 247, 255, 255, 255, 255, 0, 15, 255, 246, 0, 255, 255, 0, 0, 72, 255, 214, 0, 255, 255, 9, 62, 220, 255, 135, 0, 255, 255, 255, 255, 255, 219, 17, 0, 219, 247, 250, 223, 146, 19, 0], "Top": 4, "Height": 10}, "209": {"Width": 7, "Advance": 8, "Bitmap": [0, 75, 226, 203, 49, 86, 167, 0, 167, 85, 52, 206, 225, 75, 0, 0, 0, 0, 0, 0, 0, 255, 246, 31, 0, 0, 255, 255, 255, 255, 171, 0, 0, 255, 255, 255, 255, 255, 53, 0, 255, 255, 255, 255, 229, 185, 0, 255, 255, 255, 255, 106, 255, 57, 255, 255, 255, 255, 6, 223, 178, 255, 255, 255, 255, 0, 97, 254, 255, 255, 255, 255, 0, 5, 229, 255, 255, 255, 255, 0, 0, 120, 255, 255, 255, 255, 0, 0, 17, 244, 255], "Top": 1, "Height": 13}, "210": {"Width": 7, "Advance": 8, "Bitmap": [0, 64, 187, 34, 0, 0, 0, 0, 175, 255, 245, 113, 0, 0, 0, 0, 65, 179, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 213, 249, 215, 83, 0, 55, 252, 255, 255, 255, 252, 57, 168, 255, 146, 19, 145, 255, 169, 226, 255, 32, 0, 34, 255, 226, 250, 255, 5, 0, 7, 255, 249, 250, 255, 5, 0, 7, 255, 249, 227, 255, 31, 0, 34, 255, 225, 170, 255, 145, 19, 147, 255, 168, 59, 253, 255, 255, 255, 252, 56, 0, 84, 215, 250, 215, 82, 0], "Top": 0, "Height": 14}, "211": {"Width": 7, "Advance": 8, "Bitmap": [0, 0, 35, 187, 63, 0, 0, 0, 113, 245, 255, 174, 0, 0, 0, 136, 179, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 213, 249, 215, 83, 0, 55, 252, 255, 255, 255, 252, 57, 168, 255, 146, 19, 145, 255, 169, 226, 255, 32, 0, 34, 255, 226, 250, 255, 5, 0, 7, 255, 249, 250, 255, 5, 0, 7, 255, 249, 227, 255, 31, 0, 34, 255, 225, 170, 255, 145, 19, 147, 255, 168, 59, 253, 255, 255, 255, 252, 56, 0, 84, 215, 250, 215, 82, 0], "Top": 0, "Height": 14}, "212": {"Width": 7, "Advance": 8, "Bitmap": [0, 0, 1, 139, 139, 1, 0, 0, 4, 162, 255, 255, 161, 4, 0, 8, 186, 91, 89, 188, 8, 0, 0, 0, 0, 0, 0, 0, 0, 78, 213, 249, 215, 83, 0, 55, 252, 255, 255, 255, 252, 57, 168, 255, 146, 19, 145, 255, 169, 226, 255, 32, 0, 34, 255, 226, 250, 255, 5, 0, 7, 255, 249, 250, 255, 5, 0, 7, 255, 249, 227, 255, 31, 0, 34, 255, 225, 170, 255, 145, 19, 147, 255, 168, 59, 253, 255, 255, 255, 252, 56, 0, 84, 215, 250, 215, 82, 0], "Top": 0, "Height": 14}, "213": {"Width": 7, "Advance": 8, "Bitmap": [0, 75, 226, 203, 49, 86, 167, 0, 167, 85, 52, 206, 225, 75, 0, 0, 0, 0, 0, 0, 0, 0, 78, 213, 249, 215, 83, 0, 55, 252, 255, 255, 255, 252, 57, 168, 255, 146, 19, 145, 255, 169, 226, 255, 32, 0, 34, 255, 226, 250, 255, 5, 0, 7, 255, 249, 250, 255, 5, 0, 7, 255, 249, 227, 255, 31, 0, 34, 255, 225, 170, 255, 145, 19, 147, 255, 168, 59, 253, 255, 255, 255, 252, 56, 0, 84, 215, 250, 215, 82, 0], "Top": 1, "Height": 13}, "214": {"Width": 7, "Advance": 8, "Bitmap": [0, 199, 198, 0, 199, 199, 0, 0, 201, 199, 0, 201, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 213, 249, 215, 83, 0, 55, 252, 255, 255, 255, 252, 57, 168, 255, 146, 19, 145, 255, 169, 226, 255, 32, 0, 34, 255, 226, 250, 255, 5, 0, 7, 255, 249, 250, 255, 5, 0, 7, 255, 249, 227, 255, 31, 0, 34, 255, 225, 170, 255, 145, 19, 147, 255, 168, 59, 253, 255, 255, 255, 252, 56, 0, 84, 215, 250, 215, 82, 0], "Top": 1, "Height": 13}, "215": {"Width": 6, "Advance": 8, "Bitmap": [102, 139, 0, 0, 141, 99, 156, 255, 132, 133, 255, 154, 1, 144, 255, 255, 140, 0, 0, 129, 255, 255, 132, 0, 145, 255, 124, 131, 255, 148, 98, 123, 0, 0, 129, 92], "Top": 7, "Height": 6}, "216": {"Width": 8, "Advance": 8, "Bitmap": [0, 0, 0, 0, 0, 0, 150, 18, 0, 34, 169, 237, 249, 214, 253, 52, 25, 233, 255, 255, 236, 255, 255, 0, 145, 255, 181, 20, 143, 255, 255, 0, 216, 255, 44, 57, 245, 255, 255, 0, 247, 255, 14, 216, 146, 255, 255, 0, 250, 255, 137, 247, 31, 255, 248, 0, 226, 255, 252, 143, 30, 255, 223, 0, 165, 255, 254, 46, 136, 255, 165, 0, 49, 254, 255, 255, 255, 252, 54, 0, 46, 246, 213, 249, 215, 81, 0, 0, 17, 139, 0, 0, 0, 0, 0, 0], "Top": 3, "Height": 12}, "217": {"Width": 6, "Advance": 8, "Bitmap": [0, 64, 187, 34, 0, 0, 0, 175, 255, 245, 113, 0, 0, 0, 65, 179, 136, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 249, 255, 7, 8, 255, 249, 225, 255, 71, 73, 255, 225, 148, 255, 255, 255, 255, 145, 14, 168, 245, 244, 164, 13], "Top": 0, "Height": 14}, "218": {"Width": 6, "Advance": 8, "Bitmap": [0, 0, 35, 187, 63, 0, 0, 113, 245, 255, 174, 0, 0, 136, 179, 64, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 249, 255, 7, 8, 255, 249, 225, 255, 71, 73, 255, 225, 148, 255, 255, 255, 255, 145, 14, 168, 245, 244, 164, 13], "Top": 0, "Height": 14}, "219": {"Width": 6, "Advance": 8, "Bitmap": [0, 1, 139, 139, 1, 0, 4, 162, 255, 255, 161, 4, 8, 186, 91, 89, 188, 8, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 249, 255, 7, 8, 255, 249, 225, 255, 71, 73, 255, 225, 148, 255, 255, 255, 255, 145, 14, 168, 245, 244, 164, 13], "Top": 0, "Height": 14}, "220": {"Width": 6, "Advance": 8, "Bitmap": [0, 199, 198, 0, 199, 199, 0, 201, 199, 0, 201, 201, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 249, 255, 7, 8, 255, 249, 225, 255, 71, 73, 255, 225, 148, 255, 255, 255, 255, 145, 14, 168, 245, 244, 164, 13], "Top": 1, "Height": 13}, "221": {"Width": 8, "Advance": 8, "Bitmap": [0, 0, 0, 35, 187, 63, 0, 0, 0, 0, 113, 245, 255, 174, 0, 0, 0, 0, 136, 179, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 201, 255, 76, 0, 0, 57, 255, 203, 92, 255, 172, 0, 0, 158, 255, 98, 6, 232, 249, 22, 17, 245, 233, 8, 0, 118, 255, 130, 120, 255, 120, 0, 0, 9, 227, 236, 234, 233, 11, 0, 0, 0, 98, 255, 255, 101, 0, 0, 0, 0, 2, 255, 255, 2, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0], "Top": 0, "Height": 14}, "222": {"Width": 6, "Advance": 8, "Bitmap": [255, 255, 2, 0, 0, 0, 255, 255, 254, 226, 136, 8, 255, 255, 249, 255, 255, 133, 255, 255, 0, 84, 255, 220, 255, 255, 0, 9, 255, 246, 255, 255, 3, 100, 255, 217, 255, 255, 255, 255, 255, 121, 255, 255, 252, 221, 125, 4, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0], "Top": 4, "Height": 10}, "223": {"Width": 7, "Advance": 8, "Bitmap": [9, 146, 238, 249, 193, 44, 0, 140, 255, 255, 255, 255, 206, 0, 225, 255, 88, 48, 255, 249, 0, 252, 255, 5, 44, 255, 194, 0, 255, 255, 0, 176, 255, 60, 0, 255, 255, 0, 244, 255, 20, 0, 255, 255, 0, 184, 255, 191, 17, 255, 255, 0, 7, 141, 255, 181, 255, 255, 0, 115, 40, 255, 245, 255, 255, 0, 231, 255, 255, 215, 255, 255, 0, 177, 248, 219, 70], "Top": 3, "Height": 11}, "224": {"Width": 7, "Advance": 8, "Bitmap": [0, 0, 64, 187, 34, 0, 0, 0, 0, 175, 255, 245, 113, 0, 0, 0, 0, 65, 179, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 232, 250, 235, 164, 19, 0, 211, 255, 255, 255, 255, 163, 0, 35, 15, 6, 71, 255, 235, 22, 160, 230, 250, 234, 255, 255, 198, 255, 255, 255, 255, 255, 255, 249, 255, 76, 6, 8, 255, 255, 195, 255, 255, 255, 255, 255, 255, 27, 168, 234, 251, 241, 223, 186], "Top": 2, "Height": 12}, "225": {"Width": 7, "Advance": 8, "Bitmap": [0, 0, 0, 35, 187, 63, 0, 0, 0, 113, 245, 255, 174, 0, 0, 0, 136, 179, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 232, 250, 235, 164, 19, 0, 211, 255, 255, 255, 255, 163, 0, 35, 15, 6, 71, 255, 235, 22, 160, 230, 250, 234, 255, 255, 198, 255, 255, 255, 255, 255, 255, 249, 255, 76, 6, 8, 255, 255, 195, 255, 255, 255, 255, 255, 255, 27, 168, 234, 251, 241, 223, 186], "Top": 2, "Height": 12}, "226": {"Width": 7, "Advance": 8, "Bitmap": [0, 0, 1, 139, 139, 1, 0, 0, 4, 162, 255, 255, 161, 4, 0, 8, 186, 91, 89, 188, 8, 0, 0, 0, 0, 0, 0, 0, 0, 181, 232, 250, 235, 164, 19, 0, 211, 255, 255, 255, 255, 163, 0, 35, 15, 6, 71, 255, 235, 22, 160, 230, 250, 234, 255, 255, 198, 255, 255, 255, 255, 255, 255, 249, 255, 76, 6, 8, 255, 255, 195, 255, 255, 255, 255, 255, 255, 27, 168, 234, 251, 241, 223, 186], "Top": 2, "Height": 12}, "227": {"Width": 7, "Advance": 8, "Bitmap": [0, 75, 226, 203, 49, 86, 167, 0, 167, 85, 52, 206, 225, 75, 0, 0, 0, 0, 0, 0, 0, 0, 181, 232, 250, 235, 164, 19, 0, 211, 255, 255, 255, 255, 163, 0, 35, 15, 6, 71, 255, 235, 22, 160, 230, 250, 234, 255, 255, 198, 255, 255, 255, 255, 255, 255, 249, 255, 76, 6, 8, 255, 255, 195, 255, 255, 255, 255, 255, 255, 27, 168, 234, 251, 241, 223, 186], "Top": 3, "Height": 11}, "228": {"Width": 7, "Advance": 8, "Bitmap": [0, 199, 198, 0, 199, 199, 0, 0, 201, 199, 0, 201, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 232, 250, 235, 164, 19, 0, 211, 255, 255, 255, 255, 163, 0, 35, 15, 6, 71, 255, 235, 22, 160, 230, 250, 234, 255, 255, 198, 255, 255, 255, 255, 255, 255, 249, 255, 76, 6, 8, 255, 255, 195, 255, 255, 255, 255, 255, 255, 27, 168, 234, 251, 241, 223, 186], "Top": 3, "Height": 11}, "229": {"Width": 7, "Advance": 8, "Bitmap": [0, 0, 141, 244, 141, 0, 0, 0, 0, 244, 72, 244, 0, 0, 0, 0, 143, 245, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 232, 250, 235, 164, 19, 0, 211, 255, 255, 255, 255, 163, 0, 35, 15, 6, 71, 255, 235, 22, 160, 230, 250, 234, 255, 255, 198, 255, 255, 255, 255, 255, 255, 249, 255, 76, 6, 8, 255, 255, 195, 255, 255, 255, 255, 255, 255, 27, 168, 234, 251, 241, 223, 186], "Top": 2, "Height": 12}, "230": {"Width": 9, "Advance": 8, "Bitmap": [0, 182, 243, 205, 99, 226, 207, 37, 0, 0, 218, 255, 255, 255, 255, 255, 170, 0, 0, 36, 14, 183, 255, 144, 81, 234, 0, 47, 203, 249, 247, 255, 255, 255, 255, 2, 206, 255, 255, 255, 255, 255, 255, 255, 6, 245, 255, 55, 123, 255, 150, 7, 26, 0, 189, 255, 255, 255, 255, 255, 255, 214, 0, 34, 194, 244, 196, 102, 214, 246, 186, 0], "Top": 6, "Height": 8}, "231": {"Width": 6, "Advance": 8, "Bitmap": [0, 70, 198, 246, 242, 179, 57, 253, 255, 255, 255, 190, 183, 255, 160, 26, 0, 0, 235, 255, 19, 0, 0, 0, 241, 255, 20, 0, 0, 0, 192, 255, 157, 23, 0, 0, 64, 249, 255, 255, 255, 211, 0, 59, 185, 252, 253, 206, 0, 0, 0, 211, 114, 0, 0, 0, 0, 44, 238, 0, 0, 0, 207, 245, 136, 0], "Top": 6, "Height": 11}, "232": {"Width": 8, "Advance": 8, "Bitmap": [0, 64, 187, 34, 0, 0, 0, 0, 0, 175, 255, 245, 113, 0, 0, 0, 0, 0, 65, 179, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 203, 249, 225, 119, 0, 0, 63, 253, 255, 255, 255, 255, 93, 0, 190, 255, 85, 8, 76, 255, 203, 0, 244, 255, 255, 255, 255, 255, 241, 0, 246, 255, 255, 255, 255, 255, 255, 4, 196, 255, 107, 20, 1, 0, 0, 0, 64, 251, 255, 255, 255, 255, 203, 0, 0, 57, 179, 236, 251, 234, 178, 0], "Top": 2, "Height": 12}, "233": {"Width": 8, "Advance": 8, "Bitmap": [0, 0, 0, 35, 187, 63, 0, 0, 0, 0, 113, 245, 255, 174, 0, 0, 0, 0, 136, 179, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 203, 249, 225, 119, 0, 0, 63, 253, 255, 255, 255, 255, 93, 0, 190, 255, 85, 8, 76, 255, 203, 0, 244, 255, 255, 255, 255, 255, 241, 0, 246, 255, 255, 255, 255, 255, 255, 4, 196, 255, 107, 20, 1, 0, 0, 0, 64, 251, 255, 255, 255, 255, 203, 0, 0, 57, 179, 236, 251, 234, 178, 0], "Top": 2, "Height": 12}, "234": {"Width": 8, "Advance": 8, "Bitmap": [0, 0, 1, 139, 139, 1, 0, 0, 0, 4, 162, 255, 255, 161, 4, 0, 0, 8, 186, 91, 89, 188, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 203, 249, 225, 119, 0, 0, 63, 253, 255, 255, 255, 255, 93, 0, 190, 255, 85, 8, 76, 255, 203, 0, 244, 255, 255, 255, 255, 255, 241, 0, 246, 255, 255, 255, 255, 255, 255, 4, 196, 255, 107, 20, 1, 0, 0, 0, 64, 251, 255, 255, 255, 255, 203, 0, 0, 57, 179, 236, 251, 234, 178, 0], "Top": 2, "Height": 12}, "235": {"Width": 8, "Advance": 8, "Bitmap": [0, 199, 198, 0, 199, 199, 0, 0, 0, 201, 199, 0, 201, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 203, 249, 225, 119, 0, 0, 63, 253, 255, 255, 255, 255, 93, 0, 190, 255, 85, 8, 76, 255, 203, 0, 244, 255, 255, 255, 255, 255, 241, 0, 246, 255, 255, 255, 255, 255, 255, 4, 196, 255, 107, 20, 1, 0, 0, 0, 64, 251, 255, 255, 255, 255, 203, 0, 0, 57, 179, 236, 251, 234, 178, 0], "Top": 3, "Height": 11}, "236": {"Width": 6, "Advance": 8, "Bitmap": [0, 64, 187, 34, 0, 0, 0, 175, 255, 245, 113, 0, 0, 0, 65, 179, 136, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 241, 255, 47, 103, 0, 0, 193, 255, 255, 239, 0, 0, 53, 215, 241, 155], "Top": 2, "Height": 12}, "237": {"Width": 6, "Advance": 8, "Bitmap": [0, 0, 35, 187, 63, 0, 0, 113, 245, 255, 174, 0, 0, 136, 179, 64, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 241, 255, 47, 103, 0, 0, 193, 255, 255, 239, 0, 0, 53, 215, 241, 155], "Top": 2, "Height": 12}, "238": {"Width": 6, "Advance": 8, "Bitmap": [0, 1, 139, 139, 1, 0, 4, 162, 255, 255, 161, 4, 8, 186, 91, 89, 188, 8, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 241, 255, 47, 103, 0, 0, 193, 255, 255, 239, 0, 0, 53, 215, 241, 155], "Top": 2, "Height": 12}, "239": {"Width": 6, "Advance": 8, "Bitmap": [199, 198, 0, 199, 199, 0, 201, 199, 0, 201, 201, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 241, 255, 47, 103, 0, 0, 193, 255, 255, 239, 0, 0, 53, 215, 241, 155], "Top": 3, "Height": 11}, "240": {"Width": 7, "Advance": 8, "Bitmap": [0, 0, 109, 207, 59, 38, 74, 0, 0, 113, 255, 253, 255, 195, 0, 0, 196, 197, 217, 255, 53, 0, 0, 1, 0, 58, 255, 146, 0, 85, 203, 237, 247, 255, 208, 97, 255, 255, 255, 255, 255, 244, 213, 255, 116, 21, 13, 255, 247, 245, 255, 10, 0, 32, 255, 222, 212, 255, 108, 15, 149, 255, 164, 104, 255, 255, 255, 255, 254, 59, 0, 106, 223, 249, 215, 88, 0], "Top": 3, "Height": 11}, "241": {"Width": 7, "Advance": 8, "Bitmap": [75, 226, 203, 49, 86, 167, 0, 167, 85, 52, 206, 225, 75, 0, 0, 0, 0, 0, 0, 0, 0, 175, 217, 238, 251, 229, 144, 8, 255, 255, 255, 255, 255, 255, 143, 255, 255, 7, 9, 115, 255, 222, 255, 255, 0, 0, 12, 255, 250, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255], "Top": 3, "Height": 11}, "242": {"Width": 7, "Advance": 8, "Bitmap": [0, 64, 187, 34, 0, 0, 0, 0, 175, 255, 245, 113, 0, 0, 0, 0, 65, 179, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 214, 249, 215, 89, 0, 64, 255, 255, 255, 255, 255, 66, 195, 255, 130, 13, 126, 255, 194, 246, 255, 16, 0, 15, 255, 246, 245, 255, 14, 0, 17, 255, 245, 192, 255, 123, 13, 133, 255, 192, 64, 254, 255, 255, 255, 254, 63, 0, 75, 215, 250, 216, 76, 0], "Top": 2, "Height": 12}, "243": {"Width": 7, "Advance": 8, "Bitmap": [0, 0, 0, 35, 187, 63, 0, 0, 0, 113, 245, 255, 174, 0, 0, 0, 136, 179, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 214, 249, 215, 89, 0, 64, 255, 255, 255, 255, 255, 66, 195, 255, 130, 13, 126, 255, 194, 246, 255, 16, 0, 15, 255, 246, 245, 255, 14, 0, 17, 255, 245, 192, 255, 123, 13, 133, 255, 192, 64, 254, 255, 255, 255, 254, 63, 0, 75, 215, 250, 216, 76, 0], "Top": 2, "Height": 12}, "244": {"Width": 7, "Advance": 8, "Bitmap": [0, 1, 139, 139, 1, 0, 0, 4, 162, 255, 255, 161, 4, 0, 8, 186, 91, 89, 188, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 214, 249, 215, 89, 0, 64, 255, 255, 255, 255, 255, 66, 195, 255, 130, 13, 126, 255, 194, 246, 255, 16, 0, 15, 255, 246, 245, 255, 14, 0, 17, 255, 245, 192, 255, 123, 13, 133, 255, 192, 64, 254, 255, 255, 255, 254, 63, 0, 75, 215, 250, 216, 76, 0], "Top": 2, "Height": 12}, "245": {"Width": 7, "Advance": 8, "Bitmap": [0, 75, 226, 203, 49, 86, 167, 0, 167, 85, 52, 206, 225, 75, 0, 0, 0, 0, 0, 0, 0, 0, 86, 214, 249, 215, 89, 0, 64, 255, 255, 255, 255, 255, 66, 195, 255, 130, 13, 126, 255, 194, 246, 255, 16, 0, 15, 255, 246, 245, 255, 14, 0, 17, 255, 245, 192, 255, 123, 13, 133, 255, 192, 64, 254, 255, 255, 255, 254, 63, 0, 75, 215, 250, 216, 76, 0], "Top": 3, "Height": 11}, "246": {"Width": 7, "Advance": 8, "Bitmap": [0, 199, 198, 0, 199, 199, 0, 0, 201, 199, 0, 201, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 214, 249, 215, 89, 0, 64, 255, 255, 255, 255, 255, 66, 195, 255, 130, 13, 126, 255, 194, 246, 255, 16, 0, 15, 255, 246, 245, 255, 14, 0, 17, 255, 245, 192, 255, 123, 13, 133, 255, 192, 64, 254, 255, 255, 255, 254, 63, 0, 75, 215, 250, 216, 76, 0], "Top": 3, "Height": 11}, "247": {"Width": 6, "Advance": 8, "Bitmap": [0, 0, 199, 200, 0, 0, 0, 0, 201, 202, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 199, 200, 0, 0, 0, 0, 199, 200, 0, 0], "Top": 6, "Height": 8}, "248": {"Width": 7, "Advance": 8, "Bitmap": [0, 0, 0, 0, 0, 21, 9, 0, 86, 214, 248, 212, 206, 158, 64, 255, 255, 255, 255, 255, 86, 195, 255, 127, 45, 246, 255, 187, 246, 252, 11, 195, 131, 240, 237, 226, 239, 129, 195, 12, 253, 237, 86, 254, 245, 44, 128, 255, 196, 0, 146, 255, 255, 255, 255, 87, 0, 200, 189, 238, 235, 113, 0, 0, 29, 1, 0, 0, 0, 0], "Top": 5, "Height": 10}, "249": {"Width": 7, "Advance": 8, "Bitmap": [0, 64, 187, 34, 0, 0, 0, 0, 175, 255, 245, 113, 0, 0, 0, 0, 65, 179, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 251, 255, 14, 0, 0, 255, 255, 226, 255, 120, 10, 12, 255, 255, 139, 255, 255, 255, 255, 255, 255, 10, 148, 231, 251, 238, 215, 168], "Top": 2, "Height": 12}, "250": {"Width": 7, "Advance": 8, "Bitmap": [0, 0, 0, 35, 187, 63, 0, 0, 0, 113, 245, 255, 174, 0, 0, 0, 136, 179, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 251, 255, 14, 0, 0, 255, 255, 226, 255, 120, 10, 12, 255, 255, 139, 255, 255, 255, 255, 255, 255, 10, 148, 231, 251, 238, 215, 168], "Top": 2, "Height": 12}, "251": {"Width": 7, "Advance": 8, "Bitmap": [0, 0, 1, 139, 139, 1, 0, 0, 4, 162, 255, 255, 161, 4, 0, 8, 186, 91, 89, 188, 8, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 251, 255, 14, 0, 0, 255, 255, 226, 255, 120, 10, 12, 255, 255, 139, 255, 255, 255, 255, 255, 255, 10, 148, 231, 251, 238, 215, 168], "Top": 2, "Height": 12}, "252": {"Width": 7, "Advance": 8, "Bitmap": [0, 199, 198, 0, 199, 199, 0, 0, 201, 199, 0, 201, 201, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 251, 255, 14, 0, 0, 255, 255, 226, 255, 120, 10, 12, 255, 255, 139, 255, 255, 255, 255, 255, 255, 10, 148, 231, 251, 238, 215, 168], "Top": 3, "Height": 11}, "253": {"Width": 8, "Advance": 8, "Bitmap": [0, 0, 0, 35, 187, 63, 0, 0, 0, 0, 113, 245, 255, 174, 0, 0, 0, 0, 136, 179, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 222, 255, 34, 0, 30, 255, 222, 0, 153, 255, 105, 0, 89, 255, 156, 0, 80, 255, 180, 0, 152, 255, 88, 0, 9, 244, 249, 16, 219, 253, 19, 0, 0, 165, 255, 141, 255, 200, 0, 0, 0, 66, 255, 254, 255, 120, 0, 0, 0, 1, 214, 255, 255, 37, 0, 0, 0, 0, 123, 255, 196, 0, 0, 0, 0, 44, 228, 255, 86, 0, 0, 174, 255, 255, 255, 185, 0, 0, 0, 198, 249, 234, 156, 13, 0, 0, 0], "Top": 2, "Height": 15}, "254": {"Width": 7, "Advance": 8, "Bitmap": [255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 240, 246, 198, 73, 0, 255, 255, 255, 255, 255, 254, 71, 255, 255, 8, 19, 163, 255, 196, 255, 255, 0, 0, 24, 255, 246, 255, 255, 0, 0, 14, 255, 238, 255, 255, 28, 10, 131, 255, 197, 255, 255, 255, 255, 255, 255, 90, 255, 255, 203, 248, 228, 106, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0], "Top": 3, "Height": 14}, "255": {"Width": 8, "Advance": 8, "Bitmap": [0, 0, 199, 198, 0, 199, 199, 0, 0, 0, 201, 199, 0, 201, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 222, 255, 34, 0, 30, 255, 222, 0, 153, 255, 105, 0, 89, 255, 156, 0, 80, 255, 180, 0, 152, 255, 88, 0, 9, 244, 249, 16, 219, 253, 19, 0, 0, 165, 255, 141, 255, 200, 0, 0, 0, 66, 255, 254, 255, 120, 0, 0, 0, 1, 214, 255, 255, 37, 0, 0, 0, 0, 123, 255, 196, 0, 0, 0, 0, 44, 228, 255, 86, 0, 0, 174, 255, 255, 255, 185, 0, 0, 0, 198, 249, 234, 156, 13, 0, 0, 0], "Top": 3, "Height": 14}}, "Size": 16}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/GenerateErrorCodes.py	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,143 @@
+#!/usr/bin/python
+
+# Orthanc - A Lightweight, RESTful DICOM Store
+# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, Belgium
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# In addition, as a special exception, the copyright holders of this
+# program give permission to link the code of its release with the
+# OpenSSL project's "OpenSSL" library (or with modified versions of it
+# that use the same license as the "OpenSSL" library), and distribute
+# the linked executables. You must obey the GNU General Public License
+# in all respects for all of the code used other than "OpenSSL". If you
+# modify file(s) with this exception, you may extend this exception to
+# your version of the file(s), but you are not obligated to do so. If
+# you do not wish to do so, delete this exception statement from your
+# version. If you delete this exception statement from all source files
+# in the program, then also delete it here.
+# 
+# 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+import json
+import os
+import re
+import sys
+
+START_PLUGINS = 1000000
+BASE = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+
+
+
+## 
+## Read all the available error codes and HTTP status
+##
+
+with open(os.path.join(BASE, 'Resources', 'ErrorCodes.json'), 'r') as f:
+    ERRORS = json.loads(re.sub('/\*.*?\*/', '', f.read()))
+
+for error in ERRORS:
+    if error['Code'] >= START_PLUGINS:
+        print('ERROR: Error code must be below %d, but "%s" is set to %d' % (START_PLUGINS, error['Name'], error['Code']))
+        sys.exit(-1)
+
+with open(os.path.join(BASE, 'Core', 'Enumerations.h'), 'r') as f:
+    a = f.read()
+
+HTTP = {}
+for i in re.findall('(HttpStatus_([0-9]+)_\w+)', a):
+    HTTP[int(i[1])] = i[0]
+
+
+
+##
+## Generate the "ErrorCode" enumeration in "Enumerations.h"
+##
+
+path = os.path.join(BASE, 'Core', 'Enumerations.h')
+with open(path, 'r') as f:
+    a = f.read()
+
+s = ',\n'.join(map(lambda x: '    ErrorCode_%s = %d    /*!< %s */' % (x['Name'], int(x['Code']), x['Description']), ERRORS))
+
+s += ',\n    ErrorCode_START_PLUGINS = %d' % START_PLUGINS
+a = re.sub('(enum ErrorCode\s*{)[^}]*?(\s*};)', r'\1\n%s\2' % s, a, re.DOTALL)
+
+with open(path, 'w') as f:
+    f.write(a)
+
+
+
+##
+## Generate the "OrthancPluginErrorCode" enumeration in "OrthancCPlugin.h"
+##
+
+path = os.path.join(BASE, 'Plugins', 'Include', 'orthanc', 'OrthancCPlugin.h')
+with open(path, 'r') as f:
+    a = f.read()
+
+s = ',\n'.join(map(lambda x: '    OrthancPluginErrorCode_%s = %d    /*!< %s */' % (x['Name'], int(x['Code']), x['Description']), ERRORS))
+s += ',\n\n    _OrthancPluginErrorCode_INTERNAL = 0x7fffffff\n  '
+a = re.sub('(typedef enum\s*{)[^}]*?(} OrthancPluginErrorCode;)', r'\1\n%s\2' % s, a, re.DOTALL)
+
+with open(path, 'w') as f:
+    f.write(a)
+
+
+
+##
+## Generate the "EnumerationToString(ErrorCode)" and
+## "ConvertErrorCodeToHttpStatus(ErrorCode)" functions in
+## "Enumerations.cpp"
+##
+
+path = os.path.join(BASE, 'Core', 'Enumerations.cpp')
+with open(path, 'r') as f:
+    a = f.read()
+
+s = '\n\n'.join(map(lambda x: '      case ErrorCode_%s:\n        return "%s";' % (x['Name'], x['Description']), ERRORS))
+a = re.sub('(EnumerationToString\(ErrorCode.*?\)\s*{\s*switch \([^)]*?\)\s*{)[^}]*?(\s*default:)',
+           r'\1\n%s\2' % s, a, re.DOTALL)
+
+def GetHttpStatus(x):
+    s = HTTP[x['HttpStatus']]
+    return '      case ErrorCode_%s:\n        return %s;' % (x['Name'], s)
+
+s = '\n\n'.join(map(GetHttpStatus, filter(lambda x: 'HttpStatus' in x, ERRORS)))
+a = re.sub('(ConvertErrorCodeToHttpStatus\(ErrorCode.*?\)\s*{\s*switch \([^)]*?\)\s*{)[^}]*?(\s*default:)',
+           r'\1\n%s\2' % s, a, re.DOTALL)
+
+with open(path, 'w') as f:
+    f.write(a)
+
+
+
+##
+## Generate the "ErrorCode" enumeration in "OrthancSQLiteException.h"
+##
+
+path = os.path.join(BASE, 'Core', 'SQLite', 'OrthancSQLiteException.h')
+with open(path, 'r') as f:
+    a = f.read()
+
+e = filter(lambda x: 'SQLite' in x and x['SQLite'], ERRORS)
+s = ',\n'.join(map(lambda x: '      ErrorCode_%s' % x['Name'], e))
+a = re.sub('(enum ErrorCode\s*{)[^}]*?(\s*};)', r'\1\n%s\2' % s, a, re.DOTALL)
+
+s = '\n\n'.join(map(lambda x: '          case ErrorCode_%s:\n            return "%s";' % (x['Name'], x['Description']), e))
+a = re.sub('(EnumerationToString\(ErrorCode.*?\)\s*{\s*switch \([^)]*?\)\s*{)[^}]*?(\s*default:)',
+           r'\1\n%s\2' % s, a, re.DOTALL)
+
+with open(path, 'w') as f:
+    f.write(a)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/MinGW-W64-Toolchain32.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,17 @@
+# the name of the target operating system
+set(CMAKE_SYSTEM_NAME Windows)
+
+# which compilers to use for C and C++
+set(CMAKE_C_COMPILER i686-w64-mingw32-gcc)
+set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
+set(CMAKE_RC_COMPILER i686-w64-mingw32-windres)
+
+# here is the target environment located
+set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32)
+
+# adjust the default behaviour of the FIND_XXX() commands:
+# search headers and libraries in the target environment, search 
+# programs in the host environment
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/MinGW-W64-Toolchain64.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,17 @@
+# the name of the target operating system
+set(CMAKE_SYSTEM_NAME Windows)
+
+# which compilers to use for C and C++
+set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
+set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
+set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
+
+# here is the target environment located
+set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32)
+
+# adjust the default behaviour of the FIND_XXX() commands:
+# search headers and libraries in the target environment, search 
+# programs in the host environment
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
--- a/Resources/MinGW64Toolchain.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-# http://sourceforge.net/apps/trac/mingw-w64/wiki/GeneralUsageInstructions
-
-# the name of the target operating system
-set(CMAKE_SYSTEM_NAME Windows)
-
-# Detect the prefix of the mingw-w64 compiler
-execute_process(
-  COMMAND uname -p
-  OUTPUT_VARIABLE MINGW64_ARCHITECTURE
-  OUTPUT_STRIP_TRAILING_WHITESPACE
-  )
-
-if (${MINGW64_ARCHITECTURE} STREQUAL "x86_64")
-  set(MINGW64_PREFIX "x86_64")
-else()
-  set(MINGW64_PREFIX "i686")
-endif()
-  
-# which compilers to use for C and C++
-set(CMAKE_C_COMPILER ${MINGW64_PREFIX}-w64-mingw32-gcc)
-set(CMAKE_CXX_COMPILER ${MINGW64_PREFIX}-w64-mingw32-g++)
-set(CMAKE_RC_COMPILER ${MINGW64_PREFIX}-w64-mingw32-windres)
-
-# here is the target environment located
-set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32)
-
-# adjust the default behaviour of the FIND_XXX() commands:
-# search headers and libraries in the target environment, search 
-# programs in the host environment
-set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
--- a/Resources/MinGWToolchain.cmake	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/MinGWToolchain.cmake	Wed Sep 30 13:23:31 2015 +0200
@@ -1,5 +1,3 @@
-# http://www.vtk.org/Wiki/CmakeMingw
-
 # the name of the target operating system
 set(CMAKE_SYSTEM_NAME Windows)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/OldBuildInstructions.txt	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,108 @@
+This file contains old build instructions that are not tested anymore.
+
+
+Debian Squeeze (6.x)
+--------------------
+
+# sudo apt-get install build-essential unzip cmake mercurial \
+       	       	       uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \
+       	       	       libgoogle-glog-dev libpng-dev libgtest-dev \
+       	       	       libsqlite3-dev libssl-dev zlib1g-dev
+
+# cmake -DALLOW_DOWNLOADS=ON \
+  	-DUSE_SYSTEM_BOOST=OFF \
+	-DUSE_SYSTEM_DCMTK=OFF \
+	-DUSE_SYSTEM_MONGOOSE=OFF \
+	-DUSE_SYSTEM_JSONCPP=OFF \
+	-DUSE_SYSTEM_PUGIXML=OFF \
+        -DENABLE_JPEG=OFF \
+        -DENABLE_JPEG_LOSSLESS=OFF \
+	~/Orthanc 
+
+
+Debian Wheezy (7.x)
+-------------------
+
+# sudo apt-get install build-essential unzip cmake mercurial \
+       	       	       uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \
+       	       	       libgtest-dev libpng-dev libsqlite3-dev \
+       	       	       libssl-dev zlib1g-dev libdcmtk2-dev \
+       	       	       libboost-all-dev libwrap0-dev libjsoncpp-dev
+
+# cmake -DALLOW_DOWNLOADS=ON \
+        -DUSE_SYSTEM_GOOGLE_LOG=OFF \
+	-DUSE_SYSTEM_MONGOOSE=OFF \
+        -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \
+	-DUSE_SYSTEM_PUGIXML=OFF \
+        -DENABLE_JPEG=OFF \
+        -DENABLE_JPEG_LOSSLESS=OFF \
+	~/Orthanc
+
+
+
+SUPPORTED - Ubuntu 12.10
+------------------------
+
+# sudo apt-get install build-essential unzip cmake mercurial \
+       	       	       uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \
+       	       	       libgoogle-glog-dev libgtest-dev libpng-dev \
+       	       	       libsqlite3-dev libssl-dev zlib1g-dev \
+       	       	       libdcmtk2-dev libboost-all-dev libwrap0-dev libcharls-dev
+
+With JPEG:
+
+# cmake "-DDCMTK_LIBRARIES=CharLS;dcmjpls;wrap;oflog" \
+        -DALLOW_DOWNLOADS=ON \
+	-DUSE_SYSTEM_MONGOOSE=OFF \
+	-DUSE_SYSTEM_JSONCPP=OFF \
+        -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \
+	-DUSE_SYSTEM_PUGIXML=OFF \
+	~/Orthanc
+
+
+Without JPEG:
+
+# cmake "-DDCMTK_LIBRARIES=wrap;oflog" \
+        -DALLOW_DOWNLOADS=ON \
+	-DUSE_SYSTEM_MONGOOSE=OFF \
+	-DUSE_SYSTEM_JSONCPP=OFF \
+        -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \
+	-DUSE_SYSTEM_PUGIXML=OFF \
+        -DENABLE_JPEG=OFF \
+        -DENABLE_JPEG_LOSSLESS=OFF \
+	~/Orthanc
+
+
+SUPPORTED - Ubuntu 13.10
+------------------------
+
+# sudo apt-get install build-essential unzip cmake mercurial \
+       	       	       uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \
+       	       	       libgoogle-glog-dev libgtest-dev libpng-dev \
+       	       	       libsqlite3-dev libssl-dev zlib1g-dev \
+       	       	       libdcmtk2-dev libboost-all-dev libwrap0-dev libjsoncpp-dev
+
+# cmake "-DDCMTK_LIBRARIES=wrap;oflog" \
+        -DALLOW_DOWNLOADS=ON \
+	-DUSE_SYSTEM_MONGOOSE=OFF \
+        -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \
+	-DUSE_SYSTEM_PUGIXML=OFF \
+        -DENABLE_JPEG=OFF \
+        -DENABLE_JPEG_LOSSLESS=OFF \
+	~/Orthanc
+
+
+SUPPORTED - Fedora 19
+---------------------
+
+# sudo yum install unzip make automake gcc gcc-c++ python cmake \
+                   boost-devel curl-devel dcmtk-devel glog-devel \
+                   gtest-devel libpng-devel libsqlite3x-devel libuuid-devel \
+                   mongoose-devel openssl-devel jsoncpp-devel lua-devel pugixml-devel
+
+# cmake  "-DDCMTK_LIBRARIES=CharLS" \
+         -DSYSTEM_MONGOOSE_USE_CALLBACKS=OFF \
+         ~/Orthanc
+       
+Note: Have also a look at the official package:
+http://pkgs.fedoraproject.org/cgit/orthanc.git/tree/?h=f18
--- a/Resources/Orthanc.doxygen	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/Orthanc.doxygen	Wed Sep 30 13:23:31 2015 +0200
@@ -656,8 +656,7 @@
 # with spaces.
 
 INPUT                  = @CMAKE_SOURCE_DIR@/Core \
-                         @CMAKE_SOURCE_DIR@/OrthancServer \
-                         @CMAKE_SOURCE_DIR@/OrthancCppClient
+                         @CMAKE_SOURCE_DIR@/OrthancServer
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
@@ -941,7 +940,7 @@
 # page will contain the date and time when the page was generated. Setting
 # this to NO can help when comparing the output of multiple runs.
 
-HTML_TIMESTAMP         = YES
+HTML_TIMESTAMP         = NO
 
 # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
 # documentation will contain sections that can be hidden and shown after the
--- a/Resources/OrthancClient.doxygen	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1792 +0,0 @@
-# Doxyfile 1.8.1.2
-
-# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project.
-#
-# All text after a hash (#) is considered a comment and will be ignored.
-# The format is:
-#       TAG = value [value, ...]
-# For lists items can also be appended using:
-#       TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ").
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the
-# iconv built into libc) for the transcoding. See
-# http://www.gnu.org/software/libiconv for the list of possible encodings.
-
-DOXYFILE_ENCODING      = UTF-8
-
-# The PROJECT_NAME tag is a single word (or sequence of words) that should
-# identify the project. Note that if you do not use Doxywizard you need
-# to put quotes around the project name if it contains spaces.
-
-PROJECT_NAME           = "Orthanc Client"
-
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
-# if some version control system is used.
-
-PROJECT_NUMBER         =
-
-# Using the PROJECT_BRIEF tag one can provide an optional one line description
-# for a project that appears at the top of each page and should give viewer
-# a quick idea about the purpose of the project. Keep the description short.
-
-PROJECT_BRIEF          = "Documentation of the client library of Orthanc"
-
-# With the PROJECT_LOGO tag one can specify an logo or icon that is
-# included in the documentation. The maximum height of the logo should not
-# exceed 55 pixels and the maximum width should not exceed 200 pixels.
-# Doxygen will copy the logo to the output directory.
-
-PROJECT_LOGO           = @CMAKE_SOURCE_DIR@/Resources/OrthancLogoDocumentation.png
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
-# where doxygen was started. If left blank the current directory will be used.
-
-OUTPUT_DIRECTORY       = OrthancClientDocumentation
-
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
-# otherwise cause performance problems for the file system.
-
-CREATE_SUBDIRS         = NO
-
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
-# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
-# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
-# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
-# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
-# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
-
-OUTPUT_LANGUAGE        = English
-
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
-# Set to NO to disable this.
-
-BRIEF_MEMBER_DESC      = YES
-
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
-# brief descriptions will be completely suppressed.
-
-REPEAT_BRIEF           = NO
-
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
-# "represents" "a" "an" "the"
-
-ABBREVIATE_BRIEF       =
-
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
-# description.
-
-ALWAYS_DETAILED_SEC    = NO
-
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
-# operators of the base classes will not be shown.
-
-INLINE_INHERITED_MEMB  = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
-# to NO the shortest path that makes the file name unique will be used.
-
-FULL_PATH_NAMES        = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the
-# path to strip.
-
-STRIP_FROM_PATH        =
-
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
-# are normally passed to the compiler using the -I flag.
-
-STRIP_FROM_INC_PATH    =
-
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful if your file system
-# doesn't support long names like on DOS, Mac, or CD-ROM.
-
-SHORT_NAMES            = NO
-
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like regular Qt-style comments
-# (thus requiring an explicit @brief command for a brief description.)
-
-JAVADOC_AUTOBRIEF      = NO
-
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
-# interpret the first line (until the first dot) of a Qt-style
-# comment as the brief description. If set to NO, the comments
-# will behave just like regular Qt-style comments (thus requiring
-# an explicit \brief command for a brief description.)
-
-QT_AUTOBRIEF           = NO
-
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
-# description. Set this tag to YES if you prefer the old behaviour instead.
-
-MULTILINE_CPP_IS_BRIEF = NO
-
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
-# re-implements.
-
-INHERIT_DOCS           = YES
-
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
-# be part of the file/class/namespace that contains it.
-
-SEPARATE_MEMBER_PAGES  = NO
-
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
-# Doxygen uses this value to replace tabs by spaces in code fragments.
-
-TAB_SIZE               = 8
-
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
-# You can put \n's in the value part of an alias to insert newlines.
-
-ALIASES                =
-
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding
-# "class=itcl::class" will allow you to use the command class in the
-# itcl::class meaning.
-
-TCL_SUBST              =
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
-# of all members will be omitted, etc.
-
-OPTIMIZE_OUTPUT_FOR_C  = NO
-
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for
-# Java. For instance, namespaces will be presented as packages, qualified
-# scopes will look different, etc.
-
-OPTIMIZE_OUTPUT_JAVA   = NO
-
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources only. Doxygen will then generate output that is more tailored for
-# Fortran.
-
-OPTIMIZE_FOR_FORTRAN   = NO
-
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for
-# VHDL.
-
-OPTIMIZE_OUTPUT_VHDL   = NO
-
-# Doxygen selects the parser to use depending on the extension of the files it
-# parses. With this tag you can assign which parser to use for a given extension.
-# Doxygen has a built-in mapping, but you can override or extend it using this
-# tag. The format is ext=language, where ext is a file extension, and language
-# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
-# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
-# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
-# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
-# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
-
-EXTENSION_MAPPING      =
-
-# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
-# comments according to the Markdown format, which allows for more readable
-# documentation. See http://daringfireball.net/projects/markdown/ for details.
-# The output of markdown processing is further processed by doxygen, so you
-# can mix doxygen, HTML, and XML commands with Markdown formatting.
-# Disable only in case of backward compatibilities issues.
-
-MARKDOWN_SUPPORT       = YES
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should
-# set this tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also makes the inheritance and collaboration
-# diagrams that involve STL classes more complete and accurate.
-
-BUILTIN_STL_SUPPORT    = YES
-
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
-# enable parsing support.
-
-CPP_CLI_SUPPORT        = NO
-
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
-# Doxygen will parse them like normal C++ but will assume all classes use public
-# instead of private inheritance when no explicit protection keyword is present.
-
-SIP_SUPPORT            = NO
-
-# For Microsoft's IDL there are propget and propput attributes to indicate getter
-# and setter methods for a property. Setting this option to YES (the default)
-# will make doxygen replace the get and set methods by a property in the
-# documentation. This will only work if the methods are indeed getting or
-# setting a simple type. If this is not the case, or you want to show the
-# methods anyway, you should set this option to NO.
-
-IDL_PROPERTY_SUPPORT   = YES
-
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
-# all members of a group must be documented explicitly.
-
-DISTRIBUTE_GROUP_DOC   = NO
-
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
-# the \nosubgrouping command.
-
-SUBGROUPING            = YES
-
-# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
-# unions are shown inside the group in which they are included (e.g. using
-# @ingroup) instead of on a separate page (for HTML and Man pages) or
-# section (for LaTeX and RTF).
-
-INLINE_GROUPED_CLASSES = NO
-
-# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
-# unions with only public data fields will be shown inline in the documentation
-# of the scope in which they are defined (i.e. file, namespace, or group
-# documentation), provided this scope is documented. If set to NO (the default),
-# structs, classes, and unions are shown on a separate page (for HTML and Man
-# pages) or section (for LaTeX and RTF).
-
-INLINE_SIMPLE_STRUCTS  = NO
-
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
-# is documented as struct, union, or enum with the name of the typedef. So
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
-# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically
-# be useful for C code in case the coding convention dictates that all compound
-# types are typedef'ed and only the typedef is referenced, never the tag name.
-
-TYPEDEF_HIDES_STRUCT   = NO
-
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
-# determine which symbols to keep in memory and which to flush to disk.
-# When the cache is full, less often used symbols will be written to disk.
-# For small to medium size projects (<1000 input files) the default value is
-# probably good enough. For larger projects a too small cache size can cause
-# doxygen to be busy swapping symbols to and from disk most of the time
-# causing a significant performance penalty.
-# If the system has enough physical memory increasing the cache will improve the
-# performance by keeping more symbols in memory. Note that the value works on
-# a logarithmic scale so increasing the size by one will roughly double the
-# memory usage. The cache size is given by this formula:
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
-# corresponding to a cache size of 2^16 = 65536 symbols.
-
-SYMBOL_CACHE_SIZE      = 0
-
-# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
-# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
-# their name and scope. Since this can be an expensive process and often the
-# same symbol appear multiple times in the code, doxygen keeps a cache of
-# pre-resolved symbols. If the cache is too small doxygen will become slower.
-# If the cache is too large, memory is wasted. The cache size is given by this
-# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
-# corresponding to a cache size of 2^16 = 65536 symbols.
-
-LOOKUP_CACHE_SIZE      = 0
-
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
-
-EXTRACT_ALL            = NO
-
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
-
-EXTRACT_PRIVATE        = NO
-
-# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal scope will be included in the documentation.
-
-EXTRACT_PACKAGE        = NO
-
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
-
-EXTRACT_STATIC         = NO
-
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
-# If set to NO only classes defined in header files are included.
-
-EXTRACT_LOCAL_CLASSES  = YES
-
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
-# If set to NO (the default) only methods in the interface are included.
-
-EXTRACT_LOCAL_METHODS  = NO
-
-# If this flag is set to YES, the members of anonymous namespaces will be
-# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base
-# name of the file that contains the anonymous namespace. By default
-# anonymous namespaces are hidden.
-
-EXTRACT_ANON_NSPACES   = NO
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
-# This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_MEMBERS     = NO
-
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_CLASSES     = NO
-
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
-# documentation.
-
-HIDE_FRIEND_COMPOUNDS  = YES
-
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
-# function's detailed documentation block.
-
-HIDE_IN_BODY_DOCS      = NO
-
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
-# Set it to YES to include the internal documentation.
-
-INTERNAL_DOCS          = NO
-
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
-
-CASE_SENSE_NAMES       = YES
-
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
-# documentation. If set to YES the scope will be hidden.
-
-HIDE_SCOPE_NAMES       = NO
-
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
-# of that file.
-
-SHOW_INCLUDE_FILES     = YES
-
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
-# will list include files with double quotes in the documentation
-# rather than with sharp brackets.
-
-FORCE_LOCAL_INCLUDES   = NO
-
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
-# is inserted in the documentation for inline members.
-
-INLINE_INFO            = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
-# declaration order.
-
-SORT_MEMBER_DOCS       = YES
-
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
-# declaration order.
-
-SORT_BRIEF_DOCS        = NO
-
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
-# will sort the (brief and detailed) documentation of class members so that
-# constructors and destructors are listed first. If set to NO (the default)
-# the constructors will appear in the respective orders defined by
-# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
-# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
-# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
-
-SORT_MEMBERS_CTORS_1ST = NO
-
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
-# hierarchy of group names into alphabetical order. If set to NO (the default)
-# the group names will appear in their defined order.
-
-SORT_GROUP_NAMES       = NO
-
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
-# alphabetical list.
-
-SORT_BY_SCOPE_NAME     = NO
-
-# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
-# do proper type resolution of all parameters of a function it will reject a
-# match between the prototype and the implementation of a member function even
-# if there is only one candidate or it is obvious which candidate to choose
-# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
-# will still accept a match between prototype and implementation in such cases.
-
-STRICT_PROTO_MATCHING  = NO
-
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
-# commands in the documentation.
-
-GENERATE_TODOLIST      = YES
-
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
-# commands in the documentation.
-
-GENERATE_TESTLIST      = YES
-
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
-# commands in the documentation.
-
-GENERATE_BUGLIST       = YES
-
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
-# \deprecated commands in the documentation.
-
-GENERATE_DEPRECATEDLIST= YES
-
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if sectionname ... \endif.
-
-ENABLED_SECTIONS       =
-
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or macro consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and macros in the
-# documentation can be controlled using \showinitializer or \hideinitializer
-# command in the documentation regardless of this setting.
-
-MAX_INITIALIZER_LINES  = 30
-
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
-# list will mention the files that were used to generate the documentation.
-
-SHOW_USED_FILES        = YES
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
-# This will remove the Files entry from the Quick Index and from the
-# Folder Tree View (if specified). The default is YES.
-
-SHOW_FILES             = YES
-
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page.
-# This will remove the Namespaces entry from the Quick Index
-# and from the Folder Tree View (if specified). The default is YES.
-
-SHOW_NAMESPACES        = YES
-
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from
-# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
-# is used as the file version. See the manual for examples.
-
-FILE_VERSION_FILTER    =
-
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
-# by doxygen. The layout file controls the global structure of the generated
-# output files in an output format independent way. To create the layout file
-# that represents doxygen's defaults, run doxygen with the -l option.
-# You can optionally specify a file name after the option, if omitted
-# DoxygenLayout.xml will be used as the name of the layout file.
-
-LAYOUT_FILE            =
-
-# The CITE_BIB_FILES tag can be used to specify one or more bib files
-# containing the references data. This must be a list of .bib files. The
-# .bib extension is automatically appended if omitted. Using this command
-# requires the bibtex tool to be installed. See also
-# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
-# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
-# feature you need bibtex and perl available in the search path.
-
-CITE_BIB_FILES         =
-
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-
-# The QUIET tag can be used to turn on/off the messages that are generated
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
-
-QUIET                  = NO
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
-# NO is used.
-
-WARNINGS               = YES
-
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
-# automatically be disabled.
-
-WARN_IF_UNDOCUMENTED   = YES
-
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
-# don't exist or using markup commands wrongly.
-
-WARN_IF_DOC_ERROR      = YES
-
-# The WARN_NO_PARAMDOC option can be enabled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
-# documentation.
-
-WARN_NO_PARAMDOC       = YES
-
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
-# be obtained via FILE_VERSION_FILTER)
-
-WARN_FORMAT            = "$file:$line: $text"
-
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
-# to stderr.
-
-WARN_LOGFILE           =
-
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
-# with spaces.
-
-INPUT                  = @CMAKE_SOURCE_DIR@/OrthancCppClient/SharedLibrary/AUTOGENERATED/OrthancCppClient.h
-
-# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
-# also the default input encoding. Doxygen uses libiconv (or the iconv built
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
-# the list of possible encodings.
-
-INPUT_ENCODING         = UTF-8
-
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
-# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
-# *.f90 *.f *.for *.vhd *.vhdl
-
-FILE_PATTERNS          = *.h
-
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
-# If left blank NO is used.
-
-RECURSIVE              = YES
-
-# The EXCLUDE tag can be used to specify files and/or directories that should be
-# excluded from the INPUT source files. This way you can easily exclude a
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-# Note that relative paths are relative to the directory from which doxygen is
-# run.
-
-EXCLUDE                =
-
-# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
-# directories that are symbolic links (a Unix file system feature) are excluded
-# from the input.
-
-EXCLUDE_SYMLINKS       = NO
-
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
-# for example use the pattern */test/*
-
-EXCLUDE_PATTERNS       =
-
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# output. The symbol name can be a fully qualified name, a word, or if the
-# wildcard * is used, a substring. Examples: ANamespace, AClass,
-# AClass::ANamespace, ANamespace::*Test
-
-EXCLUDE_SYMBOLS        = OrthancClient::Internals
-
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
-# the \include command).
-
-EXAMPLE_PATH           =
-
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
-
-EXAMPLE_PATTERNS       =
-
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
-# Possible values are YES and NO. If left blank NO is used.
-
-EXAMPLE_RECURSIVE      = NO
-
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
-# the \image command).
-
-IMAGE_PATH             =
-
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output.
-# If FILTER_PATTERNS is specified, this tag will be
-# ignored.
-
-INPUT_FILTER           =
-
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis.
-# Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match.
-# The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty or if
-# non of the patterns match the file name, INPUT_FILTER is applied.
-
-FILTER_PATTERNS        =
-
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
-
-FILTER_SOURCE_FILES    = NO
-
-# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
-# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
-# and it is also possible to disable source filtering for a specific pattern
-# using *.ext= (so without naming a filter). This option only has effect when
-# FILTER_SOURCE_FILES is enabled.
-
-FILTER_SOURCE_PATTERNS =
-
-#---------------------------------------------------------------------------
-# configuration options related to source browsing
-#---------------------------------------------------------------------------
-
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
-# VERBATIM_HEADERS is set to NO.
-
-SOURCE_BROWSER         = NO
-
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
-
-INLINE_SOURCES         = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C, C++ and Fortran comments will always remain visible.
-
-STRIP_CODE_COMMENTS    = YES
-
-# If the REFERENCED_BY_RELATION tag is set to YES
-# then for each documented function all documented
-# functions referencing it will be listed.
-
-REFERENCED_BY_RELATION = NO
-
-# If the REFERENCES_RELATION tag is set to YES
-# then for each documented function all documented entities
-# called/used by that function will be listed.
-
-REFERENCES_RELATION    = NO
-
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code.
-# Otherwise they will link to the documentation.
-
-REFERENCES_LINK_SOURCE = YES
-
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
-# will need version 4.8.6 or higher.
-
-USE_HTAGS              = NO
-
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
-# which an include is specified. Set to NO to disable this.
-
-VERBATIM_HEADERS       = YES
-
-#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
-# contains a lot of classes, structs, unions or interfaces.
-
-ALPHABETICAL_INDEX     = YES
-
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
-# in which this list will be split (can be a number in the range [1..20])
-
-COLS_IN_ALPHA_INDEX    = 5
-
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
-# should be ignored while generating the index headers.
-
-IGNORE_PREFIX          =
-
-#---------------------------------------------------------------------------
-# configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
-
-GENERATE_HTML          = YES
-
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `html' will be used as the default path.
-
-HTML_OUTPUT            = doc
-
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
-# doxygen will generate files with .html extension.
-
-HTML_FILE_EXTENSION    = .html
-
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard header. Note that when using a custom header you are responsible
-#  for the proper inclusion of any scripts and style sheets that doxygen
-# needs, which is dependent on the configuration options used.
-# It is advised to generate a default header using "doxygen -w html
-# header.html footer.html stylesheet.css YourConfigFile" and then modify
-# that header. Note that the header is subject to change so you typically
-# have to redo this when upgrading to a newer version of doxygen or when
-# changing the value of configuration settings such as GENERATE_TREEVIEW!
-
-HTML_HEADER            =
-
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard footer.
-
-HTML_FOOTER            =
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If the tag is left blank doxygen
-# will generate a default style sheet. Note that doxygen will try to copy
-# the style sheet file to the HTML output directory, so don't put your own
-# style sheet in the HTML output directory as well, or it will be erased!
-
-HTML_STYLESHEET        =
-
-# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
-# other source files which should be copied to the HTML output directory. Note
-# that these files will be copied to the base HTML output directory. Use the
-# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
-# files. In the HTML_STYLESHEET file, use the file name only. Also note that
-# the files will be copied as-is; there are no commands or markers available.
-
-HTML_EXTRA_FILES       =
-
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
-# Doxygen will adjust the colors in the style sheet and background images
-# according to this color. Hue is specified as an angle on a colorwheel,
-# see http://en.wikipedia.org/wiki/Hue for more information.
-# For instance the value 0 represents red, 60 is yellow, 120 is green,
-# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
-# The allowed range is 0 to 359.
-
-HTML_COLORSTYLE_HUE    = 220
-
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
-# the colors in the HTML output. For a value of 0 the output will use
-# grayscales only. A value of 255 will produce the most vivid colors.
-
-HTML_COLORSTYLE_SAT    = 100
-
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
-# the luminance component of the colors in the HTML output. Values below
-# 100 gradually make the output lighter, whereas values above 100 make
-# the output darker. The value divided by 100 is the actual gamma applied,
-# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
-# and 100 does not change the gamma.
-
-HTML_COLORSTYLE_GAMMA  = 80
-
-# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting
-# this to NO can help when comparing the output of multiple runs.
-
-HTML_TIMESTAMP         = YES
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
-# page has loaded.
-
-HTML_DYNAMIC_SECTIONS  = NO
-
-# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
-# entries shown in the various tree structured indices initially; the user
-# can expand and collapse entries dynamically later on. Doxygen will expand
-# the tree to such a level that at most the specified number of entries are
-# visible (unless a fully collapsed tree already exceeds this amount).
-# So setting the number of entries 1 will produce a full collapsed tree by
-# default. 0 is a special value representing an infinite number of entries
-# and will result in a full expanded tree by default.
-
-HTML_INDEX_NUM_ENTRIES = 100
-
-# If the GENERATE_DOCSET tag is set to YES, additional index files
-# will be generated that can be used as input for Apple's Xcode 3
-# integrated development environment, introduced with OSX 10.5 (Leopard).
-# To create a documentation set, doxygen will generate a Makefile in the
-# HTML output directory. Running make will produce the docset in that
-# directory and running "make install" will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
-# it at startup.
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
-# for more information.
-
-GENERATE_DOCSET        = NO
-
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
-# feed. A documentation feed provides an umbrella under which multiple
-# documentation sets from a single provider (such as a company or product suite)
-# can be grouped.
-
-DOCSET_FEEDNAME        = "Doxygen generated docs"
-
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
-# should uniquely identify the documentation set bundle. This should be a
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
-# will append .docset to the name.
-
-DOCSET_BUNDLE_ID       = org.doxygen.Project
-
-# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
-# the documentation publisher. This should be a reverse domain-name style
-# string, e.g. com.mycompany.MyDocSet.documentation.
-
-DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
-
-# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
-
-DOCSET_PUBLISHER_NAME  = Publisher
-
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
-# of the generated HTML documentation.
-
-GENERATE_HTMLHELP      = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
-# written to the html output directory.
-
-CHM_FILE               =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
-# the HTML help compiler on the generated index.hhp.
-
-HHC_LOCATION           =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
-# it should be included in the master .chm file (NO).
-
-GENERATE_CHI           = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file
-# content.
-
-CHM_INDEX_ENCODING     =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
-# normal table of contents (NO) in the .chm file.
-
-BINARY_TOC             = NO
-
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
-# to the contents of the HTML help documentation and to the tree view.
-
-TOC_EXPAND             = NO
-
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
-# that can be used as input for Qt's qhelpgenerator to generate a
-# Qt Compressed Help (.qch) of the generated HTML documentation.
-
-GENERATE_QHP           = NO
-
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
-# be used to specify the file name of the resulting .qch file.
-# The path specified is relative to the HTML output folder.
-
-QCH_FILE               =
-
-# The QHP_NAMESPACE tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#namespace
-
-QHP_NAMESPACE          = org.doxygen.Project
-
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#virtual-folders
-
-QHP_VIRTUAL_FOLDER     = doc
-
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
-# add. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#custom-filters
-
-QHP_CUST_FILTER_NAME   =
-
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
-# Qt Help Project / Custom Filters</a>.
-
-QHP_CUST_FILTER_ATTRS  =
-
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's
-# filter section matches.
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
-# Qt Help Project / Filter Attributes</a>.
-
-QHP_SECT_FILTER_ATTRS  =
-
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
-# be used to specify the location of Qt's qhelpgenerator.
-# If non-empty doxygen will try to run qhelpgenerator on the generated
-# .qhp file.
-
-QHG_LOCATION           =
-
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
-#  will be generated, which together with the HTML files, form an Eclipse help
-# plugin. To install this plugin and make it available under the help contents
-# menu in Eclipse, the contents of the directory containing the HTML and XML
-# files needs to be copied into the plugins directory of eclipse. The name of
-# the directory within the plugins directory should be the same as
-# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
-# the help appears.
-
-GENERATE_ECLIPSEHELP   = NO
-
-# A unique identifier for the eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have
-# this name.
-
-ECLIPSE_DOC_ID         = org.doxygen.Project
-
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
-# at top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it. Since the tabs have the same information as the
-# navigation tree you can set this option to NO if you already set
-# GENERATE_TREEVIEW to YES.
-
-DISABLE_INDEX          = NO
-
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information.
-# If the tag value is set to YES, a side panel will be generated
-# containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
-# Windows users are probably better off using the HTML help feature.
-# Since the tree basically has the same information as the tab index you
-# could consider to set DISABLE_INDEX to NO when enabling this option.
-
-GENERATE_TREEVIEW      = NO
-
-# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
-# (range [0,1..20]) that doxygen will group on one line in the generated HTML
-# documentation. Note that a value of 0 will completely suppress the enum
-# values from appearing in the overview section.
-
-ENUM_VALUES_PER_LINE   = 1
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
-# used to set the initial width (in pixels) of the frame in which the tree
-# is shown.
-
-TREEVIEW_WIDTH         = 250
-
-# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
-# links to external symbols imported via tag files in a separate window.
-
-EXT_LINKS_IN_WINDOW    = NO
-
-# Use this tag to change the font size of Latex formulas included
-# as images in the HTML documentation. The default is 10. Note that
-# when you change the font size after a successful doxygen run you need
-# to manually remove any form_*.png images from the HTML output directory
-# to force them to be regenerated.
-
-FORMULA_FONTSIZE       = 10
-
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are
-# not supported properly for IE 6.0, but are supported on all modern browsers.
-# Note that when changing this option you need to delete any form_*.png files
-# in the HTML output before the changes have effect.
-
-FORMULA_TRANSPARENT    = YES
-
-# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
-# (see http://www.mathjax.org) which uses client side Javascript for the
-# rendering instead of using prerendered bitmaps. Use this if you do not
-# have LaTeX installed or if you want to formulas look prettier in the HTML
-# output. When enabled you may also need to install MathJax separately and
-# configure the path to it using the MATHJAX_RELPATH option.
-
-USE_MATHJAX            = NO
-
-# When MathJax is enabled you need to specify the location relative to the
-# HTML output directory using the MATHJAX_RELPATH option. The destination
-# directory should contain the MathJax.js script. For instance, if the mathjax
-# directory is located at the same level as the HTML output directory, then
-# MATHJAX_RELPATH should be ../mathjax. The default value points to
-# the MathJax Content Delivery Network so you can quickly see the result without
-# installing MathJax.
-# However, it is strongly recommended to install a local
-# copy of MathJax from http://www.mathjax.org before deployment.
-
-MATHJAX_RELPATH        = http://www.mathjax.org/mathjax
-
-# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
-# names that should be enabled during MathJax rendering.
-
-MATHJAX_EXTENSIONS     =
-
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box
-# for the HTML output. The underlying search engine uses javascript
-# and DHTML and should work on any modern browser. Note that when using
-# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
-# (GENERATE_DOCSET) there is already a search function so this one should
-# typically be disabled. For large projects the javascript based search engine
-# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
-
-SEARCHENGINE           = NO
-
-# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a PHP enabled web server instead of at the web client
-# using Javascript. Doxygen will generate the search PHP script and index
-# file to put on the web server. The advantage of the server
-# based approach is that it scales better to large projects and allows
-# full text search. The disadvantages are that it is more difficult to setup
-# and does not have live searching capabilities.
-
-SERVER_BASED_SEARCH    = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
-
-GENERATE_LATEX         = NO
-
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `latex' will be used as the default path.
-
-LATEX_OUTPUT           = latex
-
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked. If left blank `latex' will be used as the default command name.
-# Note that when enabling USE_PDFLATEX this option is only used for
-# generating bitmaps for formulas in the HTML output, but not in the
-# Makefile that is written to the output directory.
-
-LATEX_CMD_NAME         = latex
-
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
-# generate index for LaTeX. If left blank `makeindex' will be used as the
-# default command name.
-
-MAKEINDEX_CMD_NAME     = makeindex
-
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_LATEX          = NO
-
-# The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, letter, legal and
-# executive. If left blank a4wide will be used.
-
-PAPER_TYPE             = a4
-
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
-# packages that should be included in the LaTeX output.
-
-EXTRA_PACKAGES         =
-
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
-# standard header. Notice: only use this tag if you know what you are doing!
-
-LATEX_HEADER           =
-
-# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
-# the generated latex document. The footer should contain everything after
-# the last chapter. If it is left blank doxygen will generate a
-# standard footer. Notice: only use this tag if you know what you are doing!
-
-LATEX_FOOTER           =
-
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will
-# contain links (just like the HTML output) instead of page references
-# This makes the output suitable for online browsing using a pdf viewer.
-
-PDF_HYPERLINKS         = YES
-
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
-# plain latex in the generated Makefile. Set this option to YES to get a
-# higher quality PDF documentation.
-
-USE_PDFLATEX           = YES
-
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
-# This option is also used when generating formulas in HTML.
-
-LATEX_BATCHMODE        = NO
-
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not
-# include the index chapters (such as File Index, Compound Index, etc.)
-# in the output.
-
-LATEX_HIDE_INDICES     = NO
-
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include
-# source code with syntax highlighting in the LaTeX output.
-# Note that which sources are shown also depends on other settings
-# such as SOURCE_BROWSER.
-
-LATEX_SOURCE_CODE      = NO
-
-# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
-# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
-# http://en.wikipedia.org/wiki/BibTeX for more info.
-
-LATEX_BIB_STYLE        = plain
-
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
-# other RTF readers or editors.
-
-GENERATE_RTF           = NO
-
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `rtf' will be used as the default path.
-
-RTF_OUTPUT             = rtf
-
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_RTF            = NO
-
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using WORD or other
-# programs which support those fields.
-# Note: wordpad (write) and others do not support links.
-
-RTF_HYPERLINKS         = NO
-
-# Load style sheet definitions from file. Syntax is similar to doxygen's
-# config file, i.e. a series of assignments. You only have to provide
-# replacements, missing definitions are set to their default value.
-
-RTF_STYLESHEET_FILE    =
-
-# Set optional variables used in the generation of an rtf document.
-# Syntax is similar to doxygen's config file.
-
-RTF_EXTENSIONS_FILE    =
-
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
-
-GENERATE_MAN           = NO
-
-# The MAN_OUTPUT tag is used to specify where the man pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `man' will be used as the default path.
-
-MAN_OUTPUT             = man
-
-# The MAN_EXTENSION tag determines the extension that is added to
-# the generated man pages (default is the subroutine's section .3)
-
-MAN_EXTENSION          = .3
-
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
-# then it will generate one additional man file for each entity
-# documented in the real man page(s). These additional files
-# only source the real man page, but without them the man command
-# would be unable to find the correct page. The default is NO.
-
-MAN_LINKS              = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the XML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
-# the code including all documentation.
-
-GENERATE_XML           = NO
-
-# The XML_OUTPUT tag is used to specify where the XML pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `xml' will be used as the default path.
-
-XML_OUTPUT             = xml
-
-# The XML_SCHEMA tag can be used to specify an XML schema,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_SCHEMA             =
-
-# The XML_DTD tag can be used to specify an XML DTD,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_DTD                =
-
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
-# dump the program listings (including syntax highlighting
-# and cross-referencing information) to the XML output. Note that
-# enabling this will significantly increase the size of the XML output.
-
-XML_PROGRAMLISTING     = YES
-
-#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
-# and incomplete at the moment.
-
-GENERATE_AUTOGEN_DEF   = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
-# moment.
-
-GENERATE_PERLMOD       = NO
-
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able
-# to generate PDF and DVI output from the Perl module output.
-
-PERLMOD_LATEX          = NO
-
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader.
-# This is useful
-# if you want to understand what is going on.
-# On the other hand, if this
-# tag is set to NO the size of the Perl module output will be much smaller
-# and Perl will parse it just the same.
-
-PERLMOD_PRETTY         = YES
-
-# The names of the make variables in the generated doxyrules.make file
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
-# This is useful so different doxyrules.make files included by the same
-# Makefile don't overwrite each other's variables.
-
-PERLMOD_MAKEVAR_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
-#---------------------------------------------------------------------------
-
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
-# files.
-
-ENABLE_PREPROCESSING   = YES
-
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
-# way by setting EXPAND_ONLY_PREDEF to YES.
-
-MACRO_EXPANSION        = NO
-
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
-# PREDEFINED and EXPAND_AS_DEFINED tags.
-
-EXPAND_ONLY_PREDEF     = NO
-
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# pointed to by INCLUDE_PATH will be searched when a #include is found.
-
-SEARCH_INCLUDES        = YES
-
-# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
-# the preprocessor.
-
-INCLUDE_PATH           =
-
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
-# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
-# be used.
-
-INCLUDE_FILE_PATTERNS  =
-
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
-# instead of the = operator.
-
-PREDEFINED             =
-
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition that
-# overrules the definition found in the source code.
-
-EXPAND_AS_DEFINED      =
-
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all references to function-like macros
-# that are alone on a line, have an all uppercase name, and do not end with a
-# semicolon, because these will confuse the parser if not removed.
-
-SKIP_FUNCTION_MACROS   = YES
-
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references
-#---------------------------------------------------------------------------
-
-# The TAGFILES option can be used to specify one or more tagfiles. For each
-# tag file the location of the external documentation should be added. The
-# format of a tag file without this location is as follows:
-#
-# TAGFILES = file1 file2 ...
-# Adding location for the tag files is done as follows:
-#
-# TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths
-# or URLs. Note that each tag file must have a unique name (where the name does
-# NOT include the path). If a tag file is not located in the directory in which
-# doxygen is run, you must also specify the path to the tagfile here.
-
-TAGFILES               =
-
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
-# a tag file that is based on the input files it reads.
-
-GENERATE_TAGFILE       =
-
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
-# will be listed.
-
-ALLEXTERNALS           = NO
-
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
-# be listed.
-
-EXTERNAL_GROUPS        = YES
-
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of `which perl').
-
-PERL_PATH              = /usr/bin/perl
-
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
-#---------------------------------------------------------------------------
-
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option also works with HAVE_DOT disabled, but it is recommended to
-# install and use dot, since it yields more powerful graphs.
-
-CLASS_DIAGRAMS         = YES
-
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
-MSCGEN_PATH            =
-
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
-# or is not a class.
-
-HIDE_UNDOC_RELATIONS   = YES
-
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
-# have no effect if this option is set to NO (the default)
-
-HAVE_DOT               = NO
-
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
-# allowed to run in parallel. When set to 0 (the default) doxygen will
-# base this on the number of processors available in the system. You can set it
-# explicitly to a value larger than 0 to get control over the balance
-# between CPU load and processing speed.
-
-DOT_NUM_THREADS        = 0
-
-# By default doxygen will use the Helvetica font for all dot files that
-# doxygen generates. When you want a differently looking font you can specify
-# the font name using DOT_FONTNAME. You need to make sure dot is able to find
-# the font, which can be done by putting it in a standard location or by setting
-# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
-# directory containing the font.
-
-DOT_FONTNAME           = Helvetica
-
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
-# The default size is 10pt.
-
-DOT_FONTSIZE           = 10
-
-# By default doxygen will tell dot to use the Helvetica font.
-# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
-# set the path where dot can find it.
-
-DOT_FONTPATH           =
-
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
-# CLASS_DIAGRAMS tag to NO.
-
-CLASS_GRAPH            = YES
-
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
-# class references variables) of the class with other documented classes.
-
-COLLABORATION_GRAPH    = YES
-
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for groups, showing the direct groups dependencies
-
-GROUP_GRAPHS           = YES
-
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
-# collaboration diagrams in a style similar to the OMG's Unified Modeling
-# Language.
-
-UML_LOOK               = NO
-
-# If the UML_LOOK tag is enabled, the fields and methods are shown inside
-# the class node. If there are many fields or methods and many nodes the
-# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
-# threshold limits the number of items for each type to make the size more
-# managable. Set this to 0 for no limit. Note that the threshold may be
-# exceeded by 50% before the limit is enforced.
-
-UML_LIMIT_NUM_FIELDS   = 10
-
-# If set to YES, the inheritance and collaboration graphs will show the
-# relations between templates and their instances.
-
-TEMPLATE_RELATIONS     = NO
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
-# other documented files.
-
-INCLUDE_GRAPH          = YES
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
-# indirectly include this file.
-
-INCLUDED_BY_GRAPH      = YES
-
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then
-# doxygen will generate a call dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable call graphs
-# for selected functions only using the \callgraph command.
-
-CALL_GRAPH             = NO
-
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
-# doxygen will generate a caller dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable caller
-# graphs for selected functions only using the \callergraph command.
-
-CALLER_GRAPH           = NO
-
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will generate a graphical hierarchy of all classes instead of a textual one.
-
-GRAPHICAL_HIERARCHY    = YES
-
-# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
-# relations between the files in the directories.
-
-DIRECTORY_GRAPH        = YES
-
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are svg, png, jpg, or gif.
-# If left blank png will be used. If you choose svg you need to set
-# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
-# visible in IE 9+ (other browsers do not have this requirement).
-
-DOT_IMAGE_FORMAT       = png
-
-# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
-# enable generation of interactive SVG images that allow zooming and panning.
-# Note that this requires a modern browser other than Internet Explorer.
-# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
-# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
-# visible. Older versions of IE do not have SVG support.
-
-INTERACTIVE_SVG        = NO
-
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
-# found. If left blank, it is assumed the dot tool can be found in the path.
-
-DOT_PATH               =
-
-# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
-# \dotfile command).
-
-DOTFILE_DIRS           =
-
-# The MSCFILE_DIRS tag can be used to specify one or more directories that
-# contain msc files that are included in the documentation (see the
-# \mscfile command).
-
-MSCFILE_DIRS           =
-
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen if the
-# number of direct children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
-# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
-
-DOT_GRAPH_MAX_NODES    = 50
-
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
-# graphs generated by dot. A depth value of 3 means that only nodes reachable
-# from the root by following a path via at most 3 edges will be shown. Nodes
-# that lay further from the root node will be omitted. Note that setting this
-# option to 1 or 2 may greatly reduce the computation time needed for large
-# code bases. Also note that the size of a graph can be further restricted by
-# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
-
-MAX_DOT_GRAPH_DEPTH    = 0
-
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not
-# seem to support this out of the box. Warning: Depending on the platform used,
-# enabling this option may lead to badly anti-aliased labels on the edges of
-# a graph (i.e. they become hard to read).
-
-DOT_TRANSPARENT        = NO
-
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
-# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
-# support this, this feature is disabled by default.
-
-DOT_MULTI_TARGETS      = YES
-
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
-# arrows in the dot generated graphs.
-
-GENERATE_LEGEND        = YES
-
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
-# the various graphs.
-
-DOT_CLEANUP            = YES
--- a/Resources/OrthancPlugin.doxygen	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/OrthancPlugin.doxygen	Wed Sep 30 13:23:31 2015 +0200
@@ -655,7 +655,9 @@
 # directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
-INPUT                  = @CMAKE_SOURCE_DIR@/Plugins/Include/
+INPUT                  = @CMAKE_SOURCE_DIR@/Plugins/Include/orthanc/OrthancCPlugin.h \
+                         @CMAKE_SOURCE_DIR@/Plugins/Include/orthanc/OrthancCDatabasePlugin.h \
+                         @CMAKE_SOURCE_DIR@/Plugins/Include/orthanc/OrthancCppDatabasePlugin.h
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
@@ -939,7 +941,7 @@
 # page will contain the date and time when the page was generated. Setting
 # this to NO can help when comparing the output of multiple runs.
 
-HTML_TIMESTAMP         = YES
+HTML_TIMESTAMP         = NO
 
 # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
 # documentation will contain sections that can be hidden and shown after the
--- a/Resources/Patches/boost-1.55.0-clang-atomic.patch	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
---- boost/atomic/detail/cas128strong.hpp
-+++ boost/atomic/detail/cas128strong.hpp
-@@ -196,15 +196,17 @@ class base_atomic<T, void, 16, Sign>
- 
- public:
-     BOOST_DEFAULTED_FUNCTION(base_atomic(void), {})
--    explicit base_atomic(value_type const& v) BOOST_NOEXCEPT : v_(0)
-+    explicit base_atomic(value_type const& v) BOOST_NOEXCEPT
-     {
-+        memset(&v_, 0, sizeof(v_));
-         memcpy(&v_, &v, sizeof(value_type));
-     }
- 
-     void
-     store(value_type const& value, memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT
-     {
--        storage_type value_s = 0;
-+        storage_type value_s;
-+        memset(&value_s, 0, sizeof(value_s));
-         memcpy(&value_s, &value, sizeof(value_type));
-         platform_fence_before_store(order);
-         platform_store128(value_s, &v_);
-@@ -247,7 +249,9 @@ class base_atomic<T, void, 16, Sign>
-         memory_order success_order,
-         memory_order failure_order) volatile BOOST_NOEXCEPT
-     {
--        storage_type expected_s = 0, desired_s = 0;
-+        storage_type expected_s, desired_s;
-+        memset(&expected_s, 0, sizeof(expected_s));
-+        memset(&desired_s, 0, sizeof(desired_s));
-         memcpy(&expected_s, &expected, sizeof(value_type));
-         memcpy(&desired_s, &desired, sizeof(value_type));
- 
---- boost/atomic/detail/gcc-atomic.hpp
-+++ boost/atomic/detail/gcc-atomic.hpp
-@@ -958,14 +958,16 @@ class base_atomic<T, void, 16, Sign>
- 
- public:
-     BOOST_DEFAULTED_FUNCTION(base_atomic(void), {})
--    explicit base_atomic(value_type const& v) BOOST_NOEXCEPT : v_(0)
-+    explicit base_atomic(value_type const& v) BOOST_NOEXCEPT
-     {
-+        memset(&v_, 0, sizeof(v_));
-         memcpy(&v_, &v, sizeof(value_type));
-     }
- 
-     void store(value_type const& v, memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT
-     {
--        storage_type tmp = 0;
-+        storage_type tmp;
-+        memset(&tmp, 0, sizeof(tmp));
-         memcpy(&tmp, &v, sizeof(value_type));
-         __atomic_store_n(&v_, tmp, atomics::detail::convert_memory_order_to_gcc(order));
-     }
-@@ -980,7 +982,8 @@ class base_atomic<T, void, 16, Sign>
- 
-     value_type exchange(value_type const& v, memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT
-     {
--        storage_type tmp = 0;
-+        storage_type tmp;
-+        memset(&tmp, 0, sizeof(tmp));
-         memcpy(&tmp, &v, sizeof(value_type));
-         tmp = __atomic_exchange_n(&v_, tmp, atomics::detail::convert_memory_order_to_gcc(order));
-         value_type res;
-@@ -994,7 +997,9 @@ class base_atomic<T, void, 16, Sign>
-         memory_order success_order,
-         memory_order failure_order) volatile BOOST_NOEXCEPT
-     {
--        storage_type expected_s = 0, desired_s = 0;
-+        storage_type expected_s, desired_s;
-+        memset(&expected_s, 0, sizeof(expected_s));
-+        memset(&desired_s, 0, sizeof(desired_s));
-         memcpy(&expected_s, &expected, sizeof(value_type));
-         memcpy(&desired_s, &desired, sizeof(value_type));
-         const bool success = __atomic_compare_exchange_n(&v_, &expected_s, desired_s, false,
-@@ -1010,7 +1015,9 @@ class base_atomic<T, void, 16, Sign>
-         memory_order success_order,
-         memory_order failure_order) volatile BOOST_NOEXCEPT
-     {
--        storage_type expected_s = 0, desired_s = 0;
-+        storage_type expected_s, desired_s;
-+        memset(&expected_s, 0, sizeof(expected_s));
-+        memset(&desired_s, 0, sizeof(desired_s));
-         memcpy(&expected_s, &expected, sizeof(value_type));
-         memcpy(&desired_s, &desired, sizeof(value_type));
-         const bool success = __atomic_compare_exchange_n(&v_, &expected_s, desired_s, true,
--- 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Patches/dcmtk-linux-speed.patch	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,24 @@
+diff -urEb dcmtk-3.6.0.orig/dcmnet/libsrc/dul.cc dcmtk-3.6.0/dcmnet/libsrc/dul.cc
+--- dcmtk-3.6.0.orig/dcmnet/libsrc/dul.cc	2010-12-01 09:26:36.000000000 +0100
++++ dcmtk-3.6.0/dcmnet/libsrc/dul.cc	2015-05-15 17:03:50.762451757 +0200
+@@ -1840,7 +1840,7 @@
+     }
+ #endif
+ #endif
+-    setTCPBufferLength(sock);
++    //setTCPBufferLength(sock);
+ 
+ #ifndef DONT_DISABLE_NAGLE_ALGORITHM
+     /*
+diff -urEb dcmtk-3.6.0.orig/dcmnet/libsrc/dulfsm.cc dcmtk-3.6.0/dcmnet/libsrc/dulfsm.cc
+--- dcmtk-3.6.0.orig/dcmnet/libsrc/dulfsm.cc	2010-12-01 09:26:36.000000000 +0100
++++ dcmtk-3.6.0/dcmnet/libsrc/dulfsm.cc	2015-05-15 17:03:55.570451952 +0200
+@@ -2417,7 +2417,7 @@
+           return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str());
+         }
+ #endif
+-        setTCPBufferLength(s);
++        //setTCPBufferLength(s);
+ 
+ #ifndef DONT_DISABLE_NAGLE_ALGORITHM
+         /*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Patches/glog-port-h-v2.diff	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,52 @@
+124,130c124,146
+< // ----------------------------------- THREADS
+< typedef DWORD pthread_t;
+< typedef DWORD pthread_key_t;
+< typedef LONG pthread_once_t;
+< enum { PTHREAD_ONCE_INIT = 0 };   // important that this be 0! for SpinLock
+< #define pthread_self  GetCurrentThreadId
+< #define pthread_equal(pthread_t_1, pthread_t_2)  ((pthread_t_1)==(pthread_t_2))
+---
+> // ----------------------------------- SECURE STRINGS
+> 
+> #if HAVE_SECURE_STRING_EXTENSIONS == 0
+> // Emulation of "localtime_s" and "strerror_s" for old versions of MinGW
+> inline int localtime_s(tm * _tm, const time_t * time)
+> {
+>   tm * posix_local_time_struct = localtime(time);
+>   if (posix_local_time_struct == NULL) 
+>   {
+>     return 1;
+>   }
+> 
+>   *_tm = *posix_local_time_struct;
+> 
+>   return 0;
+> }
+> 
+> inline char* strerror_s(char* buf, size_t buflen, int errnum) 
+> {
+>   const char* str = strerror(errnum);
+>   return strncpy(buf, str, buflen - 1);
+> }
+> #endif
+131a148,149
+> 
+> #if !defined(__MINGW32__) || HAVE_SECURE_STRING_EXTENSIONS == 0
+135a154,155
+> #endif
+> 
+140a161,173
+> 
+> 
+> // ----------------------------------- THREADS
+> 
+> #if !defined(__MINGW32__) || HAVE_WIN_PTHREAD == 0
+> typedef DWORD pthread_t;
+> typedef DWORD pthread_key_t;
+> typedef LONG pthread_once_t;
+> enum { PTHREAD_ONCE_INIT = 0 };   // important that this be 0! for SpinLock
+> #define pthread_self  GetCurrentThreadId
+> #define pthread_equal(pthread_t_1, pthread_t_2)  ((pthread_t_1)==(pthread_t_2))
+> #endif
+> 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/RetrieveCACertificates.py	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,70 @@
+#!/usr/bin/python
+
+# Orthanc - A Lightweight, RESTful DICOM Store
+# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, Belgium
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# In addition, as a special exception, the copyright holders of this
+# program give permission to link the code of its release with the
+# OpenSSL project's "OpenSSL" library (or with modified versions of it
+# that use the same license as the "OpenSSL" library), and distribute
+# the linked executables. You must obey the GNU General Public License
+# in all respects for all of the code used other than "OpenSSL". If you
+# modify file(s) with this exception, you may extend this exception to
+# your version of the file(s), but you are not obligated to do so. If
+# you do not wish to do so, delete this exception statement from your
+# version. If you delete this exception statement from all source files
+# in the program, then also delete it here.
+# 
+# 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+import re
+import sys
+import subprocess
+import urllib2
+
+
+if len(sys.argv) <= 2:
+    print('Download a set of CA certificates, convert them to PEM, then format them as a C macro')
+    print('Usage: %s [Macro] [Certificate1] <Certificate2>...' % sys.argv[0])
+    print('')
+    print('Example: %s BITBUCKET_CERTIFICATES https://www.digicert.com/CACerts/DigiCertHighAssuranceEVRootCA.crt' % sys.argv[0])
+    print('')
+    sys.exit(-1)
+
+MACRO = sys.argv[1]
+
+sys.stdout.write('#define %s ' % MACRO)
+
+for url in sys.argv[2:]:
+    # Download the certificate from the CA authority, in the DES format
+    des = urllib2.urlopen(url).read()
+
+    # Convert DES to PEM
+    p = subprocess.Popen([ 'openssl', 'x509', '-inform', 'DES', '-outform', 'PEM' ],
+                         stdin = subprocess.PIPE,
+                         stdout = subprocess.PIPE)
+    pem = p.communicate(input = des)[0]
+    pem = re.sub(r'\r', '', pem)       # Remove any carriage return
+    pem = re.sub(r'\\', r'\\\\', pem)  # Escape any backslash
+    pem = re.sub(r'"', r'\\"', pem)    # Escape any quote
+
+    # Write the PEM data into the macro
+    for line in pem.split('\n'):
+        sys.stdout.write(' \\\n')
+        sys.stdout.write('"%s\\n" ' % line)
+
+sys.stdout.write('\n')
+sys.stderr.write('Done!\n')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Samples/Lua/AutomatedJpeg2kCompression.lua	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,38 @@
+-- This sample shows how to use Orthanc to compress on-the-fly any
+-- incoming DICOM file, as a JPEG2k file.
+
+function OnStoredInstance(instanceId, tags, metadata, origin)
+   -- Do not compress twice the same file
+   if origin['RequestOrigin'] ~= 'Lua' then
+
+      -- Retrieve the incoming DICOM instance from Orthanc
+      local dicom = RestApiGet('/instances/' .. instanceId .. '/file')
+
+      -- Write the DICOM content to some temporary file
+      local uncompressed = instanceId .. '-uncompressed.dcm'
+      local target = assert(io.open(uncompressed, 'wb'))
+      target:write(dicom)
+      target:close()
+
+      -- Compress to JPEG2000 using gdcm
+      local compressed = instanceId .. '-compressed.dcm'
+      os.execute('gdcmconv --j2k ' .. uncompressed .. ' ' .. compressed)
+
+      -- Generate a new SOPInstanceUID for the JPEG2000 file, as
+      -- gdcmconv does not do this by itself
+      os.execute('dcmodify --no-backup -gin ' .. compressed)
+
+      -- Read the JPEG2000 file
+      local source = assert(io.open(compressed, 'rb'))
+      local jpeg2k = source:read("*all")
+      source:close()
+
+      -- Upload the JPEG2000 file and remove the uncompressed file
+      RestApiPost('/instances', jpeg2k)
+      RestApiDelete('/instances/' .. instanceId)
+
+      -- Remove the temporary DICOM files
+      os.remove(uncompressed)
+      os.remove(compressed)
+   end
+end
--- a/Resources/Samples/Lua/AutoroutingConditional.lua	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/Samples/Lua/AutoroutingConditional.lua	Wed Sep 30 13:23:31 2015 +0200
@@ -1,9 +1,6 @@
-function OnStoredInstance(instanceId, tags, metadata, remoteAet, calledAet)
-   -- The "remoteAet" and "calledAet" arguments are only available
-   -- since Orthanc 0.8.6
-   if remoteAet ~=nil and calledAet ~= nil then
-      print ("Source AET: " .. remoteAet .. " => Called AET: " .. calledAet)
-   end
+function OnStoredInstance(instanceId, tags, metadata, origin)
+   -- The "origin" is only available since Orthanc 0.9.4
+   PrintRecursive(origin)
 
    -- Extract the value of the "PatientName" DICOM tag
    local patientName = string.lower(tags['PatientName'])
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Samples/Lua/CallImageJ.lua	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,35 @@
+-- This sample shows how to invoke an ImageJ script on every DICOM
+-- image received by Orthanc. The ImageJ script is generated by the
+-- "Initialize()" function at the startup of Orthanc. Whenever a new
+-- instance is received, its DICOM file is stored into a temporary
+-- file, and a system call to ImageJ is triggered.
+
+SCRIPT = 'ImageJScript.txt'
+
+function Initialize()
+   local target = assert(io.open(SCRIPT, 'w'))
+
+   -- This is a sample ImageJ script that display the size of the DICOM image
+   target:write('if (getArgument=="") exit ("No argument!");\n')
+   target:write('open(getArgument);\n')
+   target:write('print(getTitle + ": " + getWidth + "x" + getHeight);\n')
+
+   target:close()
+end
+
+function OnStoredInstance(instanceId)
+   -- Retrieve the DICOM instance from Orthanc
+   local dicom = RestApiGet('/instances/' .. instanceId .. '/file')
+
+   -- Write the DICOM content to some temporary file
+   local path = instanceId .. '.dcm'
+   local target = assert(io.open(path, 'wb'))
+   target:write(dicom)
+   target:close()
+
+   -- Call ImageJ
+   os.execute('imagej -b ' .. SCRIPT .. ' ' .. path)
+
+   -- Remove the temporary DICOM file
+   os.remove(path)
+end
--- a/Resources/Samples/Lua/CallWebService.js	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/Samples/Lua/CallWebService.js	Wed Sep 30 13:23:31 2015 +0200
@@ -3,25 +3,18 @@
  * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
  * Department, University Hospital of Liege, Belgium
  *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU 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
+ * General Public License for more details.
  *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Samples/Lua/OnStableStudy.lua	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,46 @@
+function Initialize()
+   print('Number of stored studies at initialization: ' ..
+            table.getn(ParseJson(RestApiGet('/studies'))))
+end
+
+
+function Finalize()
+   print('Number of stored studies at finalization: ' ..
+            table.getn(ParseJson(RestApiGet('/studies'))))
+end
+
+
+function OnStoredInstance(instanceId, tags, metadata)
+   patient = ParseJson(RestApiGet('/instances/' .. instanceId .. '/patient'))
+   print('Received an instance for patient: ' .. 
+            patient['MainDicomTags']['PatientID'] .. ' - ' .. 
+            patient['MainDicomTags']['PatientName'])
+end
+
+
+function OnStableStudy(studyId, tags, metadata)
+   if (metadata['ModifiedFrom'] == nil and
+       metadata['AnonymizedFrom'] == nil) then
+
+      print('This study is now stable: ' .. studyId)
+      
+      -- The tags to be replaced
+      local replace = {}
+      replace['StudyDescription'] = 'Modified study'
+      replace['StationName'] = 'My Medical Device'
+      replace['0031-1020'] = 'Some private tag'
+
+      -- The tags to be removed
+      local remove = { 'MilitaryRank' }
+
+      -- The modification command
+      local command = {}
+      command['Remove'] = remove
+      command['Replace'] = replace
+
+      -- Modify the entire study in one single call
+      local m = RestApiPost('/studies/' .. studyId .. '/modify',
+                            DumpJson(command))
+      print('Modified study: ' .. m)
+   end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Samples/Lua/ParseDoseReport.lua	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,66 @@
+-- Sample Lua script that demonstrates how to extract DICOM tags
+-- related to dose reports. In this example, the value of the DLP
+-- (Dose Length Product), the value of the mean CTDI volume (Computed
+-- Tomography Dose Index), and the number of "IntervalsRejected" are
+-- extracted and printed. Furthermore, these values are saved as
+-- metadata that is attached to their parent DICOM instance for
+-- further processing by external software.
+
+
+function ExploreContentSequence(instanceId, tags)
+   if tags then
+      for key, value in pairs(tags) do
+         -- Recursive exploration
+         ExploreContentSequence(instanceId, value['ContentSequence'])
+
+         local concept = value['ConceptNameCodeSequence'] 
+         local measure = value['MeasuredValueSequence']
+         if concept and measure then
+
+            local value = measure[1]['NumericValue']
+            local code = concept[1]['CodeValue']
+
+            if type(value) == 'string' and type(code) == 'string' then
+               -- If the field contains the DLP, stores it as a metadata.
+               -- "DLP" is associated with CodeValue 113838.
+               -- ftp://medical.nema.org/medical/dicom/final/sup127_ft.pdf
+               if code == '113838' then
+                  print('DLP = ' .. value)
+                  RestApiPut('/instances/' .. instanceId .. '/metadata/2001', value)
+               end
+
+               -- Extract the mean CTDI volume
+               if code == '113830' then
+                  print('CTDI = ' .. value)
+                  RestApiPut('/instances/' .. instanceId .. '/metadata/2002', value)
+               end
+
+               -- Other values can be extracted here
+            end
+         end
+      end
+   end
+end
+
+
+function StoreTagToMetadata(instanceId, tags, name, metadata)
+   if tags then
+      for key, value in pairs(tags) do
+         if type(value) ~= 'string' then
+            -- Recursive exploration
+            StoreTagToMetadata(instanceId, value, name, metadata)
+         elseif key == name then
+            print(name .. ' = ' .. value)
+            if metadata then
+               RestApiPut('/instances/' .. instanceId .. '/metadata/' .. metadata, value)
+            end
+         end
+      end
+   end
+end
+
+
+function OnStoredInstance(instanceId, tags)
+   StoreTagToMetadata(instanceId, tags, 'IntervalsRejected', 2000)
+   ExploreContentSequence(instanceId, tags['ContentSequence'])
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Samples/Lua/WriteToDisk.lua	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,34 @@
+TARGET = '/tmp/lua'
+
+function ToAscii(s)
+   -- http://www.lua.org/manual/5.1/manual.html#pdf-string.gsub
+   return s:gsub('[^a-zA-Z0-9-/ ]', '_')
+end
+
+function OnStableSeries(seriesId, tags, metadata)
+   print('This series is now stable, writing its instances on the disk: ' .. seriesId)
+
+   local instances = ParseJson(RestApiGet('/series/' .. seriesId)) ['Instances']
+   local patient = ParseJson(RestApiGet('/series/' .. seriesId .. '/patient')) ['MainDicomTags']
+   local study = ParseJson(RestApiGet('/series/' .. seriesId .. '/study')) ['MainDicomTags']
+   local series = ParseJson(RestApiGet('/series/' .. seriesId)) ['MainDicomTags']
+
+   for i, instance in pairs(instances) do
+      local path = ToAscii(TARGET .. '/' .. 
+                              patient['PatientID'] .. ' - ' .. patient['PatientName'] .. '/' ..
+                              study['StudyDate'] .. ' - ' .. study['StudyDescription'] .. '/' ..
+                              series['SeriesDescription'])
+
+      -- Retrieve the DICOM file from Orthanc
+      local dicom = RestApiGet('/instances/' .. instance .. '/file')
+
+      -- Create the subdirectory (CAUTION: For Linux demo only, this is insecure!)
+      -- http://stackoverflow.com/a/16029744/881731
+      os.execute('mkdir -p "' .. path .. '"')
+
+      -- Write to the file
+      local target = assert(io.open(path .. '/' .. instance .. '.dcm', 'wb'))
+      target:write(dicom)
+      target:close()
+   end
+end
--- a/Resources/Samples/OrthancClient/Basic/CMakeLists.txt	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-cmake_minimum_required(VERSION 2.8)
-
-project(Basic)
-
-add_executable(Test main.cpp)
-
-if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
-  # Linking with "pthread" is necessary, otherwise the software crashes
-  # http://sourceware.org/bugzilla/show_bug.cgi?id=10652#c17
-  target_link_libraries(Test pthread dl)
-endif()
--- a/Resources/Samples/OrthancClient/Basic/main.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- **/
-
-
-#include <iostream>
-#include <orthanc/OrthancCppClient.h>
-
-int main()
-{
-  try
-  {
-    // The following explicit initialization is not required, except
-    // if you wish to specify the full path to the shared library
-    OrthancClient::Initialize();
-
-    // Display the content of the local Orthanc instance
-    OrthancClient::OrthancConnection orthanc("http://localhost:8042");
-
-    for (unsigned int i = 0; i < orthanc.GetPatientCount(); i++)
-    {
-      OrthancClient::Patient patient(orthanc.GetPatient(i));
-      std::cout << "Patient: " << patient.GetId() << std::endl;
-
-      for (unsigned int j = 0; j < patient.GetStudyCount(); j++)
-      {
-        OrthancClient::Study study(patient.GetStudy(j));
-        std::cout << "  Study: " << study.GetId() << std::endl;
-
-        for (unsigned int k = 0; k < study.GetSeriesCount(); k++)
-        {
-          OrthancClient::Series series(study.GetSeries(k));
-          std::cout << "    Series: " << series.GetId() << std::endl;
-
-          if (series.Is3DImage())
-          {
-            std::cout << "    This is a 3D image whose voxel size is " 
-                      << series.GetVoxelSizeX() << " x " 
-                      << series.GetVoxelSizeY() << " x " 
-                      << series.GetVoxelSizeZ() << ", and slice thickness is " 
-                      << series.GetSliceThickness() << std::endl;
-          }
-
-          for (unsigned int l = 0; l < series.GetInstanceCount(); l++)
-          {
-            std::cout << "      Instance: " << series.GetInstance(l).GetId() << std::endl;
-
-            // Load and display some raw DICOM tag
-            series.GetInstance(l).LoadTagContent("0020-000d");
-            std::cout << "        SOP instance UID: " << series.GetInstance(l).GetLoadedTagContent() << std::endl;
-          }
-        }
-      }
-    }
-
-    OrthancClient::Finalize();
-
-    return 0;
-  }
-  catch (OrthancClient::OrthancClientException& e)
-  {
-    std::cerr << "EXCEPTION: [" << e.What() << "]" << std::endl;
-    return -1;
-  }
-}
--- a/Resources/Samples/OrthancClient/Vtk/CMakeLists.txt	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-cmake_minimum_required(VERSION 2.8)
-
-project(Vtk)
-
-find_package(VTK REQUIRED)
-include(${VTK_USE_FILE})
-
-add_executable(Test
-  main.cpp
-  )
-
-# Linking with "pthread" is necessary, otherwise the software crashes
-# http://sourceware.org/bugzilla/show_bug.cgi?id=10652#c17
-target_link_libraries(Test pthread dl)
-
-if(VTK_LIBRARIES)
-  target_link_libraries(Test ${VTK_LIBRARIES})
-else()
-  target_link_libraries(Test vtkHybrid vtkVolumeRendering)
-endif()
--- a/Resources/Samples/OrthancClient/Vtk/main.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,181 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- **/
-
-
-#include <iostream>
-
-#include <vtkRenderWindow.h>
-#include <vtkImageData.h>
-#include <vtkPiecewiseFunction.h>
-#include <vtkFixedPointVolumeRayCastMapper.h>
-#include <vtkColorTransferFunction.h>
-#include <vtkVolumeProperty.h>
-#include <vtkRenderWindowInteractor.h>
-#include <vtkRenderer.h>
-#include <vtkSmartPointer.h>
-#include <vtkOpenGLRenderer.h>
-#include <vtkInteractorStyleTrackballCamera.h>
-
-#include <orthanc/OrthancCppClient.h>
-
-
-void Display(OrthancClient::Series& series)
-{
-  /**
-   * Load the 3D image from Orthanc into VTK.
-   **/
-
-  vtkSmartPointer<vtkImageData> image = vtkSmartPointer<vtkImageData>::New();
-  image->SetDimensions(series.GetWidth(), series.GetHeight(), series.GetInstanceCount());
-  image->SetScalarType(VTK_SHORT);
-  image->AllocateScalars();
-
-  if (series.GetWidth() != 0 &&
-      series.GetHeight() != 0 && 
-      series.GetInstanceCount() != 0)
-  {
-    series.Load3DImage(image->GetScalarPointer(0, 0, 0), Orthanc::PixelFormat_SignedGrayscale16,
-                       2 * series.GetWidth(), 2 * series.GetHeight() * series.GetWidth());
-  }
-
-  image->SetSpacing(series.GetVoxelSizeX(), 
-                    series.GetVoxelSizeY(), 
-                    series.GetVoxelSizeZ());
-
-
-  /**
-   * The following code is based on the VTK sample for MIP
-   * http://www.vtk.org/Wiki/VTK/Examples/Cxx/VolumeRendering/MinIntensityRendering
-   **/
-
-  // Create a transfer function mapping scalar value to opacity
-  double range[2];
-  image->GetScalarRange(range);
-
-  vtkSmartPointer<vtkPiecewiseFunction> opacityTransfer = 
-    vtkSmartPointer<vtkPiecewiseFunction>::New();
-  opacityTransfer->AddSegment(range[0], 0.0, range[1], 1.0);
- 
-  vtkSmartPointer<vtkColorTransferFunction> colorTransfer = 
-    vtkSmartPointer<vtkColorTransferFunction>::New();
-  colorTransfer->AddRGBPoint(0, 1.0, 1.0, 1.0);
-  colorTransfer->AddRGBPoint(range[1], 1.0, 1.0, 1.0);
- 
-  vtkSmartPointer<vtkVolumeProperty> property = 
-    vtkSmartPointer<vtkVolumeProperty>::New();
-  property->SetScalarOpacity(opacityTransfer);
-  property->SetColor(colorTransfer);
-  property->SetInterpolationTypeToLinear();
-
-  // Create a Maximum Intensity Projection rendering
-  vtkSmartPointer<vtkFixedPointVolumeRayCastMapper> mapper = 
-    vtkSmartPointer<vtkFixedPointVolumeRayCastMapper>::New();
-  mapper->SetBlendModeToMaximumIntensity();
-  mapper->SetInput(image);
-
-  vtkSmartPointer<vtkVolume> volume = vtkSmartPointer<vtkVolume>::New();
-  volume->SetMapper(mapper);
-  volume->SetProperty(property);
-  
-  vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkOpenGLRenderer>::New();
-  renderer->AddViewProp(volume);
-  renderer->SetBackground(0.1, 0.2, 0.3); // Background color dark blue
-
-  vtkSmartPointer<vtkInteractorStyleTrackballCamera> style = 
-    vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
- 
-  vtkSmartPointer<vtkRenderWindow> window = vtkSmartPointer<vtkRenderWindow>::New();
-  window->AddRenderer(renderer); 
-
-  vtkSmartPointer<vtkRenderWindowInteractor> interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
-  interactor->SetRenderWindow(window);
-  interactor->SetInteractorStyle(style);
-  interactor->Start();
-}
-
-
-int main()
-{
-  try
-  {
-    // The following explicit initialization is not required, except
-    // if you wish to specify the full path to the shared library
-    OrthancClient::Initialize();
-
-    // Use the commented code below if you know the identifier of a
-    // series that corresponds to a 3D image.
-
-    /*
-      {
-      OrthancClient::OrthancConnection orthanc("http://localhost:8042");
-      OrthancClient::Series series(orthanc, "dc5ec3d9-6e1a7b2c-73a829f0-64c609f6-ef976a97");
-      Display(series);
-      return 0;
-      }
-    */
-
-
-    // Try and find a 3D image inside the local store
-    OrthancClient::OrthancConnection orthanc("http://localhost:8042");
-
-    for (unsigned int i = 0; i < orthanc.GetPatientCount(); i++)
-    {
-      OrthancClient::Patient patient(orthanc.GetPatient(i));
-      std::cout << "Patient: " << patient.GetId() << std::endl;
-
-      for (unsigned int j = 0; j < patient.GetStudyCount(); j++)
-      {
-        OrthancClient::Study study(patient.GetStudy(j));
-        std::cout << "  Study: " << study.GetId() << std::endl;
-
-        for (unsigned int k = 0; k < study.GetSeriesCount(); k++)
-        {
-          OrthancClient::Series series(study.GetSeries(k));
-          std::cout << "    Series: " << series.GetId() << std::endl;
-
-          if (series.Is3DImage())
-          {
-            Display(series);
-            return 0;
-          }
-          else
-          {
-            std::cout << "      => Not a 3D image..." << std::endl;
-          }
-        }
-      }
-    }
-
-    std::cout << "Unable to find a 3D image in the local Orthanc store" << std::endl;
-
-    return 0;
-  }
-  catch (OrthancClient::OrthancClientException& e)
-  {
-    std::cerr << "EXCEPTION: [" << e.What() << "]" << std::endl;
-    return -1;
-  }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Samples/Python/ArchiveStudiesInTimeRange.py	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,78 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Orthanc - A Lightweight, RESTful DICOM Store
+# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, Belgium
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+
+import os
+import os.path
+import sys
+import RestToolbox
+
+def PrintHelp():
+    print('Download ZIP archives for all the studies generated '
+          'during a given time range (according to the StudyDate tag)\n')
+    print('Usage: %s <URL> <StartDate> <EndDate> <TargetFolder>\n' % sys.argv[0])
+    print('Example: %s http://localhost:8042/ 20150101 20151231 /tmp/\n' % sys.argv[0])
+    exit(-1)
+
+def CheckIsDate(date):
+    if len(date) != 8 or not date.isdigit():
+        print '"%s" is not a valid date!\n' % date
+        exit(-1)
+
+
+if len(sys.argv) != 5:
+    PrintHelp()
+
+URL = sys.argv[1]
+START = sys.argv[2]
+END = sys.argv[3]
+TARGET = sys.argv[4]
+
+CheckIsDate(START)
+CheckIsDate(END)
+
+# Loop over the studies
+for studyId in RestToolbox.DoGet('%s/studies' % URL):
+    # Retrieve the DICOM tags of the current study
+    study = RestToolbox.DoGet('%s/studies/%s' % (URL, studyId))['MainDicomTags']
+
+    # Retrieve the DICOM tags of the parent patient of this study
+    patient = RestToolbox.DoGet('%s/studies/%s/patient' % (URL, studyId))['MainDicomTags']
+
+    # Check that the StudyDate tag lies within the given range
+    studyDate = study['StudyDate'][:8]
+    if studyDate >= START and studyDate <= END:
+        # Create a filename
+        filename = '%s - %s %s - %s.zip' % (study['StudyDate'],
+                                            patient['PatientID'],
+                                            patient['PatientName'],
+                                            study['StudyDescription'])
+
+        # Remove any non-ASCII character in the filename
+        filename = filename.encode('ascii', errors = 'replace')
+
+        # Download the ZIP archive of the study
+        print('Downloading %s' % filename)
+        zipContent = RestToolbox.DoGet('%s/studies/%s/archive' % (URL, studyId))
+
+        # Write the ZIP archive at the proper location
+        with open(os.path.join(TARGET, filename), 'wb') as f:
+            f.write(zipContent)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Samples/Python/ContinuousPatientAnonymization.py	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,99 @@
+#!/usr/bin/python
+
+# Orthanc - A Lightweight, RESTful DICOM Store
+# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, Belgium
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+
+import time
+import sys
+import RestToolbox
+import md5
+
+
+##
+## Print help message
+##
+
+if len(sys.argv) != 3:
+    print("""
+Sample script that anonymizes patients in real-time. A patient gets
+anonymized as soon as she gets stable (i.e. when no DICOM instance has
+been received for this patient for a sufficient amount of time - cf.
+the configuration option "StableAge").
+
+Usage: %s [hostname] [HTTP port]
+For instance: %s localhost 8042
+""" % (sys.argv[0], sys.argv[0]))
+    exit(-1)
+
+URL = 'http://%s:%d' % (sys.argv[1], int(sys.argv[2]))
+
+
+
+##
+## The following function is called whenever a patient gets stable
+##
+
+COUNT = 1
+
+def AnonymizePatient(path):
+    global URL
+    global COUNT
+
+    patient = RestToolbox.DoGet(URL + path)
+    patientID = patient['MainDicomTags']['PatientID']
+
+    # Ignore anonymized patients
+    if not 'AnonymizedFrom' in patient:
+        print('Patient with ID "%s" is stabilized: anonymizing it...' % (patientID))
+        
+        # The PatientID after anonymization is taken as the 8 first
+        # characters from the MD5 hash of the original PatientID
+        anonymizedID = md5.new(patientID).hexdigest()[:8]
+        anonymizedName = 'Anonymized patient %d' % COUNT
+        COUNT += 1
+
+        RestToolbox.DoPost(URL + path + '/anonymize',
+                           { 'Replace' : { 'PatientID' : anonymizedID,
+                                           'PatientName' : anonymizedName } })
+
+        # Delete the source patient after the anonymization
+        RestToolbox.DoDelete(URL + change['Path'])
+
+
+
+##
+## Main loop that listens to the changes API.
+## 
+
+current = 0
+while True:
+    r = RestToolbox.DoGet(URL + '/changes', {
+            'since' : current,
+            'limit' : 4   # Retrieve at most 4 changes at once
+            })
+
+    for change in r['Changes']:
+        if change['ChangeType'] == 'StablePatient':
+            AnonymizePatient(change['Path'])
+
+    current = r['Last']
+
+    if r['Done']:
+        print('Everything has been processed: Waiting...')
+        time.sleep(1)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Samples/Python/Replicate.py	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,109 @@
+#!/usr/bin/python
+
+# Orthanc - A Lightweight, RESTful DICOM Store
+# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, Belgium
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+import base64
+import httplib2
+import json
+import re
+import sys
+
+URL_REGEX = re.compile('(http|https)://((.+?):(.+?)@|)(.*)')
+
+
+if len(sys.argv) != 3:
+    print("""
+Script to copy the content of one Orthanc server to another Orthanc
+server through their REST API.
+
+Usage: %s [SourceURI] [TargetURI]
+For instance: %s http://orthanc:password@localhost:8042/ http://localhost:8043/
+""" % (sys.argv[0], sys.argv[0]))
+    exit(-1)
+
+
+
+def CreateHeaders(parsedUrl):
+    headers = { }
+    username = parsedUrl.group(3)
+    password = parsedUrl.group(4)
+
+    if username != None and password != None:
+        # This is a custom reimplementation of the
+        # "Http.add_credentials()" method for Basic HTTP Access
+        # Authentication (for some weird reason, this method does not
+        # always work)
+        # http://en.wikipedia.org/wiki/Basic_access_authentication
+        headers['authorization'] = 'Basic ' + base64.b64encode(username + ':' + password)
+
+    return headers
+
+
+def GetBaseUrl(parsedUrl):
+    return '%s://%s' % (parsedUrl.group(1), parsedUrl.group(5))
+
+
+def DoGetString(url):
+    global URL_REGEX
+    parsedUrl = URL_REGEX.match(url)
+    headers = CreateHeaders(parsedUrl)
+
+    h = httplib2.Http()
+    resp, content = h.request(GetBaseUrl(parsedUrl), 'GET', headers = headers)
+
+    if resp.status == 200:
+        return content
+    else:
+        raise Exception('Unable to contact Orthanc at: ' + url)
+    
+
+def DoPostDicom(url, body):
+    global URL_REGEX
+    parsedUrl = URL_REGEX.match(url)
+    headers = CreateHeaders(parsedUrl)
+    headers['content-type'] = 'application/dicom'
+
+    h = httplib2.Http()
+    resp, content = h.request(GetBaseUrl(parsedUrl), 'POST',
+                              body = body,
+                              headers = headers)
+
+    if resp.status != 200:
+        raise Exception('Unable to contact Orthanc at: ' + url)
+    
+
+def _DecodeJson(s):
+    if (sys.version_info >= (3, 0)):
+        return json.loads(s.decode())
+    else:
+        return json.loads(s)
+
+
+def DoGetJson(url):
+    return _DecodeJson(DoGetString(url))
+
+
+SOURCE = sys.argv[1]
+TARGET = sys.argv[2]
+
+for study in DoGetJson('%s/studies' % SOURCE):
+    print('Sending study %s...' % study)
+    for instance in DoGetJson('%s/studies/%s/instances' % (SOURCE, study)):
+        dicom = DoGetString('%s/instances/%s/file' % (SOURCE, instance['ID']))
+        DoPostDicom('%s/instances' % TARGET, dicom)
--- a/Resources/Samples/Tools/CMakeLists.txt	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/Samples/Tools/CMakeLists.txt	Wed Sep 30 13:23:31 2015 +0200
@@ -20,6 +20,7 @@
 include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake)
 include(${ORTHANC_ROOT}/Resources/CMake/BoostConfiguration.cmake)
 include(${ORTHANC_ROOT}/Resources/CMake/ZlibConfiguration.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/GoogleLogConfiguration.cmake)
 
 add_library(CommonLibraries
   ${BOOST_SOURCES}
@@ -27,8 +28,8 @@
   ${ORTHANC_ROOT}/Core/OrthancException.cpp
   ${ORTHANC_ROOT}/Core/Toolbox.cpp
   ${ORTHANC_ROOT}/Core/Uuid.cpp
-  ${ORTHANC_ROOT}/Resources/md5/md5.c
-  ${ORTHANC_ROOT}/Resources/base64/base64.cpp
+  ${ORTHANC_ROOT}/Resources/ThirdParty/md5/md5.c
+  ${ORTHANC_ROOT}/Resources/ThirdParty/base64/base64.cpp
   )
 
 add_executable(RecoverCompressedFile
@@ -37,4 +38,4 @@
   ${ORTHANC_ROOT}/Core/Compression/ZlibCompressor.cpp
   )
 
-target_link_libraries(RecoverCompressedFile CommonLibraries)
+target_link_libraries(RecoverCompressedFile CommonLibraries GoogleLog)
--- a/Resources/Samples/WebApplications/DrawingDicomizer.js	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/Samples/WebApplications/DrawingDicomizer.js	Wed Sep 30 13:23:31 2015 +0200
@@ -3,25 +3,18 @@
  * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
  * Department, University Hospital of Liege, Belgium
  *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU 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
+ * General Public License for more details.
  *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
 
 
--- a/Resources/Samples/WebApplications/DrawingDicomizer/orthanc.js	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/Samples/WebApplications/DrawingDicomizer/orthanc.js	Wed Sep 30 13:23:31 2015 +0200
@@ -3,27 +3,21 @@
  * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
  * Department, University Hospital of Liege, Belgium
  *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU 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
+ * General Public License for more details.
  *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
 
+
 function guid4Block() {
   return Math.floor((1 + Math.random()) * 0x10000)
     .toString(16)
--- a/Resources/Samples/WebApplications/NodeToolbox.js	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/Samples/WebApplications/NodeToolbox.js	Wed Sep 30 13:23:31 2015 +0200
@@ -3,25 +3,18 @@
  * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
  * Department, University Hospital of Liege, Belgium
  *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU 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
+ * General Public License for more details.
  *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/ThirdParty/patch/NOTES.txt	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,70 @@
+===========
+INFORMATION
+===========
+
+This is a precompiled version of the "patch" standard tool for
+Windows. It was compiled using the MSYS framework.
+
+The binaries originate from the "Git for Windows 1.9.5" package
+(https://msysgit.github.io/). The build instructions have been
+provided on the discussion group of Git for Windows [1]. They are
+copied/pasted below for reference.
+
+
+
+================
+UPSTREAM PROJECT
+================
+
+URL to the upstream project:
+http://savannah.gnu.org/projects/patch/
+
+License of patch: GPLv2 (GNU General Public License v2)
+
+Copyright (C) 1988 Larry Wall "with lots o' patches by Paul Eggert"
+Copyright (C) 1997 Free Software Foundation, Inc.
+
+
+
+======================
+BUILD INSTRUCTIONS [1]
+======================
+
+The easiest way to find out about this is to install the Git SDK, then 
+run 
+
+     pacman -Qu $(which patch.exe) 
+
+to find out which package contains the `patch.exe` binary. It so happens 
+to be patch.2.7.5-1 at the moment. Since this is an MSys2 package (not a 
+MinGW one, otherwise the patch utility would be in /mingw64/bin/, not 
+/usr/bin/), this package is built from the recipes in 
+
+     https://github.com/msys2/MSYS2-packages 
+
+The `patch` package is obviously built from the subdirectory 
+
+     https://github.com/Alexpux/MSYS2-packages/tree/master/patch 
+
+and the PKGBUILD file specifies that the source is fetched from 
+ftp://ftp.gnu.org/gnu/patch/patch-2.7.5.tar.xz: 
+
+https://github.com/Alexpux/MSYS2-packages/blob/900744becd072f687029b0f830ab6fe95cf533d6/patch/PKGBUILD#L14 
+
+and then these two patches are applied before building: 
+     
+https://github.com/Alexpux/MSYS2-packages/blob/900744becd072f687029b0f830ab6fe95cf533d6/patch/msys2-patch-2.7.1.patch 
+
+and 
+      
+https://github.com/Alexpux/MSYS2-packages/blob/900744becd072f687029b0f830ab6fe95cf533d6/patch/msys2-patch-manifest.patch 
+
+As you can see, some light changes are applied, i.e. `patch.exe` will 
+always write in binary mode with MSys2, and the executable will have a 
+manifest embedded that allows it to run as non-administrator. 
+
+Ciao, 
+Johannes Schindelin
+
+
+[1] https://groups.google.com/d/msg/git-for-windows/xWyVr4z6Ri0/6RKeV028EAAJ
Binary file Resources/ThirdParty/patch/msys-1.0.dll has changed
Binary file Resources/ThirdParty/patch/patch.exe has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/ThirdParty/patch/patch.exe.manifest	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <assemblyIdentity version="7.95.0.0"
+     processorArchitecture="X86"
+     name="patch.exe"
+     type="win32"/>
+
+  <!-- Identify the application security requirements. -->
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+    <security>
+      <requestedPrivileges>
+        <requestedExecutionLevel
+          level="asInvoker"
+          uiAccess="false"/>
+        </requestedPrivileges>
+       </security>
+  </trustInfo>
+</assembly>
+
--- a/Resources/Toolbox.lua	Wed Feb 11 10:40:08 2015 +0100
+++ b/Resources/Toolbox.lua	Wed Sep 30 13:23:31 2015 +0200
@@ -32,80 +32,92 @@
 end
 
 
-function SendToModality(instanceId, modality)
-   if instanceId == nil then
-      error('Cannot send a nonexistent instance')
+function SendToModality(resourceId, modality, localAet)
+   if resourceId == nil then
+      error('Cannot send a nonexistent resource')
    end
 
    table.insert(_job, { 
                    Operation = 'store-scu', 
-                   Instance = instanceId,
-                   Modality = modality 
+                   Resource = resourceId,
+                   Modality = modality,
+                   LocalAet = localAet
                 })
-   return instanceId
+   return resourceId
 end
 
 
-function SendToPeer(instanceId, peer)
-   if instanceId == nil then
-      error('Cannot send a nonexistent instance')
+function SendToPeer(resourceId, peer)
+   if resourceId == nil then
+      error('Cannot send a nonexistent resource')
    end
 
    table.insert(_job, { 
                    Operation = 'store-peer', 
-                   Instance = instanceId,
+                   Resource = resourceId,
                    Peer = peer
                 })
-   return instanceId
+   return resourceId
 end
 
 
-function Delete(instanceId)
-   if instanceId == nil then
-      error('Cannot delete a nonexistent instance')
+function Delete(resourceId)
+   if resourceId == nil then
+      error('Cannot delete a nonexistent resource')
    end
 
    table.insert(_job, { 
                    Operation = 'delete', 
-                   Instance = instanceId
+                   Resource = resourceId
                 })
    return nil  -- Forbid chaining
 end
 
 
-function ModifyInstance(instanceId, replacements, removals, removePrivateTags)
-   if instanceId == nil then
-      error('Cannot modify a nonexistent instance')
+function ModifyResource(resourceId, replacements, removals, removePrivateTags)
+   if resourceId == nil then
+      error('Cannot modify a nonexistent resource')
    end
 
-   if instanceId == '' then
-      error('Cannot modify twice an instance');
+   if resourceId == '' then
+      error('Cannot modify twice an resource');
    end
 
    table.insert(_job, { 
                    Operation = 'modify', 
-                   Instance = instanceId,
+                   Resource = resourceId,
                    Replace = replacements, 
                    Remove = removals,
                    RemovePrivateTags = removePrivateTags 
                 })
+
    return ''  -- Chain with another operation
 end
 
 
-function CallSystem(instanceId, command, args)
-   if instanceId == nil then
-      error('Cannot modify a nonexistent instance')
+function ModifyInstance(resourceId, replacements, removals, removePrivateTags)
+   return ModifyResource(resourceId, replacements, removals, removePrivateTags)
+end
+
+
+-- This function is only applicable to individual instances
+function CallSystem(resourceId, command, args)
+   if resourceId == nil then
+      error('Cannot execute a system call on a nonexistent resource')
+   end
+
+   if command == nil then
+      error('No command was specified for system call')
    end
 
    table.insert(_job, { 
                    Operation = 'call-system', 
-                   Instance = instanceId,
+                   Resource = resourceId,
                    Command = command,
                    Arguments = args
                 })
 
-   return instanceId
+   return resourceId
 end
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/WindowsResources.py	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,89 @@
+#!/usr/bin/python
+
+# Orthanc - A Lightweight, RESTful DICOM Store
+# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, Belgium
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# In addition, as a special exception, the copyright holders of this
+# program give permission to link the code of its release with the
+# OpenSSL project's "OpenSSL" library (or with modified versions of it
+# that use the same license as the "OpenSSL" library), and distribute
+# the linked executables. You must obey the GNU General Public License
+# in all respects for all of the code used other than "OpenSSL". If you
+# modify file(s) with this exception, you may extend this exception to
+# your version of the file(s), but you are not obligated to do so. If
+# you do not wish to do so, delete this exception statement from your
+# version. If you delete this exception statement from all source files
+# in the program, then also delete it here.
+# 
+# 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+import os
+import sys
+import datetime
+
+if len(sys.argv) != 5:
+    sys.stderr.write('Usage: %s <Version> <ProductName> <Filename> <Description>\n\n' % sys.argv[0])
+    sys.stderr.write('Example: %s 0.9.1 Orthanc Orthanc.exe "Lightweight, RESTful DICOM server for medical imaging"\n' % sys.argv[0])
+    sys.exit(-1)
+
+SOURCE = os.path.join(os.path.dirname(__file__), 'WindowsResources.rc')
+
+VERSION = sys.argv[1]
+PRODUCT = sys.argv[2]
+FILENAME = sys.argv[3]
+DESCRIPTION = sys.argv[4]
+
+if VERSION == 'mainline':
+    VERSION = '999.999.999'
+    RELEASE = 'This is a mainline build, not an official release'
+else:
+    RELEASE = 'Release %s' % VERSION
+
+v = VERSION.split('.')
+if len(v) != 2 and len(v) != 3:
+    sys.stderr.write('Bad version number: %s\n' % VERSION)
+    sys.exit(-1)
+
+if len(v) == 2:
+    v.append('0')
+
+extension = os.path.splitext(FILENAME)[1]
+if extension.lower() == '.dll':
+    BLOCK = '040904E4'
+    TYPE = 'VFT_DLL'
+elif extension.lower() == '.exe':
+    #BLOCK = '040904B0'   # LANG_ENGLISH/SUBLANG_ENGLISH_US,
+    BLOCK = '040904E4'   # Lang=US English, CharSet=Windows Multilingual
+    TYPE = 'VFT_APP'
+else:
+    sys.stderr.write('Unsupported extension (.EXE or .DLL only): %s\n' % extension)
+    sys.exit(-1)
+
+
+with open(SOURCE, 'r') as source:
+    content = source.read()
+    content = content.replace('${VERSION_MAJOR}', v[0])
+    content = content.replace('${VERSION_MINOR}', v[1])
+    content = content.replace('${VERSION_PATCH}', v[2])
+    content = content.replace('${RELEASE}', RELEASE)
+    content = content.replace('${DESCRIPTION}', DESCRIPTION)
+    content = content.replace('${PRODUCT}', PRODUCT)   
+    content = content.replace('${FILENAME}', FILENAME)   
+    content = content.replace('${YEAR}', str(datetime.datetime.now().year))
+    content = content.replace('${BLOCK}', BLOCK)
+    content = content.replace('${TYPE}', TYPE)
+
+    sys.stdout.write(content)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/WindowsResources.rc	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,30 @@
+#include <winver.h>
+
+VS_VERSION_INFO VERSIONINFO
+   FILEVERSION ${VERSION_MAJOR},${VERSION_MINOR},0,${VERSION_PATCH}
+   PRODUCTVERSION ${VERSION_MAJOR},${VERSION_MINOR},0,0
+   FILEOS VOS_NT_WINDOWS32
+   FILETYPE ${TYPE}
+   BEGIN
+      BLOCK "StringFileInfo"
+      BEGIN
+         BLOCK "${BLOCK}"
+         BEGIN
+            VALUE "Comments", "${RELEASE}"
+            VALUE "CompanyName", "University Hospital of Liege, Belgium"
+            VALUE "FileDescription", "${DESCRIPTION}"
+            VALUE "FileVersion", "${VERSION_MAJOR}.${VERSION_MINOR}.0.${VERSION_PATCH}"
+            VALUE "InternalName", "${PRODUCT}"
+            VALUE "LegalCopyright", "(c) 2012-${YEAR}, Sebastien Jodogne, University Hospital of Liege, Belgium"
+            VALUE "LegalTrademarks", "Licensing information is available at http://www.orthanc-server.com/"
+            VALUE "OriginalFilename", "${FILENAME}"
+            VALUE "ProductName", "${PRODUCT}"
+            VALUE "ProductVersion", "${VERSION_MAJOR}.${VERSION_MINOR}"
+         END
+      END
+
+      BLOCK "VarFileInfo"
+      BEGIN
+        VALUE "Translation", 0x409, 1252  // U.S. English
+      END
+   END
--- a/THANKS	Wed Feb 11 10:40:08 2015 +0100
+++ b/THANKS	Wed Sep 30 13:23:31 2015 +0200
@@ -28,6 +28,7 @@
 * Vincent Kersten <vincent1234567@gmail.com>, for DICOMDIR in the GUI.
 * Emsy Chan <emlscs@yahoo.com>, for various contributions
   and sample DICOM files.
+* Mikhail <mp39590@gmail.com>, for FreeBSD support.
 
 
 Thanks also to all the contributors active in our Google Group:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UnitTestsSources/BitbucketCACertificates.h	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,25 @@
+#define BITBUCKET_CERTIFICATES  \
+"-----BEGIN CERTIFICATE-----\n"  \
+"MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs\n"  \
+"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"  \
+"d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j\n"  \
+"ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL\n"  \
+"MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3\n"  \
+"LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug\n"  \
+"RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm\n"  \
+"+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW\n"  \
+"PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM\n"  \
+"xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB\n"  \
+"Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3\n"  \
+"hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg\n"  \
+"EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF\n"  \
+"MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA\n"  \
+"FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec\n"  \
+"nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z\n"  \
+"eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF\n"  \
+"hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2\n"  \
+"Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe\n"  \
+"vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep\n"  \
+"+OkuE6N36B9K\n"  \
+"-----END CERTIFICATE-----\n"  \
+"\n" 
--- a/UnitTestsSources/DicomMapTests.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/UnitTestsSources/DicomMapTests.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -83,22 +83,38 @@
 
 TEST(DicomMap, Tags)
 {
+  std::set<DicomTag> s;
+
   DicomMap m;
+  m.GetTags(s);
+  ASSERT_EQ(0u, s.size());
+
   ASSERT_FALSE(m.HasTag(DICOM_TAG_PATIENT_NAME));
   ASSERT_FALSE(m.HasTag(0x0010, 0x0010));
   m.SetValue(0x0010, 0x0010, "PatientName");
   ASSERT_TRUE(m.HasTag(DICOM_TAG_PATIENT_NAME));
   ASSERT_TRUE(m.HasTag(0x0010, 0x0010));
 
+  m.GetTags(s);
+  ASSERT_EQ(1u, s.size());
+  ASSERT_EQ(DICOM_TAG_PATIENT_NAME, *s.begin());
+
   ASSERT_FALSE(m.HasTag(DICOM_TAG_PATIENT_ID));
   m.SetValue(DICOM_TAG_PATIENT_ID, "PatientID");
   ASSERT_TRUE(m.HasTag(0x0010, 0x0020));
   m.SetValue(DICOM_TAG_PATIENT_ID, "PatientID2");
   ASSERT_EQ("PatientID2", m.GetValue(0x0010, 0x0020).AsString());
 
+  m.GetTags(s);
+  ASSERT_EQ(2u, s.size());
+
   m.Remove(DICOM_TAG_PATIENT_ID);
   ASSERT_THROW(m.GetValue(0x0010, 0x0020), OrthancException);
 
+  m.GetTags(s);
+  ASSERT_EQ(1u, s.size());
+  ASSERT_EQ(DICOM_TAG_PATIENT_NAME, *s.begin());
+
   std::auto_ptr<DicomMap> mm(m.Clone());
   ASSERT_EQ("PatientName", mm->GetValue(DICOM_TAG_PATIENT_NAME).AsString());  
 
@@ -137,7 +153,7 @@
                        DicomModule module)
 {
   std::set<DicomTag> moduleTags, main;
-  DicomTag::GetTagsForModule(moduleTags, module);
+  DicomTag::AddTagsForModule(moduleTags, module);
   DicomMap::GetMainDicomTags(main, level);
   
   // The main dicom tags are a subset of the module
--- a/UnitTestsSources/FileStorageTests.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/UnitTestsSources/FileStorageTests.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -34,17 +34,16 @@
 #include "gtest/gtest.h"
 
 #include <ctype.h>
-#include <glog/logging.h>
 
 #include "../Core/FileStorage/FilesystemStorage.h"
-#include "../OrthancServer/ServerIndex.h"
+#include "../Core/FileStorage/StorageAccessor.h"
+#include "../Core/HttpServer/BufferHttpSender.h"
+#include "../Core/HttpServer/FilesystemHttpSender.h"
+#include "../Core/Logging.h"
+#include "../Core/OrthancException.h"
 #include "../Core/Toolbox.h"
-#include "../Core/OrthancException.h"
 #include "../Core/Uuid.h"
-#include "../Core/HttpServer/FilesystemHttpSender.h"
-#include "../Core/HttpServer/BufferHttpSender.h"
-#include "../Core/FileStorage/FileStorageAccessor.h"
-#include "../Core/FileStorage/CompressedFileStorageAccessor.h"
+#include "../OrthancServer/ServerIndex.h"
 
 using namespace Orthanc;
 
@@ -123,111 +122,69 @@
 }
 
 
-TEST(FileStorageAccessor, Simple)
+TEST(StorageAccessor, NoCompression)
 {
   FilesystemStorage s("UnitTestsStorage");
-  FileStorageAccessor accessor(s);
+  StorageAccessor accessor(s);
 
   std::string data = "Hello world";
-  FileInfo info = accessor.Write(data, FileContentType_Dicom);
+  FileInfo info = accessor.Write(data, FileContentType_Dicom, CompressionType_None, true);
   
   std::string r;
-  accessor.Read(r, info.GetUuid(), FileContentType_Unknown);
-
-  ASSERT_EQ(data, r);
-  ASSERT_EQ(CompressionType_None, info.GetCompressionType());
-  ASSERT_EQ(11u, info.GetUncompressedSize());
-  ASSERT_EQ(11u, info.GetCompressedSize());
-  ASSERT_EQ(FileContentType_Dicom, info.GetContentType());
-}
-
-
-TEST(FileStorageAccessor, NoCompression)
-{
-  FilesystemStorage s("UnitTestsStorage");
-  CompressedFileStorageAccessor accessor(s);
-
-  accessor.SetCompressionForNextOperations(CompressionType_None);
-  std::string data = "Hello world";
-  FileInfo info = accessor.Write(data, FileContentType_Dicom);
-  
-  std::string r;
-  accessor.Read(r, info.GetUuid(), FileContentType_Unknown);
+  accessor.Read(r, info);
 
   ASSERT_EQ(data, r);
   ASSERT_EQ(CompressionType_None, info.GetCompressionType());
   ASSERT_EQ(11u, info.GetUncompressedSize());
   ASSERT_EQ(11u, info.GetCompressedSize());
   ASSERT_EQ(FileContentType_Dicom, info.GetContentType());
-}
-
-
-TEST(FileStorageAccessor, NoCompression2)
-{
-  FilesystemStorage s("UnitTestsStorage");
-  CompressedFileStorageAccessor accessor(s);
-
-  accessor.SetCompressionForNextOperations(CompressionType_None);
-  std::vector<uint8_t> data;
-  StringToVector(data, "Hello world");
-  FileInfo info = accessor.Write(data, FileContentType_Dicom);
-  
-  std::string r;
-  accessor.Read(r, info.GetUuid(), FileContentType_Unknown);
-
-  ASSERT_EQ(0, memcmp(&r[0], &data[0], data.size()));
-  ASSERT_EQ(CompressionType_None, info.GetCompressionType());
-  ASSERT_EQ(11u, info.GetUncompressedSize());
-  ASSERT_EQ(11u, info.GetCompressedSize());
-  ASSERT_EQ(FileContentType_Dicom, info.GetContentType());
+  ASSERT_EQ("3e25960a79dbc69b674cd4ec67a72c62", info.GetUncompressedMD5());
+  ASSERT_EQ(info.GetUncompressedMD5(), info.GetCompressedMD5());
 }
 
 
-TEST(FileStorageAccessor, Compression)
+TEST(StorageAccessor, Compression)
 {
   FilesystemStorage s("UnitTestsStorage");
-  CompressedFileStorageAccessor accessor(s);
+  StorageAccessor accessor(s);
 
-  accessor.SetCompressionForNextOperations(CompressionType_Zlib);
   std::string data = "Hello world";
-  FileInfo info = accessor.Write(data, FileContentType_Dicom);
+  FileInfo info = accessor.Write(data, FileContentType_DicomAsJson, CompressionType_ZlibWithSize, true);
   
   std::string r;
-  accessor.Read(r, info.GetUuid(), FileContentType_Unknown);
+  accessor.Read(r, info);
 
   ASSERT_EQ(data, r);
-  ASSERT_EQ(CompressionType_Zlib, info.GetCompressionType());
+  ASSERT_EQ(CompressionType_ZlibWithSize, info.GetCompressionType());
   ASSERT_EQ(11u, info.GetUncompressedSize());
-  ASSERT_EQ(FileContentType_Dicom, info.GetContentType());
+  ASSERT_EQ(FileContentType_DicomAsJson, info.GetContentType());
+  ASSERT_EQ("3e25960a79dbc69b674cd4ec67a72c62", info.GetUncompressedMD5());
+  ASSERT_NE(info.GetUncompressedMD5(), info.GetCompressedMD5());
 }
 
 
-TEST(FileStorageAccessor, Mix)
+TEST(StorageAccessor, Mix)
 {
   FilesystemStorage s("UnitTestsStorage");
-  CompressedFileStorageAccessor accessor(s);
+  StorageAccessor accessor(s);
 
   std::string r;
   std::string compressedData = "Hello";
   std::string uncompressedData = "HelloWorld";
 
-  accessor.SetCompressionForNextOperations(CompressionType_Zlib);
-  FileInfo compressedInfo = accessor.Write(compressedData, FileContentType_Dicom);
+  FileInfo compressedInfo = accessor.Write(compressedData, FileContentType_Dicom, CompressionType_ZlibWithSize, false);  
+  FileInfo uncompressedInfo = accessor.Write(uncompressedData, FileContentType_Dicom, CompressionType_None, false);
   
-  accessor.SetCompressionForNextOperations(CompressionType_None);
-  FileInfo uncompressedInfo = accessor.Write(uncompressedData, FileContentType_Dicom);
-  
-  accessor.SetCompressionForNextOperations(CompressionType_Zlib);
-  accessor.Read(r, compressedInfo.GetUuid(), FileContentType_Unknown);
+  accessor.Read(r, compressedInfo);
   ASSERT_EQ(compressedData, r);
 
-  accessor.SetCompressionForNextOperations(CompressionType_None);
-  accessor.Read(r, compressedInfo.GetUuid(), FileContentType_Unknown);
+  accessor.Read(r, uncompressedInfo);
+  ASSERT_EQ(uncompressedData, r);
   ASSERT_NE(compressedData, r);
 
   /*
   // This test is too slow on Windows
-  accessor.SetCompressionForNextOperations(CompressionType_Zlib);
+  accessor.SetCompressionForNextOperations(CompressionType_ZlibWithSize);
   ASSERT_THROW(accessor.Read(r, uncompressedInfo.GetUuid(), FileContentType_Unknown), OrthancException);
   */
 }
--- a/UnitTestsSources/FromDcmtkTests.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/UnitTestsSources/FromDcmtkTests.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -37,9 +37,9 @@
 #include "../OrthancServer/OrthancInitialization.h"
 #include "../OrthancServer/DicomModification.h"
 #include "../Core/OrthancException.h"
-#include "../Core/ImageFormats/ImageBuffer.h"
-#include "../Core/ImageFormats/PngReader.h"
-#include "../Core/ImageFormats/PngWriter.h"
+#include "../Core/Images/ImageBuffer.h"
+#include "../Core/Images/PngReader.h"
+#include "../Core/Images/PngWriter.h"
 #include "../Core/Uuid.h"
 #include "../Resources/EncodingTests.h"
 
@@ -145,33 +145,30 @@
   // Red dot in http://en.wikipedia.org/wiki/Data_URI_scheme (RGBA image)
   std::string s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
 
-  std::string m, c;
-  Toolbox::DecodeDataUriScheme(m, c, s);
+  std::string m, cc;
+  Toolbox::DecodeDataUriScheme(m, cc, s);
 
   ASSERT_EQ("image/png", m);
-  ASSERT_EQ(116, c.size());
 
-  std::string cc;
-  Toolbox::DecodeBase64(cc, c);
   PngReader reader;
   reader.ReadFromMemory(cc);
 
-  ASSERT_EQ(5, reader.GetHeight());
-  ASSERT_EQ(5, reader.GetWidth());
+  ASSERT_EQ(5u, reader.GetHeight());
+  ASSERT_EQ(5u, reader.GetWidth());
   ASSERT_EQ(PixelFormat_RGBA32, reader.GetFormat());
 
   ParsedDicomFile o;
-  o.EmbedImage(s);
+  o.EmbedContent(s);
   o.SaveToFile("UnitTestsResults/png1.dcm");
 
   // Red dot, without alpha channel
   s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDTcIn2+8BgAAACJJREFUCNdj/P//PwMjIwME/P/P+J8BBTAxEOL/R9Lx/z8AynoKAXOeiV8AAAAASUVORK5CYII=";
-  o.EmbedImage(s);
+  o.EmbedContent(s);
   o.SaveToFile("UnitTestsResults/png2.dcm");
 
   // Check box in Graylevel8
   s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDDcB53FulQAAAElJREFUGNNtj0sSAEEEQ1+U+185s1CtmRkblQ9CZldsKHJDk6DLGLJa6chjh0ooQmpjXMM86zPwydGEj6Ed/UGykkEM8X+p3u8/8LcOJIWLGeMAAAAASUVORK5CYII=";
-  o.EmbedImage(s);
+  o.EmbedContent(s);
   //o.Replace(DICOM_TAG_SOP_CLASS_UID, UID_DigitalXRayImageStorageForProcessing);
   o.SaveToFile("UnitTestsResults/png3.dcm");
 
@@ -184,7 +181,7 @@
     img.SetHeight(256);
     img.SetFormat(PixelFormat_Grayscale16);
 
-    int v = 0;
+    uint16_t v = 0;
     for (unsigned int y = 0; y < img.GetHeight(); y++)
     {
       uint16_t *p = reinterpret_cast<uint16_t*>(img.GetAccessor().GetRow(y));
@@ -272,6 +269,7 @@
       f.SaveToMemoryBuffer(dicom);
     }
 
+    if (testEncodings[i] != Encoding_Windows1251)
     {
       ParsedDicomFile g(dicom);
 
@@ -282,9 +280,22 @@
 
       std::string tag;
       ASSERT_TRUE(g.GetTagValue(tag, DICOM_TAG_PATIENT_NAME));
-
-      std::string expected();
       ASSERT_EQ(std::string(testEncodingsExpected[i]), tag);
     }
   }
 }
+
+
+TEST(FromDcmtkBridge, ValueRepresentation)
+{
+  ASSERT_EQ(ValueRepresentation_PatientName, 
+            FromDcmtkBridge::GetValueRepresentation(DICOM_TAG_PATIENT_NAME));
+  ASSERT_EQ(ValueRepresentation_Date, 
+            FromDcmtkBridge::GetValueRepresentation(DicomTag(0x0008, 0x0020) /* StudyDate */));
+  ASSERT_EQ(ValueRepresentation_Time, 
+            FromDcmtkBridge::GetValueRepresentation(DicomTag(0x0008, 0x0030) /* StudyTime */));
+  ASSERT_EQ(ValueRepresentation_DateTime, 
+            FromDcmtkBridge::GetValueRepresentation(DicomTag(0x0008, 0x002a) /* AcquisitionDateTime */));
+  ASSERT_EQ(ValueRepresentation_Other, 
+            FromDcmtkBridge::GetValueRepresentation(DICOM_TAG_PATIENT_ID));
+}
--- a/UnitTestsSources/ImageProcessingTests.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/UnitTestsSources/ImageProcessingTests.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -34,8 +34,8 @@
 #include "gtest/gtest.h"
 
 #include "../Core/DicomFormat/DicomImageInformation.h"
-#include "../Core/ImageFormats/ImageBuffer.h"
-#include "../Core/ImageFormats/ImageProcessing.h"
+#include "../Core/Images/ImageBuffer.h"
+#include "../Core/Images/ImageProcessing.h"
 
 using namespace Orthanc;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UnitTestsSources/ImageTests.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,259 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "PrecompiledHeadersUnitTests.h"
+#include "gtest/gtest.h"
+
+#include "../Core/Images/Font.h"
+#include "../Core/Images/Image.h"
+#include "../Core/Images/ImageProcessing.h"
+#include "../Core/Images/JpegReader.h"
+#include "../Core/Images/JpegWriter.h"
+#include "../Core/Images/PngReader.h"
+#include "../Core/Images/PngWriter.h"
+#include "../Core/Toolbox.h"
+#include "../Core/Uuid.h"
+#include "../OrthancServer/OrthancInitialization.h"
+
+#include <stdint.h>
+
+
+TEST(PngWriter, ColorPattern)
+{
+  Orthanc::PngWriter w;
+  unsigned int width = 17;
+  unsigned int height = 61;
+  unsigned int pitch = width * 3;
+
+  std::vector<uint8_t> image(height * pitch);
+  for (unsigned int y = 0; y < height; y++)
+  {
+    uint8_t *p = &image[0] + y * pitch;
+    for (unsigned int x = 0; x < width; x++, p += 3)
+    {
+      p[0] = (y % 3 == 0) ? 255 : 0;
+      p[1] = (y % 3 == 1) ? 255 : 0;
+      p[2] = (y % 3 == 2) ? 255 : 0;
+    }
+  }
+
+  w.WriteToFile("UnitTestsResults/ColorPattern.png", width, height, pitch, Orthanc::PixelFormat_RGB24, &image[0]);
+
+  std::string f, md5;
+  Orthanc::Toolbox::ReadFile(f, "UnitTestsResults/ColorPattern.png");
+  Orthanc::Toolbox::ComputeMD5(md5, f);
+  ASSERT_EQ("604e785f53c99cae6ea4584870b2c41d", md5);
+}
+
+TEST(PngWriter, Gray8Pattern)
+{
+  Orthanc::PngWriter w;
+  int width = 17;
+  int height = 256;
+  int pitch = width;
+
+  std::vector<uint8_t> image(height * pitch);
+  for (int y = 0; y < height; y++)
+  {
+    uint8_t *p = &image[0] + y * pitch;
+    for (int x = 0; x < width; x++, p++)
+    {
+      *p = y;
+    }
+  }
+
+  w.WriteToFile("UnitTestsResults/Gray8Pattern.png", width, height, pitch, Orthanc::PixelFormat_Grayscale8, &image[0]);
+
+  std::string f, md5;
+  Orthanc::Toolbox::ReadFile(f, "UnitTestsResults/Gray8Pattern.png");
+  Orthanc::Toolbox::ComputeMD5(md5, f);
+  ASSERT_EQ("5a9b98bea3d0a6d983980cc38bfbcdb3", md5);
+}
+
+TEST(PngWriter, Gray16Pattern)
+{
+  Orthanc::PngWriter w;
+  int width = 256;
+  int height = 256;
+  int pitch = width * 2 + 16;
+
+  std::vector<uint8_t> image(height * pitch);
+
+  int v = 0;
+  for (int y = 0; y < height; y++)
+  {
+    uint16_t *p = reinterpret_cast<uint16_t*>(&image[0] + y * pitch);
+    for (int x = 0; x < width; x++, p++, v++)
+    {
+      *p = v;
+    }
+  }
+
+  w.WriteToFile("UnitTestsResults/Gray16Pattern.png", width, height, pitch, Orthanc::PixelFormat_Grayscale16, &image[0]);
+
+  std::string f, md5;
+  Orthanc::Toolbox::ReadFile(f, "UnitTestsResults/Gray16Pattern.png");
+  Orthanc::Toolbox::ComputeMD5(md5, f);
+  ASSERT_EQ("0785866a08bf0a02d2eeff87f658571c", md5);
+}
+
+TEST(PngWriter, EndToEnd)
+{
+  Orthanc::PngWriter w;
+  unsigned int width = 256;
+  unsigned int height = 256;
+  unsigned int pitch = width * 2 + 16;
+
+  std::vector<uint8_t> image(height * pitch);
+
+  int v = 0;
+  for (unsigned int y = 0; y < height; y++)
+  {
+    uint16_t *p = reinterpret_cast<uint16_t*>(&image[0] + y * pitch);
+    for (unsigned int x = 0; x < width; x++, p++, v++)
+    {
+      *p = v;
+    }
+  }
+
+  std::string s;
+  w.WriteToMemory(s, width, height, pitch, Orthanc::PixelFormat_Grayscale16, &image[0]);
+
+  {
+    Orthanc::PngReader r;
+    r.ReadFromMemory(s);
+
+    ASSERT_EQ(r.GetFormat(), Orthanc::PixelFormat_Grayscale16);
+    ASSERT_EQ(r.GetWidth(), width);
+    ASSERT_EQ(r.GetHeight(), height);
+
+    v = 0;
+    for (unsigned int y = 0; y < height; y++)
+    {
+      const uint16_t *p = reinterpret_cast<const uint16_t*>((const uint8_t*) r.GetConstBuffer() + y * r.GetPitch());
+      ASSERT_EQ(p, r.GetConstRow(y));
+      for (unsigned int x = 0; x < width; x++, p++, v++)
+      {
+        ASSERT_EQ(*p, v);
+      }
+    }
+  }
+
+  {
+    Orthanc::Toolbox::TemporaryFile tmp;
+    Orthanc::Toolbox::WriteFile(s, tmp.GetPath());
+
+    Orthanc::PngReader r2;
+    r2.ReadFromFile(tmp.GetPath());
+
+    ASSERT_EQ(r2.GetFormat(), Orthanc::PixelFormat_Grayscale16);
+    ASSERT_EQ(r2.GetWidth(), width);
+    ASSERT_EQ(r2.GetHeight(), height);
+
+    v = 0;
+    for (unsigned int y = 0; y < height; y++)
+    {
+      const uint16_t *p = reinterpret_cast<const uint16_t*>((const uint8_t*) r2.GetConstBuffer() + y * r2.GetPitch());
+      ASSERT_EQ(p, r2.GetConstRow(y));
+      for (unsigned int x = 0; x < width; x++, p++, v++)
+      {
+        ASSERT_EQ(*p, v);
+      }
+    }
+  }
+}
+
+
+
+
+TEST(JpegWriter, Basic)
+{
+  std::string s;
+
+  {
+    Orthanc::Image img(Orthanc::PixelFormat_Grayscale8, 16, 16);
+    for (unsigned int y = 0, value = 0; y < img.GetHeight(); y++)
+    {
+      uint8_t* p = reinterpret_cast<uint8_t*>(img.GetRow(y));
+      for (unsigned int x = 0; x < img.GetWidth(); x++, p++)
+      {
+        *p = value++;
+      }
+    }
+
+    Orthanc::JpegWriter w;
+    w.WriteToFile("UnitTestsResults/hello.jpg", img);
+
+    w.WriteToMemory(s, img);
+    Orthanc::Toolbox::WriteFile(s, "UnitTestsResults/hello2.jpg");
+
+    std::string t;
+    Orthanc::Toolbox::ReadFile(t, "UnitTestsResults/hello.jpg");
+    ASSERT_EQ(s.size(), t.size());
+    ASSERT_EQ(0, memcmp(s.c_str(), t.c_str(), s.size()));
+  }
+
+  {
+    Orthanc::JpegReader r1, r2;
+    r1.ReadFromFile("UnitTestsResults/hello.jpg");
+    ASSERT_EQ(16, r1.GetWidth());
+    ASSERT_EQ(16, r1.GetHeight());
+
+    r2.ReadFromMemory(s);
+    ASSERT_EQ(16, r2.GetWidth());
+    ASSERT_EQ(16, r2.GetHeight());
+
+    for (unsigned int y = 0; y < r1.GetHeight(); y++)
+    {
+      const uint8_t* p1 = reinterpret_cast<const uint8_t*>(r1.GetConstRow(y));
+      const uint8_t* p2 = reinterpret_cast<const uint8_t*>(r2.GetConstRow(y));
+      for (unsigned int x = 0; x < r1.GetWidth(); x++)
+      {
+        ASSERT_EQ(*p1, *p2);
+      }
+    }
+  }
+}
+
+
+TEST(Font, Basic)
+{
+  Orthanc::Image s(Orthanc::PixelFormat_RGB24, 640, 480);
+  memset(s.GetBuffer(), 0, s.GetPitch() * s.GetHeight());
+
+  ASSERT_GE(1, Orthanc::Configuration::GetFontRegistry().GetSize());
+  Orthanc::Configuration::GetFontRegistry().GetFont(0).Draw(s, "Hello world É\n\rComment ça va ?\nq", 50, 60, 255, 0, 0);
+
+  Orthanc::PngWriter w;
+  w.WriteToFile("UnitTestsResults/font.png", s);
+}
+
--- a/UnitTestsSources/JpegLosslessTests.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/UnitTestsSources/JpegLosslessTests.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -41,8 +41,8 @@
 
 #include "../OrthancServer/ParsedDicomFile.h"
 #include "../Core/OrthancException.h"
-#include "../Core/ImageFormats/ImageBuffer.h"
-#include "../Core/ImageFormats/PngWriter.h"
+#include "../Core/Images/ImageBuffer.h"
+#include "../Core/Images/PngWriter.h"
 
 using namespace Orthanc;
 
--- a/UnitTestsSources/LuaTests.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/UnitTestsSources/LuaTests.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,13 +33,14 @@
 #include "PrecompiledHeadersUnitTests.h"
 #include "gtest/gtest.h"
 
+#include "../Core/OrthancException.h"
 #include "../Core/Toolbox.h"
 #include "../Core/Lua/LuaFunctionCall.h"
 
 #include <boost/lexical_cast.hpp>
 
 #if !defined(UNIT_TESTS_WITH_HTTP_CONNEXIONS)
-#error "Please set UNIT_TESTS_WITH_HTTP_CONNEXIONS"
+#error "Please set UNIT_TESTS_WITH_HTTP_CONNEXIONS to 0 or 1"
 #endif
 
 
@@ -79,7 +80,7 @@
   {
     Orthanc::LuaFunctionCall f(lua, "f");
     f.PushJson(o);
-    ASSERT_THROW(f.ExecutePredicate(), Orthanc::LuaException);
+    ASSERT_THROW(f.ExecutePredicate(), Orthanc::OrthancException);
   }
 
   o["bool"] = false;
@@ -147,8 +148,8 @@
 {
   Json::Value b = Json::objectValue;
   b["a"] = 42;
-  b["b"] = 44;
-  b["c"] = 43;
+  b["b"] = 44.37;
+  b["c"] = -43;
 
   Json::Value c = Json::arrayValue;
   c.append("test3");
@@ -170,7 +171,7 @@
     Orthanc::LuaFunctionCall f(lua, "identity");
     f.PushJson("hello");
     Json::Value v;
-    f.ExecuteToJson(v);
+    f.ExecuteToJson(v, false);
     ASSERT_EQ("hello", v.asString());
   }
 
@@ -178,16 +179,24 @@
     Orthanc::LuaFunctionCall f(lua, "identity");
     f.PushJson(42.25);
     Json::Value v;
-    f.ExecuteToJson(v);
+    f.ExecuteToJson(v, false);
     ASSERT_FLOAT_EQ(42.25f, v.asFloat());
   }
 
   {
     Orthanc::LuaFunctionCall f(lua, "identity");
+    f.PushJson(-42);
+    Json::Value v;
+    f.ExecuteToJson(v, false);
+    ASSERT_EQ(-42, v.asInt());
+  }
+
+  {
+    Orthanc::LuaFunctionCall f(lua, "identity");
     Json::Value vv = Json::arrayValue;
     f.PushJson(vv);
     Json::Value v;
-    f.ExecuteToJson(v);
+    f.ExecuteToJson(v, false);
     ASSERT_EQ(Json::arrayValue, v.type());
   }
 
@@ -196,7 +205,7 @@
     Json::Value vv = Json::objectValue;
     f.PushJson(vv);
     Json::Value v;
-    f.ExecuteToJson(v);
+    f.ExecuteToJson(v, false);
     // Lua does not make the distinction between empty lists and empty objects
     ASSERT_EQ(Json::arrayValue, v.type());
   }
@@ -205,18 +214,18 @@
     Orthanc::LuaFunctionCall f(lua, "identity");
     f.PushJson(b);
     Json::Value v;
-    f.ExecuteToJson(v);
+    f.ExecuteToJson(v, false);
     ASSERT_EQ(Json::objectValue, v.type());
     ASSERT_FLOAT_EQ(42.0f, v["a"].asFloat());
-    ASSERT_FLOAT_EQ(44.0f, v["b"].asFloat());
-    ASSERT_FLOAT_EQ(43.0f, v["c"].asFloat());
+    ASSERT_FLOAT_EQ(44.37f, v["b"].asFloat());
+    ASSERT_FLOAT_EQ(-43.0f, v["c"].asFloat());
   }
 
   {
     Orthanc::LuaFunctionCall f(lua, "identity");
     f.PushJson(c);
     Json::Value v;
-    f.ExecuteToJson(v);
+    f.ExecuteToJson(v, false);
     ASSERT_EQ(Json::arrayValue, v.type());
     ASSERT_EQ("test3", v[0].asString());
     ASSERT_EQ("test1", v[1].asString());
@@ -227,29 +236,64 @@
     Orthanc::LuaFunctionCall f(lua, "identity");
     f.PushJson(a);
     Json::Value v;
-    f.ExecuteToJson(v);
+    f.ExecuteToJson(v, false);
     ASSERT_EQ("World", v["Hello"].asString());
+    ASSERT_EQ(Json::intValue, v["List"][0]["a"].type());
+    ASSERT_EQ(Json::realValue, v["List"][0]["b"].type());
+    ASSERT_EQ(Json::intValue, v["List"][0]["c"].type());
     ASSERT_EQ(42, v["List"][0]["a"].asInt());
+    ASSERT_FLOAT_EQ(44.37f, v["List"][0]["b"].asFloat());
     ASSERT_EQ(44, v["List"][0]["b"].asInt());
-    ASSERT_EQ(43, v["List"][0]["c"].asInt());
+    ASSERT_EQ(-43, v["List"][0]["c"].asInt());
     ASSERT_EQ("test3", v["List"][1][0].asString());
     ASSERT_EQ("test1", v["List"][1][1].asString());
     ASSERT_EQ("test2", v["List"][1][2].asString());
   }
+
+  {
+    Orthanc::LuaFunctionCall f(lua, "identity");
+    f.PushJson(a);
+    Json::Value v;
+    f.ExecuteToJson(v, true);
+    ASSERT_EQ("World", v["Hello"].asString());
+    ASSERT_EQ(Json::stringValue, v["List"][0]["a"].type());
+    ASSERT_EQ(Json::stringValue, v["List"][0]["b"].type());
+    ASSERT_EQ(Json::stringValue, v["List"][0]["c"].type());
+    ASSERT_EQ("42", v["List"][0]["a"].asString());
+    ASSERT_EQ("44.37", v["List"][0]["b"].asString());
+    ASSERT_EQ("-43", v["List"][0]["c"].asString());
+    ASSERT_EQ("test3", v["List"][1][0].asString());
+    ASSERT_EQ("test1", v["List"][1][1].asString());
+    ASSERT_EQ("test2", v["List"][1][2].asString());
+  }
+
+  {
+    Orthanc::LuaFunctionCall f(lua, "DumpJson");
+    f.PushJson(a);
+    std::string s;
+    f.ExecuteToString(s);
+
+    Json::FastWriter writer;
+    std::string t = writer.write(a);
+
+    ASSERT_EQ(s, t);
+  }
 }
 
+
+
 TEST(Lua, Http)
 {
   Orthanc::LuaContext lua;
 
 #if UNIT_TESTS_WITH_HTTP_CONNEXIONS == 1  
   lua.Execute("JSON = loadstring(HttpGet('http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/JSON.lua')) ()");
-  const std::string url("http://orthanc.googlecode.com/hg/OrthancCppClient/SharedLibrary/Product.json");
+  const std::string url("http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/Product.json");
 #endif
 
   std::string s;
   lua.Execute(s, "print(HttpGet({}))");
-  ASSERT_EQ("ERROR", Orthanc::Toolbox::StripSpaces(s));
+  ASSERT_EQ("nil", Orthanc::Toolbox::StripSpaces(s));
 
 #if UNIT_TESTS_WITH_HTTP_CONNEXIONS == 1  
   lua.Execute(s, "print(string.len(HttpGet(\"" + url + "\")))");
--- a/UnitTestsSources/MemoryCacheTests.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/UnitTestsSources/MemoryCacheTests.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,12 +33,14 @@
 #include "PrecompiledHeadersUnitTests.h"
 #include "gtest/gtest.h"
 
-#include <glog/logging.h>
 #include <memory>
 #include <boost/thread.hpp>
 #include <boost/lexical_cast.hpp>
+
+#include "../Core/Cache/MemoryCache.h"
+#include "../Core/Cache/SharedArchive.h"
 #include "../Core/IDynamicObject.h"
-#include "../Core/Cache/MemoryCache.h"
+#include "../Core/Logging.h"
 
 
 TEST(LRU, Basic)
@@ -188,11 +190,6 @@
       LOG(INFO) << "Removing cache entry for " << value_;
       log_ += boost::lexical_cast<std::string>(value_) + " ";
     }
-
-    int GetValue() const 
-    {
-      return value_;
-    }
   };
 
   class IntegerProvider : public Orthanc::ICachePageProvider
@@ -228,3 +225,59 @@
 
   ASSERT_EQ("45 42 43 47 44 42 ", provider.log_);
 }
+
+
+
+
+
+namespace
+{
+  class S : public Orthanc::IDynamicObject
+  {
+  private:
+    std::string value_;
+
+  public:
+    S(const std::string& value) : value_(value)
+    {
+    }
+
+    const std::string& GetValue() const
+    {
+      return value_;
+    }
+  };
+}
+
+
+TEST(LRU, SharedArchive)
+{
+  std::string first, second;
+  Orthanc::SharedArchive a(3);
+  first = a.Add(new S("First item"));
+  second = a.Add(new S("Second item"));
+
+  for (int i = 1; i < 100; i++)
+  {
+    a.Add(new S("Item " + boost::lexical_cast<std::string>(i)));
+    // Continuously protect the two first items
+    try { Orthanc::SharedArchive::Accessor(a, first);  } catch (Orthanc::OrthancException&) {}
+    try { Orthanc::SharedArchive::Accessor(a, second); } catch (Orthanc::OrthancException&) {}
+  }
+
+  std::list<std::string> i;
+  a.List(i);
+
+  size_t count = 0;
+  for (std::list<std::string>::const_iterator
+         it = i.begin(); it != i.end(); it++)
+  {
+    if (*it == first ||
+        *it == second)
+    {
+      count++;
+    }
+  }
+
+  ASSERT_EQ(2u, count);
+}
--- a/UnitTestsSources/MultiThreadingTests.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/UnitTestsSources/MultiThreadingTests.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -32,23 +32,19 @@
 
 #include "PrecompiledHeadersUnitTests.h"
 #include "gtest/gtest.h"
-#include <glog/logging.h>
 
 #include "../OrthancServer/Scheduler/ServerScheduler.h"
-
 #include "../Core/OrthancException.h"
 #include "../Core/Toolbox.h"
-#include "../Core/MultiThreading/ArrayFilledByThreads.h"
 #include "../Core/MultiThreading/Locker.h"
 #include "../Core/MultiThreading/Mutex.h"
 #include "../Core/MultiThreading/ReaderWriterLock.h"
-#include "../Core/MultiThreading/ThreadedCommandProcessor.h"
 
 using namespace Orthanc;
 
 namespace
 {
-  class DynamicInteger : public ICommand
+  class DynamicInteger : public IDynamicObject
   {
   private:
     int value_;
@@ -64,56 +60,10 @@
     {
       return value_;
     }
-
-    virtual bool Execute()
-    {
-      static boost::mutex mutex;
-      boost::mutex::scoped_lock lock(mutex);
-      target_.insert(value_);
-      return true;
-    }
-  };
-
-  class MyFiller : public ArrayFilledByThreads::IFiller
-  {
-  private:
-    int size_;
-    unsigned int created_;
-    std::set<int> set_;
-
-  public:
-    MyFiller(int size) : size_(size), created_(0)
-    {
-    }
-
-    virtual size_t GetFillerSize()
-    {
-      return size_;
-    }
-
-    virtual IDynamicObject* GetFillerItem(size_t index)
-    {
-      static boost::mutex mutex;
-      boost::mutex::scoped_lock lock(mutex);
-      created_++;
-      return new DynamicInteger(index * 2, set_);
-    }
-
-    unsigned int GetCreatedCount() const
-    {
-      return created_;
-    }
-
-    std::set<int> GetSet()
-    {
-      return set_;
-    }    
   };
 }
 
 
-
-
 TEST(MultiThreading, SharedMessageQueueBasic)
 {
   std::set<int> s;
@@ -146,7 +96,7 @@
     SharedMessageQueue q;
     q.Enqueue(new DynamicInteger(10, s));
     q.Enqueue(new DynamicInteger(20, s));  
-    throw OrthancException("Nope");
+    throw OrthancException(ErrorCode_InternalError);
   }
   catch (OrthancException&)
   {
@@ -154,78 +104,6 @@
 }
 
 
-TEST(MultiThreading, ArrayFilledByThreadEmpty)
-{
-  MyFiller f(0);
-  ArrayFilledByThreads a(f);
-  a.SetThreadCount(1);
-  ASSERT_EQ(0, a.GetSize());
-}
-
-
-TEST(MultiThreading, ArrayFilledByThread1)
-{
-  MyFiller f(100);
-  ArrayFilledByThreads a(f);
-  a.SetThreadCount(1);
-  ASSERT_EQ(100, a.GetSize());
-  for (size_t i = 0; i < a.GetSize(); i++)
-  {
-    ASSERT_EQ(2 * i, dynamic_cast<DynamicInteger&>(a.GetItem(i)).GetValue());
-  }
-}
-
-
-TEST(MultiThreading, ArrayFilledByThread4)
-{
-  MyFiller f(100);
-  ArrayFilledByThreads a(f);
-  a.SetThreadCount(4);
-  ASSERT_EQ(100, a.GetSize());
-  for (size_t i = 0; i < a.GetSize(); i++)
-  {
-    ASSERT_EQ(2 * i, dynamic_cast<DynamicInteger&>(a.GetItem(i)).GetValue());
-  }
-
-  ASSERT_EQ(100u, f.GetCreatedCount());
-
-  a.Invalidate();
-
-  ASSERT_EQ(100, a.GetSize());
-  ASSERT_EQ(200u, f.GetCreatedCount());
-  ASSERT_EQ(4u, a.GetThreadCount());
-  ASSERT_TRUE(f.GetSet().empty());
-
-  for (size_t i = 0; i < a.GetSize(); i++)
-  {
-    ASSERT_EQ(2 * i, dynamic_cast<DynamicInteger&>(a.GetItem(i)).GetValue());
-  }
-}
-
-
-TEST(MultiThreading, CommandProcessor)
-{
-  ThreadedCommandProcessor p(4);
-
-  std::set<int> s;
-
-  for (size_t i = 0; i < 100; i++)
-  {
-    p.Post(new DynamicInteger(i * 2, s));
-  }
-
-  p.Join();
-
-  for (size_t i = 0; i < 200; i++)
-  {
-    if (i % 2)
-      ASSERT_TRUE(s.find(i) == s.end());
-    else
-      ASSERT_TRUE(s.find(i) != s.end());
-  }
-}
-
-
 TEST(MultiThreading, Mutex)
 {
   Mutex mutex;
@@ -258,7 +136,8 @@
   printf("START\n"); fflush(stdout);
 
   {
-    ReusableDicomUserConnection::Locker lock(c, "STORESCP", "localhost", 2000, ModalityManufacturer_Generic);
+    RemoteModalityParameters remote("STORESCP", "localhost", 2000, ModalityManufacturer_Generic);
+    ReusableDicomUserConnection::Locker lock(c, "ORTHANC", remote);
     lock.GetConnection().StoreFile("/home/jodogne/DICOM/Cardiac/MR.X.1.2.276.0.7230010.3.1.4.2831157719.2256.1336386844.676281");
   }
 
@@ -267,7 +146,8 @@
   printf("**\n"); fflush(stdout);
 
   {
-    ReusableDicomUserConnection::Locker lock(c, "STORESCP", "localhost", 2000, ModalityManufacturer_Generic);
+    RemoteModalityParameters remote("STORESCP", "localhost", 2000, ModalityManufacturer_Generic);
+    ReusableDicomUserConnection::Locker lock(c, "ORTHANC", remote);
     lock.GetConnection().StoreFile("/home/jodogne/DICOM/Cardiac/MR.X.1.2.276.0.7230010.3.1.4.2831157719.2256.1336386844.676277");
   }
 
@@ -357,7 +237,7 @@
   IServerCommand::ListOfStrings l;
   scheduler.SubmitAndWait(l, job);
 
-  ASSERT_EQ(2, l.size());
+  ASSERT_EQ(2u, l.size());
   ASSERT_EQ(42 * 2 * 3, boost::lexical_cast<int>(l.front()));
   ASSERT_EQ(42 * 2 * 3 * 4 * 5, boost::lexical_cast<int>(l.back()));
 
@@ -369,6 +249,11 @@
   //Toolbox::ServerBarrier();
   //Toolbox::USleep(3000000);
 
+  scheduler.Stop();
+
   done = true;
-  t.join();
+  if (t.joinable())
+  {
+    t.join();
+  }
 }
--- a/UnitTestsSources/PluginsTests.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/UnitTestsSources/PluginsTests.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,12 +33,21 @@
 #include "PrecompiledHeadersUnitTests.h"
 #include "gtest/gtest.h"
 
-#include <glog/logging.h>
-
 #include "../Plugins/Engine/PluginsManager.h"
 
 using namespace Orthanc;
 
+
+#if ORTHANC_PLUGINS_ENABLED == 1
+
+TEST(SharedLibrary, Enumerations)
+{
+  // The plugin engine cannot work if the size of an enumeration does
+  // not correspond to the size of "int32_t"
+  ASSERT_EQ(sizeof(int32_t), sizeof(OrthancPluginErrorCode));
+}
+
+
 TEST(SharedLibrary, Basic)
 {
 #if defined(_WIN32)
@@ -55,6 +64,16 @@
   ASSERT_TRUE(l.HasFunction("dlclose"));
   ASSERT_FALSE(l.HasFunction("world"));
 
+#elif defined(__FreeBSD__)
+  // dlopen() in FreeBSD is supplied by libc, libc.so is
+  // a ldscript, so we can't actually use it. Use thread
+  // library instead - if it works - dlopen() is good.
+  SharedLibrary l("libpthread.so");
+  ASSERT_THROW(l.GetFunction("world"), OrthancException);
+  ASSERT_TRUE(l.GetFunction("pthread_create") != NULL);
+  ASSERT_TRUE(l.HasFunction("pthread_cancel"));
+  ASSERT_FALSE(l.HasFunction("world"));
+
 #elif defined(__APPLE__) && defined(__MACH__)
   SharedLibrary l("libdl.dylib");
   ASSERT_THROW(l.GetFunction("world"), OrthancException);
@@ -66,3 +85,5 @@
 #error Support your platform here
 #endif
 }
+
+#endif
--- a/UnitTestsSources/PngTests.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,186 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "PrecompiledHeadersUnitTests.h"
-#include "gtest/gtest.h"
-
-#include <stdint.h>
-#include "../Core/ImageFormats/PngReader.h"
-#include "../Core/ImageFormats/PngWriter.h"
-#include "../Core/Toolbox.h"
-#include "../Core/Uuid.h"
-
-
-TEST(PngWriter, ColorPattern)
-{
-  Orthanc::PngWriter w;
-  int width = 17;
-  int height = 61;
-  int pitch = width * 3;
-
-  std::vector<uint8_t> image(height * pitch);
-  for (int y = 0; y < height; y++)
-  {
-    uint8_t *p = &image[0] + y * pitch;
-    for (int x = 0; x < width; x++, p += 3)
-    {
-      p[0] = (y % 3 == 0) ? 255 : 0;
-      p[1] = (y % 3 == 1) ? 255 : 0;
-      p[2] = (y % 3 == 2) ? 255 : 0;
-    }
-  }
-
-  w.WriteToFile("UnitTestsResults/ColorPattern.png", width, height, pitch, Orthanc::PixelFormat_RGB24, &image[0]);
-
-  std::string f, md5;
-  Orthanc::Toolbox::ReadFile(f, "UnitTestsResults/ColorPattern.png");
-  Orthanc::Toolbox::ComputeMD5(md5, f);
-  ASSERT_EQ("604e785f53c99cae6ea4584870b2c41d", md5);
-}
-
-TEST(PngWriter, Gray8Pattern)
-{
-  Orthanc::PngWriter w;
-  int width = 17;
-  int height = 256;
-  int pitch = width;
-
-  std::vector<uint8_t> image(height * pitch);
-  for (int y = 0; y < height; y++)
-  {
-    uint8_t *p = &image[0] + y * pitch;
-    for (int x = 0; x < width; x++, p++)
-    {
-      *p = y;
-    }
-  }
-
-  w.WriteToFile("UnitTestsResults/Gray8Pattern.png", width, height, pitch, Orthanc::PixelFormat_Grayscale8, &image[0]);
-
-  std::string f, md5;
-  Orthanc::Toolbox::ReadFile(f, "UnitTestsResults/Gray8Pattern.png");
-  Orthanc::Toolbox::ComputeMD5(md5, f);
-  ASSERT_EQ("5a9b98bea3d0a6d983980cc38bfbcdb3", md5);
-}
-
-TEST(PngWriter, Gray16Pattern)
-{
-  Orthanc::PngWriter w;
-  int width = 256;
-  int height = 256;
-  int pitch = width * 2 + 16;
-
-  std::vector<uint8_t> image(height * pitch);
-
-  int v = 0;
-  for (int y = 0; y < height; y++)
-  {
-    uint16_t *p = reinterpret_cast<uint16_t*>(&image[0] + y * pitch);
-    for (int x = 0; x < width; x++, p++, v++)
-    {
-      *p = v;
-    }
-  }
-
-  w.WriteToFile("UnitTestsResults/Gray16Pattern.png", width, height, pitch, Orthanc::PixelFormat_Grayscale16, &image[0]);
-
-  std::string f, md5;
-  Orthanc::Toolbox::ReadFile(f, "UnitTestsResults/Gray16Pattern.png");
-  Orthanc::Toolbox::ComputeMD5(md5, f);
-  ASSERT_EQ("0785866a08bf0a02d2eeff87f658571c", md5);
-}
-
-TEST(PngWriter, EndToEnd)
-{
-  Orthanc::PngWriter w;
-  int width = 256;
-  int height = 256;
-  int pitch = width * 2 + 16;
-
-  std::vector<uint8_t> image(height * pitch);
-
-  int v = 0;
-  for (int y = 0; y < height; y++)
-  {
-    uint16_t *p = reinterpret_cast<uint16_t*>(&image[0] + y * pitch);
-    for (int x = 0; x < width; x++, p++, v++)
-    {
-      *p = v;
-    }
-  }
-
-  std::string s;
-  w.WriteToMemory(s, width, height, pitch, Orthanc::PixelFormat_Grayscale16, &image[0]);
-
-  {
-    Orthanc::PngReader r;
-    r.ReadFromMemory(s);
-
-    ASSERT_EQ(r.GetFormat(), Orthanc::PixelFormat_Grayscale16);
-    ASSERT_EQ(r.GetWidth(), width);
-    ASSERT_EQ(r.GetHeight(), height);
-
-    v = 0;
-    for (int y = 0; y < height; y++)
-    {
-      const uint16_t *p = reinterpret_cast<const uint16_t*>((const uint8_t*) r.GetConstBuffer() + y * r.GetPitch());
-      ASSERT_EQ(p, r.GetConstRow(y));
-      for (int x = 0; x < width; x++, p++, v++)
-      {
-        ASSERT_EQ(*p, v);
-      }
-    }
-  }
-
-  {
-    Orthanc::Toolbox::TemporaryFile tmp;
-    Orthanc::Toolbox::WriteFile(s, tmp.GetPath());
-
-    Orthanc::PngReader r2;
-    r2.ReadFromFile(tmp.GetPath());
-
-    ASSERT_EQ(r2.GetFormat(), Orthanc::PixelFormat_Grayscale16);
-    ASSERT_EQ(r2.GetWidth(), width);
-    ASSERT_EQ(r2.GetHeight(), height);
-
-    v = 0;
-    for (int y = 0; y < height; y++)
-    {
-      const uint16_t *p = reinterpret_cast<const uint16_t*>((const uint8_t*) r2.GetConstBuffer() + y * r2.GetPitch());
-      ASSERT_EQ(p, r2.GetConstRow(y));
-      for (int x = 0; x < width; x++, p++, v++)
-      {
-        ASSERT_EQ(*p, v);
-      }
-    }
-  }
-}
--- a/UnitTestsSources/RestApiTests.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/UnitTestsSources/RestApiTests.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -34,10 +34,10 @@
 #include "gtest/gtest.h"
 
 #include <ctype.h>
-#include <glog/logging.h>
 
 #include "../Core/ChunkedBuffer.h"
 #include "../Core/HttpClient.h"
+#include "../Core/Logging.h"
 #include "../Core/RestApi/RestApi.h"
 #include "../Core/Uuid.h"
 #include "../Core/OrthancException.h"
@@ -50,6 +50,8 @@
 #error "Please set UNIT_TESTS_WITH_HTTP_CONNEXIONS"
 #endif
 
+
+
 TEST(HttpClient, Basic)
 {
   HttpClient c;
@@ -61,30 +63,82 @@
 
 #if UNIT_TESTS_WITH_HTTP_CONNEXIONS == 1
   Json::Value v;
-  c.SetUrl("http://orthanc.googlecode.com/hg/Resources/Configuration.json");
+  c.SetUrl("http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/Configuration.json");
   c.Apply(v);
   ASSERT_TRUE(v.isMember("StorageDirectory"));
-  //ASSERT_EQ(GetLastStatusText());
-
-  v = Json::nullValue;
-
-  HttpClient cc(c);
-  cc.SetUrl("https://orthanc.googlecode.com/hg/Resources/Configuration.json");
-  cc.Apply(v);
-  ASSERT_TRUE(v.isMember("LuaScripts"));
 #endif
 }
 
+
+#if UNIT_TESTS_WITH_HTTP_CONNEXIONS == 1 && ORTHANC_SSL_ENABLED == 1
+
+/**
+   The HTTPS CA certificates for BitBucket were extracted as follows:
+   
+   (1) We retrieve the certification chain of BitBucket:
+
+   # echo | openssl s_client -showcerts -connect www.bitbucket.org:443
+
+   (2) We see that the certification authority (CA) is
+   "www.digicert.com", and the root certificate is "DigiCert High
+   Assurance EV Root CA". As a consequence, we navigate to DigiCert to
+   find the URL to this CA certificate:
+
+   firefox https://www.digicert.com/digicert-root-certificates.htm
+
+   (3) Once we get the URL to the CA certificate, we convert it to a C
+   macro that can be used by libcurl:
+
+   # cd UnitTestsSources
+   # ../Resources/RetrieveCACertificates.py BITBUCKET_CERTIFICATES https://www.digicert.com/CACerts/DigiCertHighAssuranceEVRootCA.crt > BitbucketCACertificates.h
+**/
+
+#include "BitbucketCACertificates.h"
+
+TEST(HttpClient, Ssl)
+{
+  Toolbox::WriteFile(BITBUCKET_CERTIFICATES, "UnitTestsResults/bitbucket.cert");
+
+  /*{
+    std::string s;
+    Toolbox::ReadFile(s, "/usr/share/ca-certificates/mozilla/WoSign.crt");
+    Toolbox::WriteFile(s, "UnitTestsResults/bitbucket.cert");
+    }*/
+
+  HttpClient c;
+  c.SetHttpsVerifyPeers(true);
+  c.SetHttpsCACertificates("UnitTestsResults/bitbucket.cert");
+  c.SetUrl("https://bitbucket.org/sjodogne/orthanc/raw/Orthanc-0.9.3/Resources/Configuration.json");
+
+  Json::Value v;
+  c.Apply(v);
+  ASSERT_TRUE(v.isMember("LuaScripts"));
+}
+
+TEST(HttpClient, SslNoVerification)
+{
+  HttpClient c;
+  c.SetHttpsVerifyPeers(false);
+  c.SetUrl("https://bitbucket.org/sjodogne/orthanc/raw/Orthanc-0.9.3/Resources/Configuration.json");
+
+  Json::Value v;
+  c.Apply(v);
+  ASSERT_TRUE(v.isMember("LuaScripts"));
+}
+
+#endif
+
+
 TEST(RestApi, ChunkedBuffer)
 {
   ChunkedBuffer b;
-  ASSERT_EQ(0, b.GetNumBytes());
+  ASSERT_EQ(0u, b.GetNumBytes());
 
   b.AddChunk("hello", 5);
-  ASSERT_EQ(5, b.GetNumBytes());
+  ASSERT_EQ(5u, b.GetNumBytes());
 
   b.AddChunk("world", 5);
-  ASSERT_EQ(10, b.GetNumBytes());
+  ASSERT_EQ(10u, b.GetNumBytes());
 
   std::string s;
   b.Flatten(s);
@@ -93,11 +147,11 @@
 
 TEST(RestApi, ParseCookies)
 {
-  HttpHandler::Arguments headers;
-  HttpHandler::Arguments cookies;
+  IHttpHandler::Arguments headers;
+  IHttpHandler::Arguments cookies;
 
   headers["cookie"] = "a=b;c=d;;;e=f;;g=h;";
-  HttpHandler::ParseCookies(cookies, headers);
+  HttpToolbox::ParseCookies(cookies, headers);
   ASSERT_EQ(4u, cookies.size());
   ASSERT_EQ("b", cookies["a"]);
   ASSERT_EQ("d", cookies["c"]);
@@ -105,24 +159,24 @@
   ASSERT_EQ("h", cookies["g"]);
 
   headers["cookie"] = "  name =  value  ; name2=value2";
-  HttpHandler::ParseCookies(cookies, headers);
+  HttpToolbox::ParseCookies(cookies, headers);
   ASSERT_EQ(2u, cookies.size());
   ASSERT_EQ("value", cookies["name"]);
   ASSERT_EQ("value2", cookies["name2"]);
 
   headers["cookie"] = "  ;;;    ";
-  HttpHandler::ParseCookies(cookies, headers);
+  HttpToolbox::ParseCookies(cookies, headers);
   ASSERT_EQ(0u, cookies.size());
 
   headers["cookie"] = "  ;   n=v  ;;    ";
-  HttpHandler::ParseCookies(cookies, headers);
+  HttpToolbox::ParseCookies(cookies, headers);
   ASSERT_EQ(1u, cookies.size());
   ASSERT_EQ("v", cookies["n"]);
 }
 
 TEST(RestApi, RestApiPath)
 {
-  HttpHandler::Arguments args;
+  IHttpHandler::Arguments args;
   UriComponents trail;
 
   {
@@ -220,7 +274,7 @@
   public:
     virtual bool Visit(const RestApiHierarchy::Resource& resource,
                        const UriComponents& uri,
-                       const HttpHandler::Arguments& components,
+                       const IHttpHandler::Arguments& components,
                        const UriComponents& trailing)
     {
       return resource.Handle(*reinterpret_cast<RestApiGetCall*>(NULL));
--- a/UnitTestsSources/SQLiteTests.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/UnitTestsSources/SQLiteTests.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -45,7 +45,14 @@
 
 TEST(SQLite, Configuration)
 {
-  ASSERT_EQ(1, sqlite3_threadsafe());
+  /**
+   * The system-wide version of SQLite under OS X uses
+   * SQLITE_THREADSAFE==2 (SQLITE_CONFIG_SERIALIZED), whereas the
+   * static builds of Orthanc use SQLITE_THREADSAFE==1
+   * (SQLITE_CONFIG_MULTITHREAD). In any case, we wish to ensure that
+   * SQLITE_THREADSAFE!=0 (SQLITE_CONFIG_SINGLETHREAD).
+   **/
+  ASSERT_NE(0, sqlite3_threadsafe());
 }
 
 
--- a/UnitTestsSources/ServerIndexTests.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/UnitTestsSources/ServerIndexTests.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -33,15 +33,15 @@
 #include "PrecompiledHeadersUnitTests.h"
 #include "gtest/gtest.h"
 
+#include "../Core/DicomFormat/DicomNullValue.h"
+#include "../Core/FileStorage/FilesystemStorage.h"
+#include "../Core/Logging.h"
+#include "../Core/Uuid.h"
 #include "../OrthancServer/DatabaseWrapper.h"
 #include "../OrthancServer/ServerContext.h"
 #include "../OrthancServer/ServerIndex.h"
-#include "../Core/Uuid.h"
-#include "../Core/DicomFormat/DicomNullValue.h"
-#include "../Core/FileStorage/FilesystemStorage.h"
 
 #include <ctype.h>
-#include <glog/logging.h>
 #include <algorithm>
 
 using namespace Orthanc;
@@ -54,7 +54,7 @@
   };
 
 
-  class ServerIndexListener : public IServerIndexListener
+  class TestDatabaseListener : public IDatabaseListener
   {
   public:
     std::vector<std::string> deletedFiles_;
@@ -100,7 +100,7 @@
   class DatabaseWrapperTest : public ::testing::TestWithParam<DatabaseWrapperClass>
   {
   protected:
-    std::auto_ptr<ServerIndexListener> listener_;
+    std::auto_ptr<TestDatabaseListener> listener_;
     std::auto_ptr<IDatabaseWrapper> index_;
 
     DatabaseWrapperTest()
@@ -109,7 +109,7 @@
 
     virtual void SetUp() 
     {
-      listener_.reset(new ServerIndexListener);
+      listener_.reset(new TestDatabaseListener);
 
       switch (GetParam())
       {
@@ -193,7 +193,7 @@
         {
           DatabaseWrapper* sqlite = dynamic_cast<DatabaseWrapper*>(index_.get());
           sqlite->GetChildren(j, id);
-          ASSERT_EQ(0, j.size());
+          ASSERT_EQ(0u, j.size());
           break;
         }
 
@@ -212,7 +212,7 @@
         {
           DatabaseWrapper* sqlite = dynamic_cast<DatabaseWrapper*>(index_.get());
           sqlite->GetChildren(j, id);
-          ASSERT_EQ(1, j.size());
+          ASSERT_EQ(1u, j.size());
           ASSERT_EQ(expected, j.front());
           break;
         }
@@ -234,7 +234,7 @@
         {
           DatabaseWrapper* sqlite = dynamic_cast<DatabaseWrapper*>(index_.get());
           sqlite->GetChildren(j, id);
-          ASSERT_EQ(2, j.size());
+          ASSERT_EQ(2u, j.size());
           ASSERT_TRUE((expected1 == j.front() && expected2 == j.back()) ||
                       (expected1 == j.back() && expected2 == j.front()));                    
           break;
@@ -350,7 +350,7 @@
   ASSERT_EQ(0u, md.size());
 
   index_->AddAttachment(a[4], FileInfo("my json file", FileContentType_DicomAsJson, 42, "md5", 
-                                       CompressionType_Zlib, 21, "compressedMD5"));
+                                       CompressionType_ZlibWithSize, 21, "compressedMD5"));
   index_->AddAttachment(a[4], FileInfo("my dicom file", FileContentType_Dicom, 42, "md5"));
   index_->AddAttachment(a[6], FileInfo("world", FileContentType_Dicom, 44, "md5"));
   index_->SetMetadata(a[4], MetadataType_Instance_RemoteAet, "PINNACLE");
@@ -409,7 +409,7 @@
   ASSERT_EQ("md5", att.GetUncompressedMD5());
   ASSERT_EQ("compressedMD5", att.GetCompressedMD5());
   ASSERT_EQ(42u, att.GetUncompressedSize());
-  ASSERT_EQ(CompressionType_Zlib, att.GetCompressionType());
+  ASSERT_EQ(CompressionType_ZlibWithSize, att.GetCompressionType());
 
   ASSERT_TRUE(index_->LookupAttachment(att, a[6], FileContentType_Dicom));
   ASSERT_EQ("world", att.GetUuid());
@@ -660,14 +660,15 @@
   Toolbox::RemoveFile(path + "/index");
   FilesystemStorage storage(path);
   DatabaseWrapper db;   // The SQLite DB is in memory
-  ServerContext context(db);
-  context.SetStorageArea(storage);
+  ServerContext context(db, storage);
   ServerIndex& index = context.GetIndex();
 
   ASSERT_EQ(1u, index.IncrementGlobalSequence(GlobalProperty_AnonymizationSequence));
   ASSERT_EQ(2u, index.IncrementGlobalSequence(GlobalProperty_AnonymizationSequence));
   ASSERT_EQ(3u, index.IncrementGlobalSequence(GlobalProperty_AnonymizationSequence));
   ASSERT_EQ(4u, index.IncrementGlobalSequence(GlobalProperty_AnonymizationSequence));
+
+  context.Stop();
 }
 
 
@@ -727,8 +728,7 @@
   Toolbox::RemoveFile(path + "/index");
   FilesystemStorage storage(path);
   DatabaseWrapper db;   // The SQLite DB is in memory
-  ServerContext context(db);
-  context.SetStorageArea(storage);
+  ServerContext context(db, storage);
   ServerIndex& index = context.GetIndex();
 
   index.SetMaximumStorageSize(10);
@@ -753,7 +753,7 @@
     std::map<MetadataType, std::string> instanceMetadata;
     ServerIndex::MetadataMap metadata;
     ASSERT_EQ(StoreStatus_Success, index.Store(instanceMetadata, instance, attachments, "", metadata));
-    ASSERT_EQ(2, instanceMetadata.size());
+    ASSERT_EQ(2u, instanceMetadata.size());
     ASSERT_TRUE(instanceMetadata.find(MetadataType_Instance_RemoteAet) != instanceMetadata.end());
     ASSERT_TRUE(instanceMetadata.find(MetadataType_Instance_ReceptionDate) != instanceMetadata.end());
 
@@ -779,4 +779,6 @@
 
   // Because the DB is in memory, the SQLite index must not have been created
   ASSERT_THROW(Toolbox::GetFileSize(path + "/index"), OrthancException);  
+
+  context.Stop();
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UnitTestsSources/StreamTests.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -0,0 +1,329 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "PrecompiledHeadersUnitTests.h"
+#include "gtest/gtest.h"
+
+#include "../Core/Toolbox.h"
+#include "../Core/OrthancException.h"
+#include "../Core/Uuid.h"
+#include "../Core/HttpServer/BufferHttpSender.h"
+#include "../Core/HttpServer/FilesystemHttpSender.h"
+#include "../Core/HttpServer/HttpStreamTranscoder.h"
+#include "../Core/Compression/ZlibCompressor.h"
+#include "../Core/Compression/GzipCompressor.h"
+
+
+using namespace Orthanc;
+
+
+TEST(Gzip, Basic)
+{
+  std::string s = "Hello world";
+ 
+  std::string compressed;
+  GzipCompressor c;
+  ASSERT_FALSE(c.HasPrefixWithUncompressedSize());
+  IBufferCompressor::Compress(compressed, c, s);
+
+  std::string uncompressed;
+  IBufferCompressor::Uncompress(uncompressed, c, compressed);
+  ASSERT_EQ(s.size(), uncompressed.size());
+  ASSERT_EQ(0, memcmp(&s[0], &uncompressed[0], s.size()));
+}
+
+
+TEST(Gzip, Empty)
+{
+  std::string s;
+ 
+  std::string compressed;
+  GzipCompressor c;
+  ASSERT_FALSE(c.HasPrefixWithUncompressedSize());
+  c.SetPrefixWithUncompressedSize(false);
+  IBufferCompressor::Compress(compressed, c, s);
+
+  std::string uncompressed;
+  IBufferCompressor::Uncompress(uncompressed, c, compressed);
+  ASSERT_EQ(0, uncompressed.size());
+}
+
+
+TEST(Gzip, BasicWithPrefix)
+{
+  std::string s = "Hello world";
+ 
+  std::string compressed;
+  GzipCompressor c;
+  c.SetPrefixWithUncompressedSize(true);
+  ASSERT_TRUE(c.HasPrefixWithUncompressedSize());
+  IBufferCompressor::Compress(compressed, c, s);
+
+  std::string uncompressed;
+  IBufferCompressor::Uncompress(uncompressed, c, compressed);
+  ASSERT_EQ(s.size(), uncompressed.size());
+  ASSERT_EQ(0, memcmp(&s[0], &uncompressed[0], s.size()));
+}
+
+
+TEST(Gzip, EmptyWithPrefix)
+{
+  std::string s;
+ 
+  std::string compressed;
+  GzipCompressor c;
+  c.SetPrefixWithUncompressedSize(true);
+  ASSERT_TRUE(c.HasPrefixWithUncompressedSize());
+  IBufferCompressor::Compress(compressed, c, s);
+
+  std::string uncompressed;
+  IBufferCompressor::Uncompress(uncompressed, c, compressed);
+  ASSERT_EQ(0, uncompressed.size());
+}
+
+
+TEST(Zlib, Basic)
+{
+  std::string s = Toolbox::GenerateUuid();
+  s = s + s + s + s;
+ 
+  std::string compressed, compressed2;
+  ZlibCompressor c;
+  ASSERT_TRUE(c.HasPrefixWithUncompressedSize());
+  IBufferCompressor::Compress(compressed, c, s);
+
+  std::string uncompressed;
+  IBufferCompressor::Uncompress(uncompressed, c, compressed);
+  ASSERT_EQ(s.size(), uncompressed.size());
+  ASSERT_EQ(0, memcmp(&s[0], &uncompressed[0], s.size()));
+}
+
+
+TEST(Zlib, Level)
+{
+  std::string s = Toolbox::GenerateUuid();
+  s = s + s + s + s;
+ 
+  std::string compressed, compressed2;
+  ZlibCompressor c;
+  c.SetCompressionLevel(9);
+  IBufferCompressor::Compress(compressed, c, s);
+
+  c.SetCompressionLevel(0);
+  IBufferCompressor::Compress(compressed2, c, s);
+
+  ASSERT_TRUE(compressed.size() < compressed2.size());
+}
+
+
+TEST(Zlib, DISABLED_Corrupted)  // Disabled because it may result in a crash
+{
+  std::string s = Toolbox::GenerateUuid();
+  s = s + s + s + s;
+ 
+  std::string compressed;
+  ZlibCompressor c;
+  IBufferCompressor::Compress(compressed, c, s);
+
+  compressed[compressed.size() - 1] = 'a';
+  std::string u;
+
+  ASSERT_THROW(IBufferCompressor::Uncompress(u, c, compressed), OrthancException);
+}
+
+
+TEST(Zlib, Empty)
+{
+  std::string s = "";
+ 
+  std::string compressed, compressed2;
+  ZlibCompressor c;
+  IBufferCompressor::Compress(compressed, c, s);
+  ASSERT_EQ(compressed, compressed2);
+
+  std::string uncompressed;
+  IBufferCompressor::Uncompress(uncompressed, c, compressed);
+  ASSERT_EQ(0u, uncompressed.size());
+}
+
+
+static bool ReadAllStream(std::string& result,
+                          IHttpStreamAnswer& stream,
+                          bool allowGzip = false,
+                          bool allowDeflate = false)
+{
+  stream.SetupHttpCompression(allowGzip, allowDeflate);
+
+  result.resize(static_cast<size_t>(stream.GetContentLength()));
+
+  size_t pos = 0;
+  while (stream.ReadNextChunk())
+  {
+    size_t s = stream.GetChunkSize();
+    if (pos + s > result.size())
+    {
+      return false;
+    }
+
+    memcpy(&result[pos], stream.GetChunkContent(), s);
+    pos += s;
+  }
+
+  return pos == result.size();
+}
+
+
+TEST(BufferHttpSender, Basic)
+{
+  const std::string s = "Hello world";
+  std::string t;
+
+  {
+    BufferHttpSender sender;
+    sender.SetChunkSize(1);
+    ASSERT_TRUE(ReadAllStream(t, sender));
+    ASSERT_EQ(0u, t.size());
+  }
+
+  for (int cs = 0; cs < 5; cs++)
+  {
+    BufferHttpSender sender;
+    sender.SetChunkSize(cs);
+    sender.GetBuffer() = s;
+    ASSERT_TRUE(ReadAllStream(t, sender));
+    ASSERT_EQ(s, t);
+  }
+}
+
+
+TEST(FilesystemHttpSender, Basic)
+{
+  const std::string& path = "UnitTestsResults/stream";
+  const std::string s = "Hello world";
+  std::string t;
+
+  {
+    Toolbox::WriteFile(s, path);
+    FilesystemHttpSender sender(path);
+    ASSERT_TRUE(ReadAllStream(t, sender));
+    ASSERT_EQ(s, t);
+  }
+
+  {
+    Toolbox::WriteFile("", path);
+    FilesystemHttpSender sender(path);
+    ASSERT_TRUE(ReadAllStream(t, sender));
+    ASSERT_EQ(0u, t.size());
+  }
+}
+
+
+TEST(HttpStreamTranscoder, Basic)
+{
+  ZlibCompressor compressor;
+
+  const std::string s = "Hello world " + Toolbox::GenerateUuid();
+
+  std::string t;
+  IBufferCompressor::Compress(t, compressor, s);
+
+  for (int cs = 0; cs < 5; cs++)
+  {
+    BufferHttpSender sender;
+    sender.SetChunkSize(cs);
+    sender.GetBuffer() = t;
+    std::string u;
+    ASSERT_TRUE(ReadAllStream(u, sender));
+
+    std::string v;
+    IBufferCompressor::Uncompress(v, compressor, u);
+    ASSERT_EQ(s, v);
+  }
+
+  // Pass-through test, no decompression occurs
+  for (int cs = 0; cs < 5; cs++)
+  {
+    BufferHttpSender sender;
+    sender.SetChunkSize(cs);
+    sender.GetBuffer() = t;
+
+    HttpStreamTranscoder transcode(sender, CompressionType_None);
+    
+    std::string u;
+    ASSERT_TRUE(ReadAllStream(u, transcode));
+    
+    ASSERT_EQ(t, u);
+  }
+
+  // Pass-through test, decompression occurs
+  for (int cs = 0; cs < 5; cs++)
+  {
+    BufferHttpSender sender;
+    sender.SetChunkSize(cs);
+    sender.GetBuffer() = t;
+
+    HttpStreamTranscoder transcode(sender, CompressionType_ZlibWithSize);
+    
+    std::string u;
+    ASSERT_TRUE(ReadAllStream(u, transcode, false, false));
+    
+    ASSERT_EQ(s, u);
+  }
+
+  // Pass-through test with zlib, no decompression occurs but deflate is sent
+  for (int cs = 0; cs < 16; cs++)
+  {
+    BufferHttpSender sender;
+    sender.SetChunkSize(cs);
+    sender.GetBuffer() = t;
+
+    HttpStreamTranscoder transcode(sender, CompressionType_ZlibWithSize);
+    
+    std::string u;
+    ASSERT_TRUE(ReadAllStream(u, transcode, false, true));
+    
+    ASSERT_EQ(t.size() - sizeof(uint64_t), u.size());
+    ASSERT_EQ(t.substr(sizeof(uint64_t)), u);
+  }
+
+  for (int cs = 0; cs < 3; cs++)
+  {
+    BufferHttpSender sender;
+    sender.SetChunkSize(cs);
+
+    HttpStreamTranscoder transcode(sender, CompressionType_ZlibWithSize);
+    std::string u;
+    ASSERT_TRUE(ReadAllStream(u, transcode, false, true));
+    
+    ASSERT_EQ(0u, u.size());
+  }
+}
--- a/UnitTestsSources/UnitTestsMain.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/UnitTestsSources/UnitTestsMain.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -37,14 +37,15 @@
 
 #include <ctype.h>
 
-#include "../Core/Compression/ZlibCompressor.h"
 #include "../Core/DicomFormat/DicomTag.h"
-#include "../Core/HttpServer/HttpHandler.h"
+#include "../Core/HttpServer/HttpToolbox.h"
+#include "../Core/Logging.h"
 #include "../Core/OrthancException.h"
 #include "../Core/Toolbox.h"
 #include "../Core/Uuid.h"
 #include "../OrthancServer/OrthancInitialization.h"
 
+
 using namespace Orthanc;
 
 
@@ -77,6 +78,17 @@
   ASSERT_FALSE(Toolbox::IsSHA1("012345678901234567890123456789012345678901234"));
   ASSERT_TRUE(Toolbox::IsSHA1("b5ed549f-956400ce-69a8c063-bf5b78be-2732a4b9"));
 
+  std::string sha = "         b5ed549f-956400ce-69a8c063-bf5b78be-2732a4b9          ";
+  ASSERT_TRUE(Toolbox::IsSHA1(sha));
+  sha[3] = '\0';
+  sha[53] = '\0';
+  ASSERT_TRUE(Toolbox::IsSHA1(sha));
+  sha[40] = '\0';
+  ASSERT_FALSE(Toolbox::IsSHA1(sha));
+  ASSERT_FALSE(Toolbox::IsSHA1("       "));
+
+  ASSERT_TRUE(Toolbox::IsSHA1("16738bc3-e47ed42a-43ce044c-a3414a45-cb069bd0"));
+
   std::string s;
   Toolbox::ComputeSHA1(s, "The quick brown fox jumps over the lazy dog");
   ASSERT_TRUE(Toolbox::IsSHA1(s));
@@ -85,99 +97,15 @@
   ASSERT_FALSE(Toolbox::IsSHA1("b5ed549f-956400ce-69a8c063-bf5b78be-2732a4b_"));
 }
 
-static void StringToVector(std::vector<uint8_t>& v,
-                           const std::string& s)
-{
-  v.resize(s.size());
-  for (size_t i = 0; i < s.size(); i++)
-    v[i] = s[i];
-}
-
-
-TEST(Zlib, Basic)
-{
-  std::string s = Toolbox::GenerateUuid();
-  s = s + s + s + s;
- 
-  std::string compressed, compressed2;
-  ZlibCompressor c;
-  c.Compress(compressed, s);
-
-  std::vector<uint8_t> v, vv;
-  StringToVector(v, s);
-  c.Compress(compressed2, v);
-  ASSERT_EQ(compressed, compressed2);
-
-  std::string uncompressed;
-  c.Uncompress(uncompressed, compressed);
-  ASSERT_EQ(s.size(), uncompressed.size());
-  ASSERT_EQ(0, memcmp(&s[0], &uncompressed[0], s.size()));
-
-  StringToVector(vv, compressed);
-  c.Uncompress(uncompressed, vv);
-  ASSERT_EQ(s.size(), uncompressed.size());
-  ASSERT_EQ(0, memcmp(&s[0], &uncompressed[0], s.size()));
-}
-
-
-TEST(Zlib, Level)
-{
-  std::string s = Toolbox::GenerateUuid();
-  s = s + s + s + s;
- 
-  std::string compressed, compressed2;
-  ZlibCompressor c;
-  c.SetCompressionLevel(9);
-  c.Compress(compressed, s);
-
-  c.SetCompressionLevel(0);
-  c.Compress(compressed2, s);
-
-  ASSERT_TRUE(compressed.size() < compressed2.size());
-}
-
-
-TEST(Zlib, DISABLED_Corrupted)  // Disabled because it may result in a crash
-{
-  std::string s = Toolbox::GenerateUuid();
-  s = s + s + s + s;
- 
-  std::string compressed;
-  ZlibCompressor c;
-  c.Compress(compressed, s);
-
-  compressed[compressed.size() - 1] = 'a';
-  std::string u;
-
-  ASSERT_THROW(c.Uncompress(u, compressed), OrthancException);
-}
-
-
-TEST(Zlib, Empty)
-{
-  std::string s = "";
-  std::vector<uint8_t> v, vv;
- 
-  std::string compressed, compressed2;
-  ZlibCompressor c;
-  c.Compress(compressed, s);
-  c.Compress(compressed2, v);
-  ASSERT_EQ(compressed, compressed2);
-
-  std::string uncompressed;
-  c.Uncompress(uncompressed, compressed);
-  ASSERT_EQ(0u, uncompressed.size());
-
-  StringToVector(vv, compressed);
-  c.Uncompress(uncompressed, vv);
-  ASSERT_EQ(0u, uncompressed.size());
-}
-
 
 TEST(ParseGetArguments, Basic)
 {
-  HttpHandler::Arguments a;
-  HttpHandler::ParseGetArguments(a, "aaa=baaa&bb=a&aa=c");
+  IHttpHandler::GetArguments b;
+  HttpToolbox::ParseGetArguments(b, "aaa=baaa&bb=a&aa=c");
+
+  IHttpHandler::Arguments a;
+  HttpToolbox::CompileGetArguments(a, b);
+
   ASSERT_EQ(3u, a.size());
   ASSERT_EQ(a["aaa"], "baaa");
   ASSERT_EQ(a["bb"], "a");
@@ -186,8 +114,12 @@
 
 TEST(ParseGetArguments, BasicEmpty)
 {
-  HttpHandler::Arguments a;
-  HttpHandler::ParseGetArguments(a, "aaa&bb=aa&aa");
+  IHttpHandler::GetArguments b;
+  HttpToolbox::ParseGetArguments(b, "aaa&bb=aa&aa");
+
+  IHttpHandler::Arguments a;
+  HttpToolbox::CompileGetArguments(a, b);
+
   ASSERT_EQ(3u, a.size());
   ASSERT_EQ(a["aaa"], "");
   ASSERT_EQ(a["bb"], "aa");
@@ -196,16 +128,24 @@
 
 TEST(ParseGetArguments, Single)
 {
-  HttpHandler::Arguments a;
-  HttpHandler::ParseGetArguments(a, "aaa=baaa");
+  IHttpHandler::GetArguments b;
+  HttpToolbox::ParseGetArguments(b, "aaa=baaa");
+
+  IHttpHandler::Arguments a;
+  HttpToolbox::CompileGetArguments(a, b);
+
   ASSERT_EQ(1u, a.size());
   ASSERT_EQ(a["aaa"], "baaa");
 }
 
 TEST(ParseGetArguments, SingleEmpty)
 {
-  HttpHandler::Arguments a;
-  HttpHandler::ParseGetArguments(a, "aaa");
+  IHttpHandler::GetArguments b;
+  HttpToolbox::ParseGetArguments(b, "aaa");
+
+  IHttpHandler::Arguments a;
+  HttpToolbox::CompileGetArguments(a, b);
+
   ASSERT_EQ(1u, a.size());
   ASSERT_EQ(a["aaa"], "");
 }
@@ -213,8 +153,12 @@
 TEST(ParseGetQuery, Test1)
 {
   UriComponents uri;
-  HttpHandler::Arguments a;
-  HttpHandler::ParseGetQuery(uri, a, "/instances/test/world?aaa=baaa&bb=a&aa=c");
+  IHttpHandler::GetArguments b;
+  HttpToolbox::ParseGetQuery(uri, b, "/instances/test/world?aaa=baaa&bb=a&aa=c");
+
+  IHttpHandler::Arguments a;
+  HttpToolbox::CompileGetArguments(a, b);
+
   ASSERT_EQ(3u, uri.size());
   ASSERT_EQ("instances", uri[0]);
   ASSERT_EQ("test", uri[1]);
@@ -228,8 +172,12 @@
 TEST(ParseGetQuery, Test2)
 {
   UriComponents uri;
-  HttpHandler::Arguments a;
-  HttpHandler::ParseGetQuery(uri, a, "/instances/test/world");
+  IHttpHandler::GetArguments b;
+  HttpToolbox::ParseGetQuery(uri, b, "/instances/test/world");
+
+  IHttpHandler::Arguments a;
+  HttpToolbox::CompileGetArguments(a, b);
+
   ASSERT_EQ(3u, uri.size());
   ASSERT_EQ("instances", uri[0]);
   ASSERT_EQ("test", uri[1]);
@@ -452,8 +400,6 @@
 }
 
 
-#include <glog/logging.h>
-
 TEST(Logger, Basic)
 {
   LOG(INFO) << "I say hello";
@@ -576,6 +522,13 @@
   RegisterUserMetadata(2047, "Ceci est un test");
   ASSERT_EQ(2047, StringToMetadata("2047"));
   ASSERT_EQ(2047, StringToMetadata("Ceci est un test"));
+
+  ASSERT_STREQ("Generic", EnumerationToString(StringToModalityManufacturer("Generic")));
+  ASSERT_STREQ("StoreScp", EnumerationToString(StringToModalityManufacturer("StoreScp")));
+  ASSERT_STREQ("ClearCanvas", EnumerationToString(StringToModalityManufacturer("ClearCanvas")));
+  ASSERT_STREQ("MedInria", EnumerationToString(StringToModalityManufacturer("MedInria")));
+  ASSERT_STREQ("Dcm4Chee", EnumerationToString(StringToModalityManufacturer("Dcm4Chee")));
+  ASSERT_STREQ("SyngoVia", EnumerationToString(StringToModalityManufacturer("SyngoVia")));
 }
 
 
@@ -624,15 +577,15 @@
   std::vector<std::string> t;
   
   Toolbox::TokenizeString(t, "", ','); 
-  ASSERT_EQ(1, t.size());
+  ASSERT_EQ(1u, t.size());
   ASSERT_EQ("", t[0]);
   
   Toolbox::TokenizeString(t, "abc", ','); 
-  ASSERT_EQ(1, t.size());
+  ASSERT_EQ(1u, t.size());
   ASSERT_EQ("abc", t[0]);
   
   Toolbox::TokenizeString(t, "ab,cd,ef,", ','); 
-  ASSERT_EQ(4, t.size());
+  ASSERT_EQ(4u, t.size());
   ASSERT_EQ("ab", t[0]);
   ASSERT_EQ("cd", t[1]);
   ASSERT_EQ("ef", t[2]);
@@ -668,19 +621,29 @@
 
 #if defined(__linux)
 #include <endian.h>
+#elif defined(__FreeBSD__)
+#include <machine/endian.h>
 #endif
 
+
 TEST(Toolbox, Endianness)
 {
   // Parts of this test come from Adam Conrad
   // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=728822#5
 
-#if defined(_WIN32)
+
+  /**
+   * Windows and OS X are assumed to always little-endian.
+   **/
+  
+#if defined(_WIN32) || defined(__APPLE__)
   ASSERT_EQ(Endianness_Little, Toolbox::DetectEndianness());
 
-#elif defined(__APPLE__)
-  ASSERT_EQ(Endianness_Little, Toolbox::DetectEndianness());
 
+  /**
+   * Linux.
+   **/
+  
 #elif defined(__linux) || defined(__FreeBSD_kernel__)
 
 #if !defined(__BYTE_ORDER)
@@ -693,6 +656,18 @@
   ASSERT_EQ(Endianness_Little, Toolbox::DetectEndianness());
 #  endif
 
+  
+  /**
+   * FreeBSD.
+   **/
+  
+#elif defined(__FreeBSD__)
+#  if _BYTE_ORDER == _BIG_ENDIAN
+   ASSERT_EQ(Endianness_Big, Toolbox::DetectEndianness());
+#  else // _LITTLE_ENDIAN
+   ASSERT_EQ(Endianness_Little, Toolbox::DetectEndianness());
+#  endif
+
 #else
 #error Support your platform here
 #endif
@@ -742,24 +717,31 @@
 }
 
 
+TEST(Toolbox, StartsWith)
+{
+  ASSERT_TRUE(Toolbox::StartsWith("hello world", ""));
+  ASSERT_TRUE(Toolbox::StartsWith("hello world", "hello"));
+  ASSERT_TRUE(Toolbox::StartsWith("hello world", "h"));
+  ASSERT_FALSE(Toolbox::StartsWith("hello world", "H"));
+  ASSERT_FALSE(Toolbox::StartsWith("h", "hello"));
+  ASSERT_TRUE(Toolbox::StartsWith("h", "h"));
+  ASSERT_FALSE(Toolbox::StartsWith("", "h"));
+}
+
+
 int main(int argc, char **argv)
 {
-  // Initialize Google's logging library.
-  FLAGS_logtostderr = true;
-  FLAGS_minloglevel = 0;
-
-  // Go to trace-level verbosity
-  //FLAGS_v = 1;
+  Logging::Initialize();
+  Logging::EnableInfoLevel(true);
+  Toolbox::DetectEndianness();
+  Toolbox::MakeDirectory("UnitTestsResults");
+  OrthancInitialize();
 
-  Toolbox::DetectEndianness();
-
-  google::InitGoogleLogging("Orthanc");
-
-  Toolbox::CreateDirectory("UnitTestsResults");
-
-  OrthancInitialize();
   ::testing::InitGoogleTest(&argc, argv);
   int result = RUN_ALL_TESTS();
+
   OrthancFinalize();
+  Logging::Finalize();
+
   return result;
 }
--- a/UnitTestsSources/VersionsTests.cpp	Wed Feb 11 10:40:08 2015 +0100
+++ b/UnitTestsSources/VersionsTests.cpp	Wed Sep 30 13:23:31 2015 +0200
@@ -42,7 +42,11 @@
 #include <boost/version.hpp>
 #include <sqlite3.h>
 #include <lua.h>
+#include <jpeglib.h>
+
+#if ORTHANC_SSL_ENABLED == 1
 #include <openssl/opensslv.h>
+#endif
 
 
 TEST(Versions, Zlib)
@@ -59,15 +63,21 @@
 TEST(Versions, Png)
 {
   ASSERT_EQ(PNG_LIBPNG_VER_MAJOR * 10000 + PNG_LIBPNG_VER_MINOR * 100 + PNG_LIBPNG_VER_RELEASE,
-            png_access_version_number());
+            static_cast<int>(png_access_version_number()));
 }
 
 TEST(Versions, SQLite)
 {
+#if defined(__APPLE__)
+  // Under OS X, there might exist minor differences between the
+  // version of the headers and the version of the library, for the
+  // system-wide SQLite. Ignore these differences.
+#else
   // http://www.sqlite.org/capi3ref.html#sqlite3_libversion
-  assert(sqlite3_libversion_number() == SQLITE_VERSION_NUMBER );
-  assert(strcmp(sqlite3_sourceid(), SQLITE_SOURCE_ID) == 0);
-  assert(strcmp(sqlite3_libversion(), SQLITE_VERSION) == 0);
+  EXPECT_EQ(sqlite3_libversion_number(), SQLITE_VERSION_NUMBER);
+  EXPECT_STREQ(sqlite3_sourceid(), SQLITE_SOURCE_ID);
+  EXPECT_STREQ(sqlite3_libversion(), SQLITE_VERSION);
+#endif
 
   // Ensure that the SQLite version is above 3.7.0.
   // "sqlite3_create_function_v2" is not defined in previous versions.
@@ -84,6 +94,7 @@
 
 
 #if ORTHANC_STATIC == 1
+
 TEST(Versions, ZlibStatic)
 {
   ASSERT_STREQ("1.2.7", zlibVersion());
@@ -91,13 +102,13 @@
 
 TEST(Versions, BoostStatic)
 {
-  ASSERT_STREQ("1_55", BOOST_LIB_VERSION);
+  ASSERT_STREQ("1_58", BOOST_LIB_VERSION);
 }
 
 TEST(Versions, CurlStatic)
 {
   curl_version_info_data* v = curl_version_info(CURLVERSION_NOW);
-  ASSERT_STREQ("7.26.0", v->version);
+  ASSERT_STREQ("7.44.0", v->version);
 }
 
 TEST(Versions, PngStatic)
@@ -106,12 +117,18 @@
   ASSERT_STREQ("1.5.12", PNG_LIBPNG_VER_STRING);
 }
 
+TEST(Versions, JpegStatic)
+{
+  ASSERT_EQ(9, JPEG_LIB_VERSION_MAJOR);
+  ASSERT_EQ(1, JPEG_LIB_VERSION_MINOR);
+}
+
 TEST(Versions, CurlSslStatic)
 {
   curl_version_info_data * vinfo = curl_version_info(CURLVERSION_NOW);
 
   // Check that SSL support is enabled when required
-  bool curlSupportsSsl = vinfo->features & CURL_VERSION_SSL;
+  bool curlSupportsSsl = (vinfo->features & CURL_VERSION_SSL) != 0;
 
 #if ORTHANC_SSL_ENABLED == 0
   ASSERT_FALSE(curlSupportsSsl);
@@ -125,9 +142,20 @@
   ASSERT_STREQ("Lua 5.1.5", LUA_RELEASE);
 }
 
+
+#if ORTHANC_SSL_ENABLED == 1
 TEST(Version, OpenSslStatic)
 {
-  ASSERT_EQ(0x1000107fL /* openssl-1.0.1g */, OPENSSL_VERSION_NUMBER);
+  ASSERT_EQ(0x1000204fL /* openssl-1.0.2d */, OPENSSL_VERSION_NUMBER);
+}
+#endif
+
+
+#include <json/version.h>
+
+TEST(Version, JsonCpp)
+{
+  ASSERT_STREQ("0.10.5", JSONCPP_VERSION_STRING);
 }
 
 #endif