changeset 98:f4d23ad4aa27

Common: refactoring class mlk::size 1. Make uniform size representation in JSON format: { "width": 100, "height": 200 } The Javascript API was using this form while an array was used in loaders. 2. Create brand new functions in util::json to parse mlk::size objects, Updated util::json::require_(u)int as well. 3. Add new tests for mlk::size object, 4. Get rid of utilities in loader as they are in util::json instead.
author David Demelier <markand@malikania.fr>
date Tue, 04 Jul 2017 13:23:15 +0200
parents 7377a3d8600d
children 0addfab87b17
files docs/CMakeLists.txt docs/specs/size.md examples/animation/main.cpp examples/animation/resources/sprites/margins.json examples/font/main.cpp examples/image/main.cpp examples/js-animation/main.cpp examples/js-animation/resources/sprites/margins.json examples/js-font/main.cpp examples/js-image/main.cpp examples/js-sprite/main.cpp examples/js-sprite/resources/sprites/margins.json examples/sprite/main.cpp examples/sprite/resources/sprites/margins.json libclient-js/CMakeLists.txt libclient-js/malikania/js_image.cpp libclient-js/malikania/js_size.cpp libclient-js/malikania/js_size.hpp libclient/CMakeLists.txt libclient/malikania/client/backend/sdl/font_backend.cpp libclient/malikania/client/backend/sdl/font_backend.hpp libclient/malikania/client/backend/sdl/image_backend.cpp libclient/malikania/client/backend/sdl/image_backend.hpp libclient/malikania/client/button.cpp libclient/malikania/client/button.hpp libclient/malikania/client/font.cpp libclient/malikania/client/font.hpp libclient/malikania/client/frame.cpp libclient/malikania/client/frame.hpp libclient/malikania/client/image.cpp libclient/malikania/client/image.hpp libclient/malikania/client/label.cpp libclient/malikania/client/label.hpp libclient/malikania/client/layout.hpp libclient/malikania/client/loader.cpp libclient/malikania/client/loader.hpp libclient/malikania/client/size.hpp libclient/malikania/client/sprite.cpp libclient/malikania/client/sprite.hpp libclient/malikania/client/unique_layout.cpp libclient/malikania/client/unique_layout.hpp libclient/malikania/client/widget.cpp libclient/malikania/client/widget.hpp libcommon-js/CMakeLists.txt libcommon-js/malikania/js_size.cpp libcommon-js/malikania/js_size.hpp libcommon/CMakeLists.txt libcommon/malikania/loader.cpp libcommon/malikania/loader.hpp libcommon/malikania/size.hpp libcommon/malikania/util.cpp libcommon/malikania/util.hpp tests/libclient/CMakeLists.txt tests/libclient/js-size/CMakeLists.txt tests/libclient/js-size/main.cpp tests/libclient/size/CMakeLists.txt tests/libclient/size/main.cpp tests/libcommon/CMakeLists.txt tests/libcommon/js-size/CMakeLists.txt tests/libcommon/js-size/main.cpp tests/libcommon/size/CMakeLists.txt tests/libcommon/size/main.cpp tests/libcommon/util/main.cpp
diffstat 63 files changed, 1324 insertions(+), 1136 deletions(-) [+]
line wrap: on
line diff
--- a/docs/CMakeLists.txt	Fri Jun 16 12:38:04 2017 +0200
+++ b/docs/CMakeLists.txt	Tue Jul 04 13:23:15 2017 +0200
@@ -28,6 +28,7 @@
     ALL
     COMMENT "Generating documentation"
     SOURCES
+        ${docs_SOURCE_DIR}/specs/size.md
         ${docs_SOURCE_DIR}/specs/sprite.md
 )
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/specs/size.md	Tue Jul 04 13:23:15 2017 +0200
@@ -0,0 +1,19 @@
+# Size
+
+A size describe an object which has width and height dimensions.
+
+## JSON Specification
+
+### Synopsis
+
+````json
+{
+    "width": 800,
+    "height: 600
+}
+````
+
+### Properties
+
+  - **width**: width as positive int,
+  - **height**: height as positive int.
--- a/examples/animation/main.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/examples/animation/main.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -17,6 +17,7 @@
  */
 
 #include <chrono>
+#include <iostream>
 #include <thread>
 #include <exception>
 
--- a/examples/animation/resources/sprites/margins.json	Fri Jun 16 12:38:04 2017 +0200
+++ b/examples/animation/resources/sprites/margins.json	Tue Jul 04 13:23:15 2017 +0200
@@ -1,6 +1,15 @@
 {
-  "image": "images/margins.png",
-  "cell": [ 32, 32 ],
-  "margin": [ 4, 6 ],
-  "space": [ 2, 3 ]
+    "image": "images/margins.png",
+    "cell": {
+        "width": 32,
+        "height": 32
+    },
+    "margin": {
+        "width": 4,
+        "height": 6
+    },
+    "space": {
+        "width": 2,
+        "height": 3
+    }
 }
--- a/examples/font/main.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/examples/font/main.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -17,6 +17,7 @@
  */
 
 #include <chrono>
+#include <iostream>
 #include <thread>
 
 #include <malikania/client/loader.hpp>
--- a/examples/image/main.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/examples/image/main.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -17,6 +17,7 @@
  */
 
 #include <chrono>
+#include <iostream>
 #include <thread>
 
 #include <malikania/client/loader.hpp>
--- a/examples/js-animation/main.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/examples/js-animation/main.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -17,6 +17,7 @@
  */
 
 #include <chrono>
+#include <iostream>
 #include <thread>
 
 #include <malikania/js_client_resources_loader.hpp>
--- a/examples/js-animation/resources/sprites/margins.json	Fri Jun 16 12:38:04 2017 +0200
+++ b/examples/js-animation/resources/sprites/margins.json	Tue Jul 04 13:23:15 2017 +0200
@@ -1,6 +1,15 @@
 {
-  "image": "images/margins.png",
-  "cell": [ 32, 32 ],
-  "margin": [ 4, 6 ],
-  "space": [ 2, 3 ]
+    "image": "images/margins.png",
+    "cell": {
+        "width": 32,
+        "height": 32
+    },
+    "margin": {
+        "width": 4,
+        "height": 6
+    },
+    "space": {
+        "width": 2,
+        "height": 3
+    }
 }
--- a/examples/js-font/main.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/examples/js-font/main.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -17,6 +17,7 @@
  */
 
 #include <chrono>
+#include <iostream>
 #include <thread>
 
 #include <malikania/js_client_resources_loader.hpp>
--- a/examples/js-image/main.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/examples/js-image/main.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -17,6 +17,7 @@
  */
 
 #include <chrono>
+#include <iostream>
 #include <thread>
 
 #include <malikania/js_client_resources_loader.hpp>
--- a/examples/js-sprite/main.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/examples/js-sprite/main.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -17,6 +17,7 @@
  */
 
 #include <chrono>
+#include <iostream>
 #include <thread>
 
 #include <malikania/js_client_resources_loader.hpp>
--- a/examples/js-sprite/resources/sprites/margins.json	Fri Jun 16 12:38:04 2017 +0200
+++ b/examples/js-sprite/resources/sprites/margins.json	Tue Jul 04 13:23:15 2017 +0200
@@ -1,6 +1,15 @@
 {
-  "image": "images/margins.png",
-  "cell": [ 32, 32 ],
-  "margin": [ 4, 6 ],
-  "space": [ 2, 3 ]
+    "image": "images/margins.png",
+    "cell": {
+        "width": 32,
+        "height": 32
+    },
+    "margin": {
+        "width": 4,
+        "height": 6
+    },
+    "space": {
+        "width": 2,
+        "height": 3
+    }
 }
--- a/examples/sprite/main.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/examples/sprite/main.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -17,6 +17,7 @@
  */
 
 #include <chrono>
+#include <iostream>
 #include <thread>
 #include <exception>
 
--- a/examples/sprite/resources/sprites/margins.json	Fri Jun 16 12:38:04 2017 +0200
+++ b/examples/sprite/resources/sprites/margins.json	Tue Jul 04 13:23:15 2017 +0200
@@ -1,6 +1,15 @@
 {
-  "image": "images/margins.png",
-  "cell": [ 32, 32 ],
-  "margin": [ 4, 6 ],
-  "space": [ 2, 3 ]
+    "image": "images/margins.png",
+    "cell": {
+        "width": 32,
+        "height": 32
+    },
+    "margin": {
+        "width": 4,
+        "height": 6
+    },
+    "space": {
+        "width": 2,
+        "height": 3
+    }
 }
--- a/libclient-js/CMakeLists.txt	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient-js/CMakeLists.txt	Tue Jul 04 13:23:15 2017 +0200
@@ -45,7 +45,6 @@
     ${libmlk-client-js_SOURCE_DIR}/malikania/js_point.cpp
     ${libmlk-client-js_SOURCE_DIR}/malikania/js_rectangle.cpp
     ${libmlk-client-js_SOURCE_DIR}/malikania/js_sprite.cpp
-    ${libmlk-client-js_SOURCE_DIR}/malikania/js_size.cpp
     ${libmlk-client-js_SOURCE_DIR}/malikania/js_window.cpp
 )
 
--- a/libclient-js/malikania/js_image.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient-js/malikania/js_image.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -16,6 +16,8 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <cassert>
+
 #include "js_client_resources_loader.hpp"
 #include "js_image.hpp"
 #include "js_point.hpp"
--- a/libclient-js/malikania/js_size.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,146 +0,0 @@
-/*
- * js_size.cpp -- size description (JavaScript binding)
- *
- * Copyright (c) 2013-2017 Malikania Authors
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <cassert>
-
-#include "js_size.hpp"
-
-namespace mlk {
-
-namespace {
-
-client::size parse(duk_context* ctx, duk_idx_t index, bool required, client::size ret = {})
-{
-    dukx_stack_assert sa(ctx);
-
-    if (duk_is_object(ctx, index)) {
-        if (required && !duk_has_prop_string(ctx, index, "width")) {
-            duk_error(ctx, DUK_ERR_ERROR, "missing width property in size description");
-        } else if (required && !duk_has_prop_string(ctx, index, "height")) {
-            duk_error(ctx, DUK_ERR_ERROR, "missing height property in size description");
-        }
-
-        int width;
-        int height;
-
-        duk_get_prop_string(ctx, index, "width");
-        width = duk_to_int(ctx, -1);
-        duk_pop(ctx);
-        duk_get_prop_string(ctx, index, "height");
-        height = duk_to_int(ctx, -1);
-        duk_pop(ctx);
-
-        if (width < 0) {
-            duk_error(ctx, DUK_ERR_RANGE_ERROR, "width can not be negative");
-        }
-        if (height < 0) {
-            duk_error(ctx, DUK_ERR_RANGE_ERROR, "height can not be negative");
-        }
-
-        ret = client::size(width, height);
-    } else if (required) {
-        duk_error(ctx, DUK_ERR_TYPE_ERROR, "size object expected");
-    }
-
-    return ret;
-}
-
-duk_ret_t constructor(duk_context* ctx)
-{
-    client::size obj;
-
-    if (duk_get_top(ctx) == 2) {
-        int width;
-        int height;
-
-        if ((width = duk_require_int(ctx, 0)) < 0) {
-            duk_error(ctx, DUK_ERR_RANGE_ERROR, "argument #0 can not be negative");
-        }
-        if ((height = duk_require_int(ctx, 1)) < 0) {
-            duk_error(ctx, DUK_ERR_RANGE_ERROR, "argument #1 can not be negative");
-        }
-
-        obj = client::size(static_cast<unsigned>(width), static_cast<unsigned>(height));
-    } else if (duk_get_top(ctx) == 1) {
-        obj = parse(ctx, 0, true);
-    }
-
-    duk_ret_t ret;
-
-    // Allow both constructor and non constructor calls.
-    if (duk_is_constructor_call(ctx)) {
-        duk_push_this(ctx);
-        dukx_put_size(ctx, obj);
-        duk_pop(ctx);
-        ret = 0;
-    } else {
-        dukx_push_size(ctx, obj);
-        ret = 1;
-    }
-
-    return ret;
-}
-
-} // !namespace
-
-client::size dukx_get_size(duk_context* ctx, duk_idx_t index)
-{
-    return parse(ctx, index, false);
-}
-
-client::size dukx_require_size(duk_context* ctx, duk_idx_t index)
-{
-    return parse(ctx, index, true);
-}
-
-client::size dukx_optional_size(duk_context* ctx, duk_idx_t index, client::size def)
-{
-    return parse(ctx, index, false, std::move(def));
-}
-
-void dukx_push_size(duk_context* ctx, const client::size& size)
-{
-    dukx_stack_assert sa(ctx, 1);
-
-    duk_push_object(ctx);
-    dukx_put_size(ctx, size);
-}
-
-void dukx_put_size(duk_context* ctx, const client::size& size)
-{
-    assert(duk_is_object(ctx, -1));
-
-    dukx_stack_assert sa(ctx, 0);
-
-    duk_push_uint(ctx, size.width());
-    duk_put_prop_string(ctx, -2, "width");
-    duk_push_uint(ctx, size.height());
-    duk_put_prop_string(ctx, -2, "height");
-}
-
-void dukx_load_size(duk_context* ctx)
-{
-    dukx_stack_assert sa(ctx, 0);
-
-    duk_get_global_string(ctx, "Malikania");
-    duk_push_c_function(ctx, constructor, DUK_VARARGS);
-    duk_put_prop_string(ctx, -2, "Size");
-    duk_pop(ctx);
-}
-
-} // !mlk
--- a/libclient-js/malikania/js_size.hpp	Fri Jun 16 12:38:04 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-/*
- * js_size.hpp -- size description (JavaScript binding)
- *
- * Copyright (c) 2013-2017 Malikania Authors
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MALIKANIA_JS_SIZE_HPP
-#define MALIKANIA_JS_SIZE_HPP
-
-/**
- * \file js_size.hpp
- * \brief JavaScript binding for size.
- *
- * size are plain objects.
- *
- * ````
- * {
- *   width: 1000,
- *   height: 2000
- * }
- * ````
- */
-
-#include <malikania/duktape.hpp>
-#include <malikania/client/size.hpp>
-
-namespace mlk {
-
-/**
- * Get a size.
- *
- * The size may be adjusted if any values are incorrect.
- *
- * \param ctx the context
- * \param index the value index
- * \return the size
- */
-client::size dukx_get_size(duk_context* ctx, duk_idx_t index);
-
-/**
- * Require a size
- *
- * If the object is not a size, raise a JavaScript error.
- *
- * \param ctx the context
- * \param index the index
- * \return the size
- */
-client::size dukx_require_size(duk_context* ctx, duk_idx_t index);
-
-/**
- * Like get but return the default value if the value at the given index is not an object.
- *
- * \param ctx the context
- * \param index the idnex
- * \param def the default value
- * \return the size
- */
-client::size dukx_optional_size(duk_context* ctx, duk_idx_t index, client::size def);
-
-/**
- * Push the size as object.
- *
- * \param ctx the context
- * \param size the size
- */
-void dukx_push_size(duk_context* ctx, const client::size& size);
-
-/**
- * Put the size properties into the object at the top of the stack.
- *
- * \param ctx the context
- * \param size the size
- */
-void dukx_put_size(duk_context* ctx, const client::size& size);
-
-void dukx_load_size(duk_context* ctx);
-
-} // !mlk
-
-#endif // !MALIKANIA_JS_SIZE_HPP
--- a/libclient/CMakeLists.txt	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/CMakeLists.txt	Tue Jul 04 13:23:15 2017 +0200
@@ -37,7 +37,6 @@
     ${libmlk-client_SOURCE_DIR}/malikania/client/mouse.hpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/point.hpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/rectangle.hpp
-    ${libmlk-client_SOURCE_DIR}/malikania/client/size.hpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/sprite.hpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/theme.hpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/unique_layout.hpp
--- a/libclient/malikania/client/backend/sdl/font_backend.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/backend/sdl/font_backend.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -52,7 +52,7 @@
         throw std::runtime_error(SDL_GetError());
     }
 
-    return mlk::client::size(static_cast<unsigned>(width), static_cast<unsigned>(height));
+    return mlk::size(static_cast<unsigned>(width), static_cast<unsigned>(height));
 }
 
 } // !client
--- a/libclient/malikania/client/backend/sdl/font_backend.hpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/backend/sdl/font_backend.hpp	Tue Jul 04 13:23:15 2017 +0200
@@ -31,7 +31,6 @@
 namespace client {
 
 class font;
-class size;
 
 class font::backend_impl {
 private:
@@ -45,7 +44,7 @@
         return m_font.get();
     }
 
-    mlk::client::size clip(const mlk::client::font& self, const std::string& text) const;
+    mlk::size clip(const mlk::client::font& self, const std::string& text) const;
 };
 
 } // !client
--- a/libclient/malikania/client/backend/sdl/image_backend.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/backend/sdl/image_backend.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -57,7 +57,7 @@
         throw std::runtime_error(SDL_GetError());
     }
 
-    m_size = mlk::client::size(
+    m_size = mlk::size(
         static_cast<unsigned>(m_surface->w),
         static_cast<unsigned>(m_surface->h)
     );
--- a/libclient/malikania/client/backend/sdl/image_backend.hpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/backend/sdl/image_backend.hpp	Tue Jul 04 13:23:15 2017 +0200
@@ -40,7 +40,7 @@
     std::unique_ptr<SDL_Surface, void (*)(SDL_Surface *)> m_surface;
     std::unique_ptr<SDL_Texture, void (*)(SDL_Texture *)> m_texture;
 
-    mlk::client::size m_size;
+    mlk::size m_size;
 
     void create_texture(window& window);
 
@@ -52,7 +52,7 @@
         return m_texture.get();
     }
 
-    inline const mlk::client::size& size() const noexcept
+    inline const mlk::size& size() const noexcept
     {
         return m_size;
     }
--- a/libclient/malikania/client/button.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/button.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -41,7 +41,7 @@
     w.theme().draw_button(w, *this, rect);
 }
 
-mlk::client::size button::size(window& w) const noexcept
+mlk::size button::size(window& w) const noexcept
 {
     return w.theme().size_button(*this);
 }
--- a/libclient/malikania/client/button.hpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/button.hpp	Tue Jul 04 13:23:15 2017 +0200
@@ -91,7 +91,7 @@
     /**
      * \copydoc widget::size
      */
-    mlk::client::size size(window& w) const noexcept override;
+    mlk::size size(window& w) const noexcept override;
 };
 
 } // !client
--- a/libclient/malikania/client/font.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/font.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -32,7 +32,7 @@
 
 font::~font() noexcept = default;
 
-size font::clip(const std::string& text) const
+mlk::size font::clip(const std::string& text) const
 {
     return m_backend->clip(*this, text);
 }
--- a/libclient/malikania/client/font.hpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/font.hpp	Tue Jul 04 13:23:15 2017 +0200
@@ -100,7 +100,7 @@
      * \param text the text to clip
      * \return the required size
      */
-    mlk::client::size clip(const std::string& text) const;
+    mlk::size clip(const std::string& text) const;
 
     /**
      * Default move assignment operator.
--- a/libclient/malikania/client/frame.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/frame.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -27,9 +27,9 @@
 
 namespace client {
 
-mlk::client::size frame::size(window &win)
+mlk::size frame::size(window &win)
 {
-    return m_layout ? m_layout->size(win) : mlk::client::size();
+    return m_layout ? m_layout->size(win) : mlk::size();
 }
 
 void frame::draw(window& win)
--- a/libclient/malikania/client/frame.hpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/frame.hpp	Tue Jul 04 13:23:15 2017 +0200
@@ -44,7 +44,7 @@
 private:
     std::shared_ptr<mlk::client::layout> m_layout;
     mlk::client::point m_position;
-    mlk::client::size m_padding{10, 10};
+    mlk::size m_padding{10, 10};
 
 public:
     /**
@@ -117,7 +117,7 @@
      *
      * \return the frame padding
      */
-    inline const mlk::client::size& padding() const noexcept
+    inline const mlk::size& padding() const noexcept
     {
         return m_padding;
     }
@@ -127,7 +127,7 @@
      *
      * \return the padding
      */
-    inline mlk::client::size& padding() noexcept
+    inline mlk::size& padding() noexcept
     {
         return m_padding;
     }
@@ -137,7 +137,7 @@
      *
      * \param padding the padding
      */
-    inline void set_padding(mlk::client::size padding) noexcept
+    inline void set_padding(mlk::size padding) noexcept
     {
         m_padding = std::move(padding);
     }
@@ -151,7 +151,7 @@
      * \param win the window
      * \return the size required to draw this frame
      */
-    virtual mlk::client::size size(window& win);
+    virtual mlk::size size(window& win);
 
     /**
      * Draw the frame and its content.
--- a/libclient/malikania/client/image.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/image.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -31,7 +31,7 @@
 
 image::~image() noexcept = default;
 
-const mlk::client::size& image::size() const noexcept
+const mlk::size& image::size() const noexcept
 {
     return m_backend->size();
 }
--- a/libclient/malikania/client/image.hpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/image.hpp	Tue Jul 04 13:23:15 2017 +0200
@@ -92,7 +92,7 @@
      *
      * \return the size
      */
-    const mlk::client::size& size() const noexcept;
+    const mlk::size& size() const noexcept;
 
     /**
      * Draw the image to the window.
--- a/libclient/malikania/client/label.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/label.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -35,7 +35,7 @@
     w.theme().draw_label(w, *this, rect);
 }
 
-mlk::client::size label::size(window& w) const noexcept
+mlk::size label::size(window& w) const noexcept
 {
     return w.theme().size_label(*this);
 }
--- a/libclient/malikania/client/label.hpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/label.hpp	Tue Jul 04 13:23:15 2017 +0200
@@ -87,7 +87,7 @@
     /**
      * \copydoc widget::size
      */
-    mlk::client::size size(window& w) const noexcept override;
+    mlk::size size(window& w) const noexcept override;
 };
 
 } // !client
--- a/libclient/malikania/client/layout.hpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/layout.hpp	Tue Jul 04 13:23:15 2017 +0200
@@ -25,11 +25,12 @@
 
 namespace mlk {
 
+class size;
+
 namespace client {
 
 class mouse_click_event;
 class rectangle;
-class size;
 class window;
 
 /**
@@ -64,7 +65,7 @@
     /**
      * Return the required minimum size to draw the whole layout.
      */
-    virtual mlk::client::size size(window& win) const = 0;
+    virtual mlk::size size(window& win) const = 0;
 
     /**
      * Draw the layout content at the given position.
--- a/libclient/malikania/client/loader.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/loader.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -16,62 +16,23 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <boost/format.hpp>
-
-#include <cassert>
+#include <json.hpp>
 
 #include "animation.hpp"
+#include "font.hpp"
+#include "image.hpp"
 #include "loader.hpp"
+#include "locator.hpp"
 #include "size.hpp"
 #include "sprite.hpp"
 #include "util.hpp"
 
-using boost::str;
-using boost::format;
-
 using namespace nlohmann;
 
 namespace mlk {
 
 namespace client {
 
-size loader::require_size(const std::string& id,
-                          const nlohmann::json& object,
-                          const std::string& property) const
-{
-    assert(object.is_object());
-
-    auto it = object.find(property);
-
-    if (it == object.end() || !it->is_array()) {
-        throw std::runtime_error(str(format("%s: missing '%' property (array expected)") % id % property));
-    }
-    if (it->size() != 2) {
-        throw std::runtime_error(str(format("%s: property '%s' muve have two values") % id % property));
-    }
-    if (!(*it)[0].is_number_integer() || !(*it)[1].is_number_integer()) {
-        throw std::runtime_error(str(format("%s: property '%s' must contains two ints")));
-    }
-
-    return size((*it)[0].get<int>(), (*it)[1].get<int>());
-}
-
-size loader::get_size(const std::string&,
-                      const nlohmann::json& object,
-                      const std::string& key) const noexcept
-{
-    assert(object.is_object());
-
-    auto it = object.find(key);
-
-    if (it == object.end() || !it->is_array() || it->size() != 2 ||
-        !(*it)[0].is_number_integer() || !(*it)[1].is_number_integer()) {
-        return size();
-    }
-
-    return size((*it)[0].get<int>(), (*it)[1].get<int>());
-}
-
 font loader::load_font(const std::string& id, unsigned size)
 {
     return font(locator().read(id), size);
@@ -91,11 +52,11 @@
     }
 
     return sprite(
-        load_image(require_string(id, value, "image")),
-        require_size(id, value, "cell"),
-        get_size(id, value, "size"),
-        get_size(id, value, "space"),
-        get_size(id, value, "margin")
+        load_image(util::json::require_string(value, "/image"_json_pointer)),
+        util::json::require_size(value, "/cell"_json_pointer),
+        util::json::get_size(value, "/size"_json_pointer),
+        util::json::get_size(value, "/space"_json_pointer),
+        util::json::get_size(value, "/margin"_json_pointer)
     );
 }
 
@@ -107,7 +68,7 @@
         throw std::runtime_error("not a JSON object");
     }
 
-    auto sprite = load_sprite(require_string(id, value, "sprite"));
+    auto sprite = load_sprite(util::json::require_string(value, "/sprite"_json_pointer));
 
     // Load all frames.
     auto property = util::json::require_array(value, "/frames"_json_pointer);
--- a/libclient/malikania/client/loader.hpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/loader.hpp	Tue Jul 04 13:23:15 2017 +0200
@@ -26,50 +26,22 @@
 
 #include <malikania/loader.hpp>
 
-#include "animation.hpp"
-#include "font.hpp"
-#include "image.hpp"
-#include "size.hpp"
-#include "sprite.hpp"
+#include <string>
 
 namespace mlk {
 
 namespace client {
 
+class animation;
+class font;
+class image;
+class size;
+class sprite;
+
 /**
  * \brief Load client resources.
  */
 class loader : public mlk::loader {
-protected:
-    /**
-     * Require a size object from an object property.
-     *
-     * The size is an array of two integers (e.g. [ 1, 2 ]).
-     *
-     * \pre object.is_object()
-     * \param id the resource id
-     * \param object the object
-     * \param property the property
-     * \return the size
-     * \throw std::runtime_error if the property is not a size
-     */
-    size require_size(const std::string& id,
-                      const nlohmann::json& object,
-                      const std::string& property) const;
-
-    /**
-     * Get a size object or a default one if not present or invalid.
-     *
-     * \pre object.is_object()
-     * \param id the resource id
-     * \param object the object
-     * \param property the property
-     * \return the size or default one
-     */
-    size get_size(const std::string& id,
-                  const nlohmann::json& object,
-                  const std::string& property) const noexcept;
-
 public:
     /**
      * Client resources loader constructor.
--- a/libclient/malikania/client/size.hpp	Fri Jun 16 12:38:04 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-/*
- * size.hpp -- size description
- *
- * Copyright (c) 2013-2017 Malikania Authors
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MALIKANIA_CLIENT_SIZE_HPP
-#define MALIKANIA_CLIENT_SIZE_HPP
-
-namespace mlk {
-
-namespace client {
-
-/**
- * \brief size description.
- */
-class size {
-private:
-    unsigned m_width;
-    unsigned m_height;
-
-public:
-    /**
-     * Constructor.
-     *
-     * \param width the size width
-     * \param height the size height
-     */
-    inline size(unsigned width = 0, unsigned height = 0) noexcept
-        : m_width(width)
-        , m_height(height)
-    {
-    }
-
-    /**
-     * Get the width.
-     *
-     * \return the width
-     */
-    inline unsigned width() const noexcept
-    {
-        return m_width;
-    }
-
-    /**
-     * Get the height.
-     *
-     * \return the height
-     */
-    inline unsigned height() const noexcept
-    {
-        return m_height;
-    }
-
-    /**
-     * Check if the size is 0, 0.
-     *
-     * \return true if height and width are 0
-     */
-    inline bool is_null() const noexcept
-    {
-        return m_height == 0 && m_width == 0;
-    }
-};
-
-/**
- * Compare equality.
- *
- * \param s1 the first size
- * \param s2 the second size
- * \return true if they equal
- */
-inline bool operator==(const size& s1, const size& s2) noexcept
-{
-    return s1.width() == s2.width() && s1.height() == s2.height();
-}
-
-/**
- * Compare equality.
- *
- * \param s1 the first size
- * \param s2 the second size
- * \return false if they equal
- */
-inline bool operator!=(const size& s1, const size& s2) noexcept
-{
-    return !(s1 == s2);
-}
-
-} // !client
-
-} // !mlk
-
-#endif // !MALIKANIA_CLIENT_SIZE_HPP
--- a/libclient/malikania/client/sprite.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/sprite.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -25,10 +25,10 @@
 namespace client {
 
 sprite::sprite(mlk::client::image image,
-               mlk::client::size cell,
-               mlk::client::size size,
-               mlk::client::size space,
-               mlk::client::size margin) noexcept
+               mlk::size cell,
+               mlk::size size,
+               mlk::size space,
+               mlk::size margin) noexcept
     : m_image(std::move(image))
     , m_cell(std::move(cell))
     , m_margin(std::move(margin))
--- a/libclient/malikania/client/sprite.hpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/sprite.hpp	Tue Jul 04 13:23:15 2017 +0200
@@ -36,10 +36,10 @@
 class sprite {
 private:
     mlk::client::image m_image;
-    mlk::client::size m_cell;
-    mlk::client::size m_margin;
-    mlk::client::size m_space;
-    mlk::client::size m_size;
+    mlk::size m_cell;
+    mlk::size m_margin;
+    mlk::size m_space;
+    mlk::size m_size;
 
     unsigned m_rows;
     unsigned m_columns;
@@ -56,10 +56,10 @@
      * \param size the sprite size (if 0, taken from the image)
      */
     sprite(mlk::client::image image,
-           mlk::client::size cell,
-           mlk::client::size margin = { 0, 0 },
-           mlk::client::size space = { 0, 0 },
-           mlk::client::size size = { 0, 0 }) noexcept;
+           mlk::size cell,
+           mlk::size margin = { 0, 0 },
+           mlk::size space = { 0, 0 },
+           mlk::size size = { 0, 0 }) noexcept;
 
     /**
      * Get the underlying image.
@@ -86,7 +86,7 @@
      *
      * \return the cell size
      */
-    inline const size& cell() const noexcept
+    inline const mlk::size& cell() const noexcept
     {
         return m_cell;
     }
@@ -96,7 +96,7 @@
      *
      * \return the margin size
      */
-    inline const size& margin() noexcept
+    inline const mlk::size& margin() noexcept
     {
         return m_margin;
     }
@@ -106,7 +106,7 @@
      *
      * \return the space size
      */
-    inline const size& space() const noexcept
+    inline const mlk::size& space() const noexcept
     {
         return m_space;
     }
--- a/libclient/malikania/client/unique_layout.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/unique_layout.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -43,9 +43,9 @@
     }
 }
 
-mlk::client::size unique_layout::size(window& win) const
+mlk::size unique_layout::size(window& win) const
 {
-    return m_widget ? m_widget->size(win) : mlk::client::size();
+    return m_widget ? m_widget->size(win) : mlk::size();
 }
 
 void unique_layout::draw(window& win, const rectangle& dst)
--- a/libclient/malikania/client/unique_layout.hpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/unique_layout.hpp	Tue Jul 04 13:23:15 2017 +0200
@@ -92,7 +92,7 @@
     /**
      * \copydoc layout::size
      */
-    mlk::client::size size(window& win) const override;
+    mlk::size size(window& win) const override;
 
     /**
      * \copydoc layout::draw
--- a/libclient/malikania/client/widget.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/widget.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -43,7 +43,7 @@
 
 void widget::draw(window& w, const point& pos)
 {
-    mlk::client::size sz = size(w);
+    mlk::size sz = size(w);
     mlk::client::rectangle rect{
         pos.x(), pos.y(),
         sz.width(), sz.height()
--- a/libclient/malikania/client/widget.hpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libclient/malikania/client/widget.hpp	Tue Jul 04 13:23:15 2017 +0200
@@ -26,13 +26,14 @@
 
 namespace mlk {
 
+class size;
+
 namespace client {
 
 class key_event;
 class mouse_click_event;
 class point;
 class rectangle;
-class size;
 class window;
 
 /**
@@ -97,7 +98,7 @@
      *
      * \return the widget size
      */
-    virtual mlk::client::size size(window& w) const noexcept = 0;
+    virtual mlk::size size(window& w) const noexcept = 0;
 };
 
 } // !client
--- a/libcommon-js/CMakeLists.txt	Fri Jun 16 12:38:04 2017 +0200
+++ b/libcommon-js/CMakeLists.txt	Tue Jul 04 13:23:15 2017 +0200
@@ -23,12 +23,14 @@
     ${libmlk-common-js_SOURCE_DIR}/malikania/duktape.hpp
     ${libmlk-common-js_SOURCE_DIR}/malikania/js_elapsed_timer.hpp
     ${libmlk-common-js_SOURCE_DIR}/malikania/js_resources_loader.hpp
+    ${libmlk-common-js_SOURCE_DIR}/malikania/js_size.hpp
 )
 
 set(
     SOURCES
     ${libmlk-common-js_SOURCE_DIR}/malikania/js_elapsed_timer.cpp
     ${libmlk-common-js_SOURCE_DIR}/malikania/js_resources_loader.cpp
+    ${libmlk-common-js_SOURCE_DIR}/malikania/js_size.cpp
 )
 
 malikania_define_library(
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libcommon-js/malikania/js_size.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -0,0 +1,146 @@
+/*
+ * js_size.cpp -- size description (JavaScript binding)
+ *
+ * Copyright (c) 2013-2017 Malikania Authors
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <cassert>
+
+#include "js_size.hpp"
+
+namespace mlk {
+
+namespace {
+
+size parse(duk_context* ctx, duk_idx_t index, bool required, size ret = {})
+{
+    dukx_stack_assert sa(ctx);
+
+    if (duk_is_object(ctx, index)) {
+        if (required && !duk_has_prop_string(ctx, index, "width")) {
+            duk_error(ctx, DUK_ERR_ERROR, "missing width property in size description");
+        } else if (required && !duk_has_prop_string(ctx, index, "height")) {
+            duk_error(ctx, DUK_ERR_ERROR, "missing height property in size description");
+        }
+
+        int width;
+        int height;
+
+        duk_get_prop_string(ctx, index, "width");
+        width = duk_to_int(ctx, -1);
+        duk_pop(ctx);
+        duk_get_prop_string(ctx, index, "height");
+        height = duk_to_int(ctx, -1);
+        duk_pop(ctx);
+
+        if (width < 0) {
+            duk_error(ctx, DUK_ERR_RANGE_ERROR, "width can not be negative");
+        }
+        if (height < 0) {
+            duk_error(ctx, DUK_ERR_RANGE_ERROR, "height can not be negative");
+        }
+
+        ret = size(width, height);
+    } else if (required) {
+        duk_error(ctx, DUK_ERR_TYPE_ERROR, "size object expected");
+    }
+
+    return ret;
+}
+
+duk_ret_t constructor(duk_context* ctx)
+{
+    size obj;
+
+    if (duk_get_top(ctx) == 2) {
+        int width;
+        int height;
+
+        if ((width = duk_require_int(ctx, 0)) < 0) {
+            duk_error(ctx, DUK_ERR_RANGE_ERROR, "argument #0 can not be negative");
+        }
+        if ((height = duk_require_int(ctx, 1)) < 0) {
+            duk_error(ctx, DUK_ERR_RANGE_ERROR, "argument #1 can not be negative");
+        }
+
+        obj = size(static_cast<unsigned>(width), static_cast<unsigned>(height));
+    } else if (duk_get_top(ctx) == 1) {
+        obj = parse(ctx, 0, true);
+    }
+
+    duk_ret_t ret;
+
+    // Allow both constructor and non constructor calls.
+    if (duk_is_constructor_call(ctx)) {
+        duk_push_this(ctx);
+        dukx_put_size(ctx, obj);
+        duk_pop(ctx);
+        ret = 0;
+    } else {
+        dukx_push_size(ctx, obj);
+        ret = 1;
+    }
+
+    return ret;
+}
+
+} // !namespace
+
+size dukx_get_size(duk_context* ctx, duk_idx_t index)
+{
+    return parse(ctx, index, false);
+}
+
+size dukx_require_size(duk_context* ctx, duk_idx_t index)
+{
+    return parse(ctx, index, true);
+}
+
+size dukx_optional_size(duk_context* ctx, duk_idx_t index, size def)
+{
+    return parse(ctx, index, false, std::move(def));
+}
+
+void dukx_push_size(duk_context* ctx, const size& size)
+{
+    dukx_stack_assert sa(ctx, 1);
+
+    duk_push_object(ctx);
+    dukx_put_size(ctx, size);
+}
+
+void dukx_put_size(duk_context* ctx, const size& size)
+{
+    assert(duk_is_object(ctx, -1));
+
+    dukx_stack_assert sa(ctx, 0);
+
+    duk_push_uint(ctx, size.width());
+    duk_put_prop_string(ctx, -2, "width");
+    duk_push_uint(ctx, size.height());
+    duk_put_prop_string(ctx, -2, "height");
+}
+
+void dukx_load_size(duk_context* ctx)
+{
+    dukx_stack_assert sa(ctx, 0);
+
+    duk_get_global_string(ctx, "Malikania");
+    duk_push_c_function(ctx, constructor, DUK_VARARGS);
+    duk_put_prop_string(ctx, -2, "Size");
+    duk_pop(ctx);
+}
+
+} // !mlk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libcommon-js/malikania/js_size.hpp	Tue Jul 04 13:23:15 2017 +0200
@@ -0,0 +1,93 @@
+/*
+ * js_size.hpp -- size description (JavaScript binding)
+ *
+ * Copyright (c) 2013-2017 Malikania Authors
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MALIKANIA_JS_SIZE_HPP
+#define MALIKANIA_JS_SIZE_HPP
+
+/**
+ * \file js_size.hpp
+ * \brief JavaScript binding for size.
+ *
+ * size are plain objects.
+ *
+ * ````
+ * {
+ *   width: 1000,
+ *   height: 2000
+ * }
+ * ````
+ */
+
+#include <malikania/duktape.hpp>
+#include <malikania/size.hpp>
+
+namespace mlk {
+
+/**
+ * Get a size.
+ *
+ * The size may be adjusted if any values are incorrect.
+ *
+ * \param ctx the context
+ * \param index the value index
+ * \return the size
+ */
+size dukx_get_size(duk_context* ctx, duk_idx_t index);
+
+/**
+ * Require a size
+ *
+ * If the object is not a size, raise a JavaScript error.
+ *
+ * \param ctx the context
+ * \param index the index
+ * \return the size
+ */
+size dukx_require_size(duk_context* ctx, duk_idx_t index);
+
+/**
+ * Like get but return the default value if the value at the given index is not an object.
+ *
+ * \param ctx the context
+ * \param index the idnex
+ * \param def the default value
+ * \return the size
+ */
+size dukx_optional_size(duk_context* ctx, duk_idx_t index, size def);
+
+/**
+ * Push the size as object.
+ *
+ * \param ctx the context
+ * \param size the size
+ */
+void dukx_push_size(duk_context* ctx, const size& size);
+
+/**
+ * Put the size properties into the object at the top of the stack.
+ *
+ * \param ctx the context
+ * \param size the size
+ */
+void dukx_put_size(duk_context* ctx, const size& size);
+
+void dukx_load_size(duk_context* ctx);
+
+} // !mlk
+
+#endif // !MALIKANIA_JS_SIZE_HPP
--- a/libcommon/CMakeLists.txt	Fri Jun 16 12:38:04 2017 +0200
+++ b/libcommon/CMakeLists.txt	Tue Jul 04 13:23:15 2017 +0200
@@ -24,6 +24,7 @@
     ${libmlk-common_SOURCE_DIR}/malikania/id.hpp
     ${libmlk-common_SOURCE_DIR}/malikania/loader.hpp
     ${libmlk-common_SOURCE_DIR}/malikania/locator.hpp
+    ${libmlk-common_SOURCE_DIR}/malikania/size.hpp
     ${libmlk-common_SOURCE_DIR}/malikania/util.hpp
 )
 
--- a/libcommon/malikania/loader.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libcommon/malikania/loader.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -16,69 +16,13 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <boost/format.hpp>
-
-#include <cassert>
-
 #include "game.hpp"
 #include "loader.hpp"
 #include "locator.hpp"
+#include "util.hpp"
 
 namespace mlk {
 
-void loader::requires(const std::string& id,
-                      const nlohmann::json& object,
-                      const std::unordered_map<std::string, nlohmann::json::value_t>& props) const
-{
-    assert(object.is_object());
-
-    for (const auto& pair : props) {
-        auto it = object.find(pair.first);
-
-        if (it == object.end() || it->type() != pair.second) {
-            std::string type;
-
-            switch (pair.second) {
-            case nlohmann::json::value_t::array:
-                type = "array";
-                break;
-            case nlohmann::json::value_t::boolean:
-                type = "boolean";
-                break;
-            case nlohmann::json::value_t::number_unsigned:
-                type = "number";
-                break;
-            case nlohmann::json::value_t::object:
-                type = "object";
-                break;
-            case nlohmann::json::value_t::string:
-                type = "string";
-                break;
-            default:
-                break;
-            }
-
-            throw std::runtime_error(boost::str(
-                boost::format("%s: missing '%s' property (%s expected)") % id % pair.first % type));
-        }
-    }
-}
-
-std::string loader::require_string(const std::string& id,
-                                   const nlohmann::json& object,
-                                   const std::string& property) const
-{
-    assert(object.is_object());
-
-    auto it = object.find(property);
-
-    if (it == object.end() || !it->is_string()) {
-        throw std::runtime_error(id + ": missing '" + property + "' property (string expected)");
-    }
-
-    return *it;
-}
-
 loader::loader(mlk::locator& locator)
     : m_locator(locator)
 {
@@ -92,15 +36,9 @@
         throw std::runtime_error("game.json: not a JSON object");
     }
 
-    requires("game.json", value, {
-        { "name",       nlohmann::json::value_t::string },
-        { "version",    nlohmann::json::value_t::string },
-        { "requires",   nlohmann::json::value_t::string }
-    });
-
-    return game{value["name"],
-                value["version"],
-                value["requires"],
+    return game{util::json::require_string(value, "/name"_json_pointer),
+                util::json::require_string(value, "/version"_json_pointer),
+                util::json::require_string(value, "/requires"_json_pointer),
                 value.count("license") > 0 ? value["license"] : "",
                 value.count("author") > 0 ? value["author"] : ""};
 }
--- a/libcommon/malikania/loader.hpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libcommon/malikania/loader.hpp	Tue Jul 04 13:23:15 2017 +0200
@@ -19,16 +19,10 @@
 #ifndef MALIKANIA_LOADER_HPP
 #define MALIKANIA_LOADER_HPP
 
-#include <string>
-#include <unordered_map>
-
-#include <json.hpp>
-
-#include "locator.hpp"
-
 namespace mlk {
 
 class game;
+class locator;
 
 /**
  * \brief Open resources files using a locator.
@@ -42,41 +36,6 @@
 private:
     mlk::locator& m_locator;
 
-protected:
-    /**
-     * Check that an object has the specified properties of the given type.
-     *
-     * Throws an error when any of the property is missing or not the correct
-     * type.
-     *
-     * You can use this function when you have lot of properties to extract,
-     * otherwise, you can use one of the require* or get* functions to avoid
-     * performances overhead.
-     *
-     * \pre object.is_object()
-     * \param id the resource id
-     * \param object the object
-     * \param props the properties
-     * \throw std::runtime_error when a property is missing / invalid
-     */
-    void requires(const std::string& id,
-                  const nlohmann::json& object,
-                  const std::unordered_map<std::string, nlohmann::json::value_t>& props) const;
-
-    /**
-     * Require a string.
-     *
-     * \pre object.isObject()
-     * \param id the resource id
-     * \param object the object
-     * \param property the property
-     * \return the string
-     * \throw std::runtime_error if the property is not a string or missing
-     */
-    std::string require_string(const std::string& id,
-                               const nlohmann::json& object,
-                               const std::string& property) const;
-
 public:
     /**
      * Construct the resources loader.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libcommon/malikania/size.hpp	Tue Jul 04 13:23:15 2017 +0200
@@ -0,0 +1,118 @@
+/*
+ * size.hpp -- size description
+ *
+ * Copyright (c) 2013-2017 Malikania Authors
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MALIKANIA_SIZE_HPP
+#define MALIKANIA_SIZE_HPP
+
+#include <ostream>
+
+namespace mlk {
+
+/**
+ * \brief size description.
+ */
+class size {
+private:
+    unsigned m_width;
+    unsigned m_height;
+
+public:
+    /**
+     * Constructor.
+     *
+     * \param width the size width
+     * \param height the size height
+     */
+    inline size(unsigned width = 0, unsigned height = 0) noexcept
+        : m_width(width)
+        , m_height(height)
+    {
+    }
+
+    /**
+     * Get the width.
+     *
+     * \return the width
+     */
+    inline unsigned width() const noexcept
+    {
+        return m_width;
+    }
+
+    /**
+     * Get the height.
+     *
+     * \return the height
+     */
+    inline unsigned height() const noexcept
+    {
+        return m_height;
+    }
+
+    /**
+     * Check if the size is 0, 0.
+     *
+     * \return true if height and width are 0
+     */
+    inline bool is_null() const noexcept
+    {
+        return m_height == 0 && m_width == 0;
+    }
+};
+
+/**
+ * Compare equality.
+ *
+ * \param s1 the first size
+ * \param s2 the second size
+ * \return true if they equal
+ */
+inline bool operator==(const size& s1, const size& s2) noexcept
+{
+    return s1.width() == s2.width() && s1.height() == s2.height();
+}
+
+/**
+ * Compare equality.
+ *
+ * \param s1 the first size
+ * \param s2 the second size
+ * \return false if they equal
+ */
+inline bool operator!=(const size& s1, const size& s2) noexcept
+{
+    return !(s1 == s2);
+}
+
+/**
+ * Prints the size object.
+ *
+ * \param out the output
+ * \param size the size object
+ * \return out
+ */
+inline std::ostream& operator<<(std::ostream& out, const size& size)
+{
+    out << "{" << size.width() << ", " << size.height() << "}";
+
+    return out;
+}
+
+} // !mlk
+
+#endif // !MALIKANIA_SIZE_HPP
--- a/libcommon/malikania/util.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libcommon/malikania/util.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -17,6 +17,7 @@
  */
 
 #include <cassert>
+#include <climits>
 #include <string>
 #include <stdexcept>
 
@@ -58,6 +59,7 @@
 #include <boost/filesystem.hpp>
 
 #include "util.hpp"
+#include "size.hpp"
 
 namespace {
 
@@ -297,6 +299,26 @@
     return value;
 }
 
+nlohmann::json get(const nlohmann::json& object,
+                   const nlohmann::json::json_pointer& pointer,
+                   const nlohmann::json::value_t expected,
+                   const nlohmann::json def)
+{
+    assert(object.is_object());
+
+    nlohmann::json value;
+
+    try {
+        value = object.at(pointer);
+    } catch (...) {
+    }
+
+    if (value.type() != expected)
+        value = def;
+
+    return value;
+}
+
 nlohmann::json require_array(const nlohmann::json& object,
                              const nlohmann::json::json_pointer& pointer)
 {
@@ -312,7 +334,19 @@
 int require_int(const nlohmann::json& object,
                 const nlohmann::json::json_pointer& pointer)
 {
-    return require(object, pointer, nlohmann::json::value_t::number_integer);
+    nlohmann::json v;
+
+    try {
+        v = object.at(pointer);
+
+        if (v.is_number_integer())
+            return v.get<int>();
+        else if (v.is_number_unsigned() && v.get<unsigned>() <= static_cast<unsigned>(INT_MAX))
+            return static_cast<int>(v.get<unsigned>());
+    } catch (...) {
+    }
+
+    throw property_error(object, pointer, v.type(), nlohmann::json::value_t::number_integer);
 }
 
 nlohmann::json require_object(const nlohmann::json& object,
@@ -330,7 +364,76 @@
 unsigned require_uint(const nlohmann::json& object,
                       const nlohmann::json::json_pointer& pointer)
 {
-    return require(object, pointer, nlohmann::json::value_t::number_unsigned);
+    nlohmann::json v;
+
+    try {
+        v = object.at(pointer);
+
+        if (v.is_number_unsigned())
+            return v.get<unsigned>();
+        else if (v.is_number_integer() && v.get<int>() >= 0)
+            return static_cast<unsigned>(v.get<int>());
+    } catch (...) {
+    }
+
+    throw property_error(object, pointer, v.type(), nlohmann::json::value_t::number_unsigned);
+}
+
+mlk::size require_size(const nlohmann::json& object,
+                       const nlohmann::json::json_pointer& index)
+{
+    using pointer = nlohmann::json::json_pointer;
+
+    return {
+        require_uint(object, pointer(index.to_string() + "/width")),
+        require_uint(object, pointer(index.to_string() + "/height"))
+    };
+}
+
+int get_int(const nlohmann::json &object,
+            const nlohmann::json::json_pointer &pointer,
+            int value) noexcept
+{
+    try {
+        auto v = object.at(pointer);
+
+        if (v.is_number_integer())
+            value = v.get<int>();
+        else if (v.is_number_unsigned() && v.get<unsigned>() <= static_cast<unsigned>(INT_MAX))
+            value = static_cast<int>(v.get<unsigned>());
+    } catch (...) {
+    }
+
+    return value;
+}
+
+int get_uint(const nlohmann::json &object,
+            const nlohmann::json::json_pointer& pointer,
+            unsigned value) noexcept
+{
+    try {
+        auto v = object.at(pointer);
+
+        if (v.is_number_unsigned())
+            value = v.get<unsigned>();
+        else if (v.is_number_integer() && v.get<int>() >= 0)
+            value = static_cast<unsigned>(v.get<int>());
+    } catch (...) {
+    }
+
+    return value;
+}
+
+mlk::size get_size(const nlohmann::json& object,
+                   const nlohmann::json::json_pointer& pointer,
+                   const mlk::size& def) noexcept
+{
+    using json_pointer = nlohmann::json::json_pointer;
+
+    return {
+        get_uint(object, json_pointer(pointer.to_string() + "/width"), def.width()),
+        get_uint(object, json_pointer(pointer.to_string() + "/height"), def.height())
+    };
 }
 
 } // !json
--- a/libcommon/malikania/util.hpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/libcommon/malikania/util.hpp	Tue Jul 04 13:23:15 2017 +0200
@@ -30,6 +30,7 @@
 #include <vector>
 
 #include "json.hpp"
+#include "size.hpp"
 
 namespace mlk {
 
@@ -268,6 +269,59 @@
 unsigned require_uint(const nlohmann::json& object,
                       const nlohmann::json::json_pointer& pointer);
 
+/**
+ * Require a size object at the given pointer.
+ *
+ * \pre object.is_object
+ * \param object the json object
+ * \param pointer the pointer to the property
+ * \return the value
+ * \throw property_error on errors
+ */
+mlk::size require_size(const nlohmann::json& object,
+                       const nlohmann::json::json_pointer& pointer);
+
+/**
+ * Get an integer at the given pointer or default value if not present or
+ * invalid.
+ *
+ * \pre object.is_object
+ * \param object the json object
+ * \param pointer the pointer to the property
+ * \param def the default value
+ * \return an integer
+ */
+int get_int(const nlohmann::json& object,
+            const nlohmann::json::json_pointer& pointer,
+            int def = 0) noexcept;
+
+/**
+ * Get an unsigned integer at the given pointer or default value if not present
+ * or invalid.
+ *
+ * \pre object.is_object
+ * \param object the json object
+ * \param pointer the pointer to the property
+ * \param def the default value
+ * \return an integer
+ */
+int get_uint(const nlohmann::json& object,
+            const nlohmann::json::json_pointer& pointer,
+            unsigned def = 0) noexcept;
+
+/**
+ * Get a size object at the given pointer or a default value.
+ *
+ * \pre object.is_object
+ * \param object the json object
+ * \param pointer the pointer to the property
+ * \param def the default value to return in case of error
+ * \return a size or a default value
+ */
+mlk::size get_size(const nlohmann::json& object,
+                   const nlohmann::json::json_pointer& pointer,
+                   const mlk::size& def = {}) noexcept;
+
 } // !json
 
 } // !util
--- a/tests/libclient/CMakeLists.txt	Fri Jun 16 12:38:04 2017 +0200
+++ b/tests/libclient/CMakeLists.txt	Tue Jul 04 13:23:15 2017 +0200
@@ -20,11 +20,9 @@
 add_subdirectory(line)
 add_subdirectory(point)
 add_subdirectory(rectangle)
-add_subdirectory(size)
 
 # JavaScript bindings
 add_subdirectory(js-color)
 add_subdirectory(js-line)
 add_subdirectory(js-point)
 add_subdirectory(js-rectangle)
-add_subdirectory(js-size)
--- a/tests/libclient/js-size/CMakeLists.txt	Fri Jun 16 12:38:04 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-#
-# CMakeLists.txt -- CMake build system for malikania
-#
-# Copyright (c) 2013-2017 Malikania Authors
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-#
-
-malikania_create_test(
-    NAME js-size
-    LIBRARIES libmlk-client-js
-    SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
-)
--- a/tests/libclient/js-size/main.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,406 +0,0 @@
-/*
- * main.cpp -- test Size (JavaScript binding)
- *
- * Copyright (c) 2013-2017 Malikania Authors
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#define BOOST_TEST_MODULE "Javascript Size"
-#include <boost/test/unit_test.hpp>
-
-#include <malikania/js_size.hpp>
-
-using namespace mlk;
-
-class test_size {
-protected:
-    dukx_context m_ctx;
-
-public:
-    test_size()
-    {
-        duk_push_object(m_ctx);
-        duk_put_global_string(m_ctx, "Malikania");
-        dukx_load_size(m_ctx);
-    }
-};
-
-BOOST_FIXTURE_TEST_SUITE(test_size_suite, test_size)
-
-/*
- * Valid constructors.
- * ------------------------------------------------------------------
- */
-
-BOOST_AUTO_TEST_SUITE(constructors)
-
-BOOST_AUTO_TEST_CASE(constructor_default)
-{
-    try {
-        auto ret = duk_peval_string(m_ctx,
-            "s = Malikania.Size();"
-            "w = s.width;"
-            "h = s.height;"
-        );
-
-        if (ret != 0) {
-            throw dukx_get_exception(m_ctx, -1);
-        }
-
-        duk_get_global_string(m_ctx, "w");
-        BOOST_REQUIRE_EQUAL(0U, duk_to_uint(m_ctx, -1));
-        duk_pop(m_ctx);
-        duk_get_global_string(m_ctx, "h");
-        BOOST_REQUIRE_EQUAL(0U, duk_to_uint(m_ctx, -1));
-        duk_pop(m_ctx);
-    } catch (const std::exception &ex) {
-        BOOST_FAIL(ex.what());
-    }
-}
-
-BOOST_AUTO_TEST_CASE(constructor_2_args)
-{
-    try {
-        auto ret = duk_peval_string(m_ctx,
-            "s = Malikania.Size(100, 200);"
-            "w = s.width;"
-            "h = s.height;"
-        );
-
-        if (ret != 0) {
-            throw dukx_get_exception(m_ctx, -1);
-        }
-
-        duk_get_global_string(m_ctx, "w");
-        BOOST_REQUIRE_EQUAL(100U, duk_to_uint(m_ctx, -1));
-        duk_pop(m_ctx);
-        duk_get_global_string(m_ctx, "h");
-        BOOST_REQUIRE_EQUAL(200U, duk_to_uint(m_ctx, -1));
-        duk_pop(m_ctx);
-    } catch (const std::exception &ex) {
-        BOOST_FAIL(ex.what());
-    }
-}
-
-BOOST_AUTO_TEST_CASE(constructor_object)
-{
-    try {
-        auto ret = duk_peval_string(m_ctx,
-            "s = Malikania.Size({ width: 100, height: 200 });"
-            "w = s.width;"
-            "h = s.height;"
-        );
-
-        if (ret != 0) {
-            throw dukx_get_exception(m_ctx, -1);
-        }
-
-        duk_get_global_string(m_ctx, "w");
-        BOOST_REQUIRE_EQUAL(100U, duk_to_uint(m_ctx, -1));
-        duk_pop(m_ctx);
-        duk_get_global_string(m_ctx, "h");
-        BOOST_REQUIRE_EQUAL(200U, duk_to_uint(m_ctx, -1));
-        duk_pop(m_ctx);
-    } catch (const std::exception &ex) {
-        BOOST_FAIL(ex.what());
-    }
-}
-
-BOOST_AUTO_TEST_CASE(constructor_new)
-{
-    try {
-        auto ret = duk_peval_string(m_ctx,
-            "s = new Malikania.Size({ width: 100, height: 200 });"
-            "w = s.width;"
-            "h = s.height;"
-        );
-
-        if (ret != 0) {
-            throw dukx_get_exception(m_ctx, -1);
-        }
-
-        duk_get_global_string(m_ctx, "w");
-        BOOST_REQUIRE_EQUAL(100U, duk_to_uint(m_ctx, -1));
-        duk_pop(m_ctx);
-        duk_get_global_string(m_ctx, "h");
-        BOOST_REQUIRE_EQUAL(200U, duk_to_uint(m_ctx, -1));
-        duk_pop(m_ctx);
-    } catch (const std::exception &ex) {
-        BOOST_FAIL(ex.what());
-    }
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
-/*
- * Invalid constructors.
- * ------------------------------------------------------------------
- */
-
-BOOST_AUTO_TEST_SUITE(invalid_constructors)
-
-BOOST_AUTO_TEST_CASE(constructor_arg_1)
-{
-    try {
-        auto ret = duk_peval_string(m_ctx,
-            "try {"
-            "  Malikania.Size(null);"
-            "} catch (e) {"
-            "  name = e.name;"
-            "  correct = (e instanceof TypeError);"
-            "}"
-        );
-
-        if (ret != 0) {
-            throw dukx_get_exception(m_ctx, -1);
-        }
-
-        duk_get_global_string(m_ctx, "name");
-        BOOST_REQUIRE_EQUAL("TypeError", duk_to_string(m_ctx, -1));
-        duk_pop(m_ctx);
-        duk_get_global_string(m_ctx, "correct");
-        BOOST_REQUIRE(duk_to_boolean(m_ctx, -1));
-        duk_pop(m_ctx);
-    } catch (const std::exception &ex) {
-        BOOST_FAIL(ex.what());
-    }
-}
-
-BOOST_AUTO_TEST_CASE(constructor_range_1)
-{
-    try {
-        auto ret = duk_peval_string(m_ctx,
-            "try {"
-            "  Malikania.Size(-1, 200);"
-            "} catch (e) {"
-            "  name = e.name;"
-            "  correct = (e instanceof RangeError);"
-            "}"
-        );
-
-        if (ret != 0) {
-            throw dukx_get_exception(m_ctx, -1);
-        }
-
-        duk_get_global_string(m_ctx, "name");
-        BOOST_REQUIRE_EQUAL("RangeError", duk_to_string(m_ctx, -1));
-        duk_pop(m_ctx);
-        duk_get_global_string(m_ctx, "correct");
-        BOOST_REQUIRE(duk_to_boolean(m_ctx, -1));
-        duk_pop(m_ctx);
-    } catch (const std::exception &ex) {
-        BOOST_FAIL(ex.what());
-    }
-}
-
-BOOST_AUTO_TEST_CASE(constructor_range_2)
-{
-    try {
-        auto ret = duk_peval_string(m_ctx,
-            "try {"
-            "  Malikania.Size(100, -1);"
-            "} catch (e) {"
-            "  name = e.name;"
-            "  correct = (e instanceof RangeError);"
-            "}"
-        );
-
-        if (ret != 0) {
-            throw dukx_get_exception(m_ctx, -1);
-        }
-
-        duk_get_global_string(m_ctx, "name");
-        BOOST_REQUIRE_EQUAL("RangeError", duk_to_string(m_ctx, -1));
-        duk_pop(m_ctx);
-        duk_get_global_string(m_ctx, "correct");
-        BOOST_REQUIRE(duk_to_boolean(m_ctx, -1));
-        duk_pop(m_ctx);
-    } catch (const std::exception &ex) {
-        BOOST_FAIL(ex.what());
-    }
-}
-
-BOOST_AUTO_TEST_CASE(constructor_range_3)
-{
-    try {
-        auto ret = duk_peval_string(m_ctx,
-            "try {"
-            "  Malikania.Size({ width: -1, height: 200 });"
-            "} catch (e) {"
-            "  name = e.name;"
-            "  correct = (e instanceof RangeError);"
-            "}"
-        );
-
-        if (ret != 0) {
-            throw dukx_get_exception(m_ctx, -1);
-        }
-
-        duk_get_global_string(m_ctx, "name");
-        BOOST_REQUIRE_EQUAL("RangeError", duk_to_string(m_ctx, -1));
-        duk_pop(m_ctx);
-        duk_get_global_string(m_ctx, "correct");
-        BOOST_REQUIRE(duk_to_boolean(m_ctx, -1));
-        duk_pop(m_ctx);
-    } catch (const std::exception &ex) {
-        BOOST_FAIL(ex.what());
-    }
-}
-
-BOOST_AUTO_TEST_CASE(constructor_range_4)
-{
-    try {
-        auto ret = duk_peval_string(m_ctx,
-            "try {"
-            "  Malikania.Size({ width: 100, height: -1 });"
-            "} catch (e) {"
-            "  name = e.name;"
-            "  correct = (e instanceof RangeError);"
-            "}"
-        );
-
-        if (ret != 0) {
-            throw dukx_get_exception(m_ctx, -1);
-        }
-
-        duk_get_global_string(m_ctx, "name");
-        BOOST_REQUIRE_EQUAL("RangeError", duk_to_string(m_ctx, -1));
-        duk_pop(m_ctx);
-        duk_get_global_string(m_ctx, "correct");
-        BOOST_REQUIRE(duk_to_boolean(m_ctx, -1));
-        duk_pop(m_ctx);
-    } catch (const std::exception &ex) {
-        BOOST_FAIL(ex.what());
-    }
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
-/*
- * Require.
- * ------------------------------------------------------------------
- */
-
-BOOST_AUTO_TEST_SUITE(require)
-
-BOOST_AUTO_TEST_CASE(success)
-{
-    try {
-        duk_push_c_function(m_ctx, [] (auto ctx) {
-            auto size = dukx_require_size(ctx, 0);
-
-            duk_push_uint(ctx, size.width());
-            duk_put_global_string(ctx, "w");
-            duk_push_uint(ctx, size.height());
-            duk_put_global_string(ctx, "h");
-
-            return 0;
-        }, 1);
-        duk_put_global_string(m_ctx, "build");
-
-        auto ret = duk_peval_string(m_ctx, "build({ width: 100, height: 200 });");
-
-        if (ret != 0) {
-            throw dukx_get_exception(m_ctx, -1);
-        }
-
-        duk_get_global_string(m_ctx, "w");
-        BOOST_REQUIRE_EQUAL(100U, duk_to_uint(m_ctx, -1));
-        duk_pop(m_ctx);
-        duk_get_global_string(m_ctx, "h");
-        BOOST_REQUIRE_EQUAL(200U, duk_to_uint(m_ctx, -1));
-        duk_pop(m_ctx);
-    } catch (const std::exception &ex) {
-        BOOST_FAIL(ex.what());
-    }
-}
-
-BOOST_AUTO_TEST_CASE(fail)
-{
-    try {
-        duk_push_c_function(m_ctx, [] (auto ctx) {
-            dukx_require_size(ctx, 0);
-
-            return 0;
-        }, 1);
-        duk_put_global_string(m_ctx, "build");
-
-        auto ret = duk_peval_string(m_ctx,
-            "try {"
-            "  build({});"
-            "} catch (e) {"
-            "  name = e.name;"
-            "  correct = (e instanceof Error);"
-            "}"
-        );
-
-        if (ret != 0) {
-            throw dukx_get_exception(m_ctx, -1);
-        }
-
-        duk_get_global_string(m_ctx, "name");
-        BOOST_REQUIRE_EQUAL("Error", duk_to_string(m_ctx, -1));
-        duk_pop(m_ctx);
-        duk_get_global_string(m_ctx, "correct");
-        BOOST_REQUIRE(duk_to_boolean(m_ctx, -1));
-        duk_pop(m_ctx);
-    } catch (const std::exception &ex) {
-        BOOST_FAIL(ex.what());
-    }
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
-/*
- * Get.
- * ------------------------------------------------------------------
- */
-
-BOOST_AUTO_TEST_SUITE(get)
-
-BOOST_AUTO_TEST_CASE(adjust_all)
-{
-    try {
-        duk_push_c_function(m_ctx, [] (auto ctx) {
-            auto size = dukx_get_size(ctx, 0);
-
-            duk_push_uint(ctx, size.width());
-            duk_put_global_string(ctx, "w");
-            duk_push_uint(ctx, size.height());
-            duk_put_global_string(ctx, "h");
-
-            return 0;
-        }, 1);
-        duk_put_global_string(m_ctx, "build");
-
-        auto ret = duk_peval_string(m_ctx, "build({});");
-
-        if (ret != 0) {
-            throw dukx_get_exception(m_ctx, -1);
-        }
-
-        duk_get_global_string(m_ctx, "w");
-        BOOST_REQUIRE_EQUAL(0U, duk_to_uint(m_ctx, -1));
-        duk_pop(m_ctx);
-        duk_get_global_string(m_ctx, "h");
-        BOOST_REQUIRE_EQUAL(0U, duk_to_uint(m_ctx, -1));
-        duk_pop(m_ctx);
-    } catch (const std::exception &ex) {
-        BOOST_FAIL(ex.what());
-    }
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
-BOOST_AUTO_TEST_SUITE_END()
--- a/tests/libclient/size/CMakeLists.txt	Fri Jun 16 12:38:04 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-#
-# CMakeLists.txt -- CMake build system for malikania
-#
-# Copyright (c) 2013-2017 Malikania Authors
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-#
-
-malikania_create_test(
-    NAME size
-    LIBRARIES libmlk-client
-    SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
-)
--- a/tests/libclient/size/main.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-/*
- * main.cpp -- test mlk::client::size
- *
- * Copyright (c) 2013-2017 Malikania Authors
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#define BOOST_TEST_MODULE "Size"
-#include <boost/test/unit_test.hpp>
-
-#include <malikania/client/size.hpp>
-
-namespace mlk {
-
-namespace client {
-
-std::ostream& operator<<(std::ostream& out, const size& size)
-{
-    out << "{" << size.width() << ", " << size.height() << "}";
-
-    return out;
-}
-
-} // !client
-
-} // !mlk
-
-BOOST_AUTO_TEST_CASE(none)
-{
-    mlk::client::size size;
-
-    BOOST_REQUIRE_EQUAL(0U, size.width());
-    BOOST_REQUIRE_EQUAL(0U, size.height());
-}
-
-BOOST_AUTO_TEST_CASE(null)
-{
-    BOOST_REQUIRE(mlk::client::size().is_null());
-    BOOST_REQUIRE(!mlk::client::size(0, 10).is_null());
-    BOOST_REQUIRE(!mlk::client::size(10, 0).is_null());
-}
-
-BOOST_AUTO_TEST_CASE(standard)
-{
-    mlk::client::size size(10, 20);
-
-    BOOST_REQUIRE_EQUAL(10U, size.width());
-    BOOST_REQUIRE_EQUAL(20U, size.height());
-}
-
-BOOST_AUTO_TEST_CASE(operator_eq)
-{
-    mlk::client::size size1, size2;
-
-    BOOST_REQUIRE_EQUAL(size1, size2);
-}
-
-BOOST_AUTO_TEST_CASE(operator_eq_1)
-{
-    mlk::client::size size1(10, 20);
-    mlk::client::size size2(10, 20);
-
-    BOOST_REQUIRE_EQUAL(size1, size2);
-}
-
-BOOST_AUTO_TEST_CASE(operator_neq)
-{
-    BOOST_REQUIRE_NE(mlk::client::size(10), mlk::client::size(20));
-    BOOST_REQUIRE_NE(mlk::client::size(10, 10), mlk::client::size(10, 20));
-}
--- a/tests/libcommon/CMakeLists.txt	Fri Jun 16 12:38:04 2017 +0200
+++ b/tests/libcommon/CMakeLists.txt	Tue Jul 04 13:23:15 2017 +0200
@@ -17,4 +17,8 @@
 #
 
 add_subdirectory(util)
+add_subdirectory(size)
+
+# JavaScript bindings
 add_subdirectory(js-elapsed-timer)
+add_subdirectory(js-size)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/libcommon/js-size/CMakeLists.txt	Tue Jul 04 13:23:15 2017 +0200
@@ -0,0 +1,23 @@
+#
+# CMakeLists.txt -- CMake build system for malikania
+#
+# Copyright (c) 2013-2017 Malikania Authors
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+malikania_create_test(
+    NAME js-size
+    LIBRARIES libmlk-common-js
+    SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/libcommon/js-size/main.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -0,0 +1,406 @@
+/*
+ * main.cpp -- test Size (JavaScript binding)
+ *
+ * Copyright (c) 2013-2017 Malikania Authors
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define BOOST_TEST_MODULE "Javascript Size"
+#include <boost/test/unit_test.hpp>
+
+#include <malikania/js_size.hpp>
+
+using namespace mlk;
+
+class test_size {
+protected:
+    dukx_context m_ctx;
+
+public:
+    test_size()
+    {
+        duk_push_object(m_ctx);
+        duk_put_global_string(m_ctx, "Malikania");
+        dukx_load_size(m_ctx);
+    }
+};
+
+BOOST_FIXTURE_TEST_SUITE(test_size_suite, test_size)
+
+/*
+ * Valid constructors.
+ * ------------------------------------------------------------------
+ */
+
+BOOST_AUTO_TEST_SUITE(constructors)
+
+BOOST_AUTO_TEST_CASE(constructor_default)
+{
+    try {
+        auto ret = duk_peval_string(m_ctx,
+            "s = Malikania.Size();"
+            "w = s.width;"
+            "h = s.height;"
+        );
+
+        if (ret != 0) {
+            throw dukx_get_exception(m_ctx, -1);
+        }
+
+        duk_get_global_string(m_ctx, "w");
+        BOOST_REQUIRE_EQUAL(0U, duk_to_uint(m_ctx, -1));
+        duk_pop(m_ctx);
+        duk_get_global_string(m_ctx, "h");
+        BOOST_REQUIRE_EQUAL(0U, duk_to_uint(m_ctx, -1));
+        duk_pop(m_ctx);
+    } catch (const std::exception &ex) {
+        BOOST_FAIL(ex.what());
+    }
+}
+
+BOOST_AUTO_TEST_CASE(constructor_2_args)
+{
+    try {
+        auto ret = duk_peval_string(m_ctx,
+            "s = Malikania.Size(100, 200);"
+            "w = s.width;"
+            "h = s.height;"
+        );
+
+        if (ret != 0) {
+            throw dukx_get_exception(m_ctx, -1);
+        }
+
+        duk_get_global_string(m_ctx, "w");
+        BOOST_REQUIRE_EQUAL(100U, duk_to_uint(m_ctx, -1));
+        duk_pop(m_ctx);
+        duk_get_global_string(m_ctx, "h");
+        BOOST_REQUIRE_EQUAL(200U, duk_to_uint(m_ctx, -1));
+        duk_pop(m_ctx);
+    } catch (const std::exception &ex) {
+        BOOST_FAIL(ex.what());
+    }
+}
+
+BOOST_AUTO_TEST_CASE(constructor_object)
+{
+    try {
+        auto ret = duk_peval_string(m_ctx,
+            "s = Malikania.Size({ width: 100, height: 200 });"
+            "w = s.width;"
+            "h = s.height;"
+        );
+
+        if (ret != 0) {
+            throw dukx_get_exception(m_ctx, -1);
+        }
+
+        duk_get_global_string(m_ctx, "w");
+        BOOST_REQUIRE_EQUAL(100U, duk_to_uint(m_ctx, -1));
+        duk_pop(m_ctx);
+        duk_get_global_string(m_ctx, "h");
+        BOOST_REQUIRE_EQUAL(200U, duk_to_uint(m_ctx, -1));
+        duk_pop(m_ctx);
+    } catch (const std::exception &ex) {
+        BOOST_FAIL(ex.what());
+    }
+}
+
+BOOST_AUTO_TEST_CASE(constructor_new)
+{
+    try {
+        auto ret = duk_peval_string(m_ctx,
+            "s = new Malikania.Size({ width: 100, height: 200 });"
+            "w = s.width;"
+            "h = s.height;"
+        );
+
+        if (ret != 0) {
+            throw dukx_get_exception(m_ctx, -1);
+        }
+
+        duk_get_global_string(m_ctx, "w");
+        BOOST_REQUIRE_EQUAL(100U, duk_to_uint(m_ctx, -1));
+        duk_pop(m_ctx);
+        duk_get_global_string(m_ctx, "h");
+        BOOST_REQUIRE_EQUAL(200U, duk_to_uint(m_ctx, -1));
+        duk_pop(m_ctx);
+    } catch (const std::exception &ex) {
+        BOOST_FAIL(ex.what());
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+/*
+ * Invalid constructors.
+ * ------------------------------------------------------------------
+ */
+
+BOOST_AUTO_TEST_SUITE(invalid_constructors)
+
+BOOST_AUTO_TEST_CASE(constructor_arg_1)
+{
+    try {
+        auto ret = duk_peval_string(m_ctx,
+            "try {"
+            "  Malikania.Size(null);"
+            "} catch (e) {"
+            "  name = e.name;"
+            "  correct = (e instanceof TypeError);"
+            "}"
+        );
+
+        if (ret != 0) {
+            throw dukx_get_exception(m_ctx, -1);
+        }
+
+        duk_get_global_string(m_ctx, "name");
+        BOOST_REQUIRE_EQUAL("TypeError", duk_to_string(m_ctx, -1));
+        duk_pop(m_ctx);
+        duk_get_global_string(m_ctx, "correct");
+        BOOST_REQUIRE(duk_to_boolean(m_ctx, -1));
+        duk_pop(m_ctx);
+    } catch (const std::exception &ex) {
+        BOOST_FAIL(ex.what());
+    }
+}
+
+BOOST_AUTO_TEST_CASE(constructor_range_1)
+{
+    try {
+        auto ret = duk_peval_string(m_ctx,
+            "try {"
+            "  Malikania.Size(-1, 200);"
+            "} catch (e) {"
+            "  name = e.name;"
+            "  correct = (e instanceof RangeError);"
+            "}"
+        );
+
+        if (ret != 0) {
+            throw dukx_get_exception(m_ctx, -1);
+        }
+
+        duk_get_global_string(m_ctx, "name");
+        BOOST_REQUIRE_EQUAL("RangeError", duk_to_string(m_ctx, -1));
+        duk_pop(m_ctx);
+        duk_get_global_string(m_ctx, "correct");
+        BOOST_REQUIRE(duk_to_boolean(m_ctx, -1));
+        duk_pop(m_ctx);
+    } catch (const std::exception &ex) {
+        BOOST_FAIL(ex.what());
+    }
+}
+
+BOOST_AUTO_TEST_CASE(constructor_range_2)
+{
+    try {
+        auto ret = duk_peval_string(m_ctx,
+            "try {"
+            "  Malikania.Size(100, -1);"
+            "} catch (e) {"
+            "  name = e.name;"
+            "  correct = (e instanceof RangeError);"
+            "}"
+        );
+
+        if (ret != 0) {
+            throw dukx_get_exception(m_ctx, -1);
+        }
+
+        duk_get_global_string(m_ctx, "name");
+        BOOST_REQUIRE_EQUAL("RangeError", duk_to_string(m_ctx, -1));
+        duk_pop(m_ctx);
+        duk_get_global_string(m_ctx, "correct");
+        BOOST_REQUIRE(duk_to_boolean(m_ctx, -1));
+        duk_pop(m_ctx);
+    } catch (const std::exception &ex) {
+        BOOST_FAIL(ex.what());
+    }
+}
+
+BOOST_AUTO_TEST_CASE(constructor_range_3)
+{
+    try {
+        auto ret = duk_peval_string(m_ctx,
+            "try {"
+            "  Malikania.Size({ width: -1, height: 200 });"
+            "} catch (e) {"
+            "  name = e.name;"
+            "  correct = (e instanceof RangeError);"
+            "}"
+        );
+
+        if (ret != 0) {
+            throw dukx_get_exception(m_ctx, -1);
+        }
+
+        duk_get_global_string(m_ctx, "name");
+        BOOST_REQUIRE_EQUAL("RangeError", duk_to_string(m_ctx, -1));
+        duk_pop(m_ctx);
+        duk_get_global_string(m_ctx, "correct");
+        BOOST_REQUIRE(duk_to_boolean(m_ctx, -1));
+        duk_pop(m_ctx);
+    } catch (const std::exception &ex) {
+        BOOST_FAIL(ex.what());
+    }
+}
+
+BOOST_AUTO_TEST_CASE(constructor_range_4)
+{
+    try {
+        auto ret = duk_peval_string(m_ctx,
+            "try {"
+            "  Malikania.Size({ width: 100, height: -1 });"
+            "} catch (e) {"
+            "  name = e.name;"
+            "  correct = (e instanceof RangeError);"
+            "}"
+        );
+
+        if (ret != 0) {
+            throw dukx_get_exception(m_ctx, -1);
+        }
+
+        duk_get_global_string(m_ctx, "name");
+        BOOST_REQUIRE_EQUAL("RangeError", duk_to_string(m_ctx, -1));
+        duk_pop(m_ctx);
+        duk_get_global_string(m_ctx, "correct");
+        BOOST_REQUIRE(duk_to_boolean(m_ctx, -1));
+        duk_pop(m_ctx);
+    } catch (const std::exception &ex) {
+        BOOST_FAIL(ex.what());
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+/*
+ * Require.
+ * ------------------------------------------------------------------
+ */
+
+BOOST_AUTO_TEST_SUITE(require)
+
+BOOST_AUTO_TEST_CASE(success)
+{
+    try {
+        duk_push_c_function(m_ctx, [] (auto ctx) {
+            auto size = dukx_require_size(ctx, 0);
+
+            duk_push_uint(ctx, size.width());
+            duk_put_global_string(ctx, "w");
+            duk_push_uint(ctx, size.height());
+            duk_put_global_string(ctx, "h");
+
+            return 0;
+        }, 1);
+        duk_put_global_string(m_ctx, "build");
+
+        auto ret = duk_peval_string(m_ctx, "build({ width: 100, height: 200 });");
+
+        if (ret != 0) {
+            throw dukx_get_exception(m_ctx, -1);
+        }
+
+        duk_get_global_string(m_ctx, "w");
+        BOOST_REQUIRE_EQUAL(100U, duk_to_uint(m_ctx, -1));
+        duk_pop(m_ctx);
+        duk_get_global_string(m_ctx, "h");
+        BOOST_REQUIRE_EQUAL(200U, duk_to_uint(m_ctx, -1));
+        duk_pop(m_ctx);
+    } catch (const std::exception &ex) {
+        BOOST_FAIL(ex.what());
+    }
+}
+
+BOOST_AUTO_TEST_CASE(fail)
+{
+    try {
+        duk_push_c_function(m_ctx, [] (auto ctx) {
+            dukx_require_size(ctx, 0);
+
+            return 0;
+        }, 1);
+        duk_put_global_string(m_ctx, "build");
+
+        auto ret = duk_peval_string(m_ctx,
+            "try {"
+            "  build({});"
+            "} catch (e) {"
+            "  name = e.name;"
+            "  correct = (e instanceof Error);"
+            "}"
+        );
+
+        if (ret != 0) {
+            throw dukx_get_exception(m_ctx, -1);
+        }
+
+        duk_get_global_string(m_ctx, "name");
+        BOOST_REQUIRE_EQUAL("Error", duk_to_string(m_ctx, -1));
+        duk_pop(m_ctx);
+        duk_get_global_string(m_ctx, "correct");
+        BOOST_REQUIRE(duk_to_boolean(m_ctx, -1));
+        duk_pop(m_ctx);
+    } catch (const std::exception &ex) {
+        BOOST_FAIL(ex.what());
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+/*
+ * Get.
+ * ------------------------------------------------------------------
+ */
+
+BOOST_AUTO_TEST_SUITE(get)
+
+BOOST_AUTO_TEST_CASE(adjust_all)
+{
+    try {
+        duk_push_c_function(m_ctx, [] (auto ctx) {
+            auto size = dukx_get_size(ctx, 0);
+
+            duk_push_uint(ctx, size.width());
+            duk_put_global_string(ctx, "w");
+            duk_push_uint(ctx, size.height());
+            duk_put_global_string(ctx, "h");
+
+            return 0;
+        }, 1);
+        duk_put_global_string(m_ctx, "build");
+
+        auto ret = duk_peval_string(m_ctx, "build({});");
+
+        if (ret != 0) {
+            throw dukx_get_exception(m_ctx, -1);
+        }
+
+        duk_get_global_string(m_ctx, "w");
+        BOOST_REQUIRE_EQUAL(0U, duk_to_uint(m_ctx, -1));
+        duk_pop(m_ctx);
+        duk_get_global_string(m_ctx, "h");
+        BOOST_REQUIRE_EQUAL(0U, duk_to_uint(m_ctx, -1));
+        duk_pop(m_ctx);
+    } catch (const std::exception &ex) {
+        BOOST_FAIL(ex.what());
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_AUTO_TEST_SUITE_END()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/libcommon/size/CMakeLists.txt	Tue Jul 04 13:23:15 2017 +0200
@@ -0,0 +1,23 @@
+#
+# CMakeLists.txt -- CMake build system for malikania
+#
+# Copyright (c) 2013-2017 Malikania Authors
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+malikania_create_test(
+    NAME size
+    LIBRARIES libmlk-common
+    SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/libcommon/size/main.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -0,0 +1,66 @@
+/*
+ * main.cpp -- test mlk::size
+ *
+ * Copyright (c) 2013-2017 Malikania Authors
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define BOOST_TEST_MODULE "Size"
+#include <boost/test/unit_test.hpp>
+
+#include <malikania/size.hpp>
+
+BOOST_AUTO_TEST_CASE(none)
+{
+    mlk::size size;
+
+    BOOST_REQUIRE_EQUAL(0U, size.width());
+    BOOST_REQUIRE_EQUAL(0U, size.height());
+}
+
+BOOST_AUTO_TEST_CASE(null)
+{
+    BOOST_REQUIRE(mlk::size().is_null());
+    BOOST_REQUIRE(!mlk::size(0, 10).is_null());
+    BOOST_REQUIRE(!mlk::size(10, 0).is_null());
+}
+
+BOOST_AUTO_TEST_CASE(standard)
+{
+    mlk::size size(10, 20);
+
+    BOOST_REQUIRE_EQUAL(10U, size.width());
+    BOOST_REQUIRE_EQUAL(20U, size.height());
+}
+
+BOOST_AUTO_TEST_CASE(operator_eq)
+{
+    mlk::size size1, size2;
+
+    BOOST_REQUIRE_EQUAL(size1, size2);
+}
+
+BOOST_AUTO_TEST_CASE(operator_eq_1)
+{
+    mlk::size size1(10, 20);
+    mlk::size size2(10, 20);
+
+    BOOST_REQUIRE_EQUAL(size1, size2);
+}
+
+BOOST_AUTO_TEST_CASE(operator_neq)
+{
+    BOOST_REQUIRE_NE(mlk::size(10), mlk::size(20));
+    BOOST_REQUIRE_NE(mlk::size(10, 10), mlk::size(10, 20));
+}
--- a/tests/libcommon/util/main.cpp	Fri Jun 16 12:38:04 2017 +0200
+++ b/tests/libcommon/util/main.cpp	Tue Jul 04 13:23:15 2017 +0200
@@ -20,6 +20,7 @@
 #include <boost/test/unit_test.hpp>
 
 #include <malikania/util.hpp>
+#include <malikania/size.hpp>
 
 using namespace mlk;
 using namespace nlohmann;
@@ -335,3 +336,136 @@
 }
 
 BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_AUTO_TEST_SUITE(json_require_size)
+
+/*
+ * util::json::require_size
+ * ------------------------------------------------------------------
+ */
+
+BOOST_AUTO_TEST_CASE(simple)
+{
+    json object{
+        { "size", {
+                { "width", 10 },
+                { "height", 20 }
+            }
+        }
+    };
+
+    BOOST_REQUIRE_EQUAL(util::json::require_size(object, "/size"_json_pointer), mlk::size(10, 20));
+}
+
+BOOST_AUTO_TEST_CASE(not_object)
+{
+    json object{
+        { "size", false }
+    };
+
+    try {
+        util::json::require_size(object, "/size"_json_pointer);
+        BOOST_FAIL("exception expected");
+    } catch (const util::json::property_error& ex) {
+        BOOST_REQUIRE_EQUAL(ex.type(), json::value_t::null);
+        BOOST_REQUIRE_EQUAL(ex.expected(), json::value_t::number_unsigned);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(not_int_width)
+{
+    json object{
+        { "size", {
+                { "width", "10" },
+                { "height", 20 }
+            }
+        }
+    };
+
+    try {
+        util::json::require_size(object, "/size"_json_pointer);
+        BOOST_FAIL("exception expected");
+    } catch (const util::json::property_error& ex) {
+        BOOST_REQUIRE_EQUAL(ex.type(), json::value_t::string);
+        BOOST_REQUIRE_EQUAL(ex.expected(), json::value_t::number_unsigned);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(not_int_height)
+{
+    json object{
+        { "size", {
+                { "width", 10 },
+                { "height", "20" }
+            }
+        }
+    };
+
+    try {
+        util::json::require_size(object, "/size"_json_pointer);
+        BOOST_FAIL("exception expected");
+    } catch (const util::json::property_error& ex) {
+        BOOST_REQUIRE_EQUAL(ex.type(), json::value_t::string);
+        BOOST_REQUIRE_EQUAL(ex.expected(), json::value_t::number_unsigned);
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_AUTO_TEST_SUITE(json_get_size)
+
+BOOST_AUTO_TEST_CASE(simple)
+{
+    json object{
+        { "size", {
+                { "width", 10 },
+                { "height", 20 }
+            }
+        }
+    };
+
+    BOOST_REQUIRE_EQUAL(util::json::get_size(object, "/size"_json_pointer), mlk::size(10, 20));
+}
+
+BOOST_AUTO_TEST_CASE(not_object)
+{
+    json object{
+        { "size", false }
+    };
+
+    mlk::size def(10, 20);
+
+    BOOST_REQUIRE_EQUAL(util::json::get_size(object, "/size"_json_pointer, def), def);
+}
+
+BOOST_AUTO_TEST_CASE(not_int_width)
+{
+    json object{
+        { "size", {
+                { "width", "10" },
+                { "height", 20 }
+            }
+        }
+    };
+
+    mlk::size def(10, 20);
+
+    BOOST_REQUIRE_EQUAL(util::json::get_size(object, "/size"_json_pointer, def), def);
+}
+
+BOOST_AUTO_TEST_CASE(not_int_height)
+{
+    json object{
+        { "size", {
+                { "width", 10 },
+                { "height", "20" }
+            }
+        }
+    };
+
+    mlk::size def(10, 20);
+
+    BOOST_REQUIRE_EQUAL(util::json::get_size(object, "/size"_json_pointer, def), def);
+}
+
+BOOST_AUTO_TEST_SUITE_END()