diff --git a/dream/platform/static/dream/InputModule_viewAddDocumentDialog.html b/dream/platform/static/dream/InputModule_viewAddDocumentDialog.html
index 877d6a9619550de2c255f69b78880e6c1696e2f6..9c4a056d5934a6936dae413e0e384c499f7853bf 100644
--- a/dream/platform/static/dream/InputModule_viewAddDocumentDialog.html
+++ b/dream/platform/static/dream/InputModule_viewAddDocumentDialog.html
@@ -11,6 +11,9 @@
     <script src="InputModule_viewAddDocumentDialog.js" type="text/javascript"></script>
   </head>
   <body>
+    <form class="new_form">
+      <button type="submit" class="ui-btn ui-btn-b ui-btn-inline ui-icon-plus ui-btn-icon-right">Empty</button>
+    </form>
     <form class="import_form">
       <input id="dream_import" type="file" required=""
              name="dream_import">
diff --git a/dream/platform/static/dream/InputModule_viewAddDocumentDialog.js b/dream/platform/static/dream/InputModule_viewAddDocumentDialog.js
index eda63022ac5323fee22e1cdb8e3f3dd7d258164d..81d46f8a582729f56e3c739964206ebb6730a1c4 100644
--- a/dream/platform/static/dream/InputModule_viewAddDocumentDialog.js
+++ b/dream/platform/static/dream/InputModule_viewAddDocumentDialog.js
@@ -2,29 +2,30 @@
          initGadgetMixin */
 (function(window, rJS, RSVP, promiseEventListener, promiseReadAsText, initGadgetMixin) {
     "use strict";
-    var gadget_klass = rJS(window);
-    initGadgetMixin(gadget_klass);
-    gadget_klass.declareAcquiredMethod("aq_post", "jio_post").declareAcquiredMethod("aq_putAttachment", "jio_putAttachment").declareAcquiredMethod("pleaseRedirectMyHash", "pleaseRedirectMyHash").declareAcquiredMethod("whoWantToDisplayThisDocument", "whoWantToDisplayThisDocument").declareMethod("startService", function() {
-        var gadget = this, json_data, name;
+    function createDocument(gadget, name) {
+        var now = new Date();
+        // Create jIO document
+        return gadget.aq_post({
+            title: name,
+            type: "Dream",
+            format: "application/json",
+            modified: now.toUTCString(),
+            date: now.getFullYear() + "-" + (now.getMonth() + 1) + "-" + now.getDate()
+        });
+    }
+    function waitForImport(gadget) {
+        var json_data, name;
         return new RSVP.Queue().push(function() {
             return promiseEventListener(gadget.props.element.getElementsByClassName("import_form")[0], "submit", false);
         }).push(function(evt) {
             // Prevent double click
-            gadget.props.element.getElementsByClassName("ui-btn")[0].disabled = true;
+            evt.target.getElementsByClassName("ui-btn")[0].disabled = true;
             var file = evt.target.dream_import.files[0];
             name = file.name;
             return promiseReadAsText(file);
         }).push(function(json) {
-            var now = new Date();
             json_data = json;
-            // Create jIO document
-            return gadget.aq_post({
-                title: name,
-                type: "Dream",
-                format: "application/json",
-                modified: now.toUTCString(),
-                date: now.getFullYear() + "-" + (now.getMonth() + 1) + "-" + now.getDate()
-            });
+            return createDocument(gadget, name);
         }).push(function(jio_document) {
             // Add JSON as attachment
             return gadget.aq_putAttachment({
@@ -33,6 +34,40 @@
                 _data: json_data,
                 _mimetype: "application/json"
             });
+        });
+    }
+    function waitForNew(gadget) {
+        var json_data = {
+            nodes: {},
+            edges: {},
+            preference: {},
+            general: {},
+            wip_part_spreadsheet: [ [ "Order ID", "Due Date", "Priority", "Project Manager", "Part", "Part Type", "Sequence", "Processing Times", "Prerequisites Parts" ] ],
+            shift_spreadsheet: [ [ "Day", "Machines", // XXX more generic name ?
+            "Start", "End" ] ]
+        }, name = "FromScratch";
+        return new RSVP.Queue().push(function() {
+            return promiseEventListener(gadget.props.element.getElementsByClassName("new_form")[0], "submit", false);
+        }).push(function(evt) {
+            // Prevent double click
+            evt.target.getElementsByClassName("ui-btn")[0].disabled = true;
+            return createDocument(gadget, name);
+        }).push(function(jio_document) {
+            // Add JSON as attachment
+            return gadget.aq_putAttachment({
+                _id: jio_document.id,
+                _attachment: "body.json",
+                _data: JSON.stringify(json_data),
+                _mimetype: "application/json"
+            });
+        });
+    }
+    var gadget_klass = rJS(window);
+    initGadgetMixin(gadget_klass);
+    gadget_klass.declareAcquiredMethod("aq_post", "jio_post").declareAcquiredMethod("aq_putAttachment", "jio_putAttachment").declareAcquiredMethod("pleaseRedirectMyHash", "pleaseRedirectMyHash").declareAcquiredMethod("whoWantToDisplayThisDocument", "whoWantToDisplayThisDocument").declareMethod("startService", function() {
+        var gadget = this;
+        return new RSVP.Queue().push(function() {
+            return RSVP.any([ waitForImport(gadget), waitForNew(gadget) ]);
         }).push(function(result) {
             return gadget.whoWantToDisplayThisDocument(result.id);
         }).push(function(url) {
diff --git a/dream/platform/static/dream/Input_viewProductionLine.js b/dream/platform/static/dream/Input_viewProductionLine.js
index e512fdbc4b4ac5ebbb4d3106c14f50985d9d95d8..d92cdef0e834c78de4dff734b1b80552cc9e8f70 100644
--- a/dream/platform/static/dream/Input_viewProductionLine.js
+++ b/dream/platform/static/dream/Input_viewProductionLine.js
@@ -19,6 +19,10 @@
             }), gadget.getDeclaredGadget("productionline_graph") ]);
         }).push(function(result_list) {
             return result_list[1].render(result_list[0]);
+        }).push(function() {
+            return gadget.getDeclaredGadget("productionline_toolbox");
+        }).push(function(toolbox_gadget) {
+            toolbox_gadget.render();
         });
     }).declareMethod("startService", function() {
         var g = this;
diff --git a/dream/platform/static/dream/Input_viewShiftSpreadsheet.html b/dream/platform/static/dream/Input_viewShiftSpreadsheet.html
index de255e8e02b5b471d69db43fc54a6b78952eaa65..dbb9ee3c8d07d8b154c5f75f13a0a897af699bf1 100644
--- a/dream/platform/static/dream/Input_viewShiftSpreadsheet.html
+++ b/dream/platform/static/dream/Input_viewShiftSpreadsheet.html
@@ -3,9 +3,11 @@
   <head>
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
-    <title>Edit Wip Part Spreadsheet</title>
+    <title>Edit Shift Spreadsheet</title>
     <script src="../lib/rsvp.min.js" type="text/javascript"></script>
     <script src="../lib/renderjs.min.js" type="text/javascript"></script>
+    <script src="../lib/jquery.js" type="text/javascript"></script>
+    <script src="../lib/jquerymobile.js" type="text/javascript"></script>
 
     <script src="mixin_gadget.js" type="text/javascript"></script>
     <script src="Input_viewShiftSpreadsheet.js" type="text/javascript"></script>
@@ -13,5 +15,9 @@
   <body>
     <div data-gadget-url="../handsontable/index.html"
          data-gadget-scope="tableeditor"></div>
+    <form class="save_form">
+      <button type="submit" class="ui-btn ui-btn-b ui-btn-inline
+        ui-icon-edit ui-btn-icon-right">Save</button>
+    </form>
   </body>
 </html>
diff --git a/dream/platform/static/dream/Input_viewShiftSpreadsheet.js b/dream/platform/static/dream/Input_viewShiftSpreadsheet.js
index 0646d5a7787aa85276a89526e8f64961c7016740..d5a93af059a180cc0de170008cec09c054a6e65f 100644
--- a/dream/platform/static/dream/Input_viewShiftSpreadsheet.js
+++ b/dream/platform/static/dream/Input_viewShiftSpreadsheet.js
@@ -1,6 +1,38 @@
-/*global rJS, RSVP, initGadgetMixin */
-(function(window, rJS, RSVP, initGadgetMixin) {
+/*global rJS, RSVP, initGadgetMixin, loopEventListener */
+(function(window, rJS, RSVP, initGadgetMixin, loopEventListener) {
     "use strict";
+    function saveSpreadsheet(evt) {
+        var gadget = this, editor_data, editor_gadget;
+        return new RSVP.Queue().push(function() {
+            // Prevent double click
+            evt.target.getElementsByClassName("ui-btn")[0].disabled = true;
+            return gadget.getDeclaredGadget("tableeditor");
+        }).push(function(tablegadget) {
+            editor_gadget = tablegadget;
+            return editor_gadget.getData();
+        }).push(function(data) {
+            editor_data = data;
+            // Always get a fresh version, to prevent deleting spreadsheet & co
+            return gadget.aq_getAttachment({
+                _id: gadget.props.jio_key,
+                _attachment: "body.json"
+            });
+        }).push(function(body) {
+            var data = JSON.parse(body);
+            data.shift_spreadsheet = JSON.parse(editor_data);
+            return gadget.aq_putAttachment({
+                _id: gadget.props.jio_key,
+                _attachment: "body.json",
+                _data: JSON.stringify(data, null, 2),
+                _mimetype: "application/json"
+            });
+        }).push(function() {
+            evt.target.getElementsByClassName("ui-btn")[0].disabled = false;
+        });
+    }
+    function waitForSave(gadget) {
+        return loopEventListener(gadget.props.element.getElementsByClassName("save_form")[0], "submit", false, saveSpreadsheet.bind(gadget));
+    }
     var gadget_klass = rJS(window);
     initGadgetMixin(gadget_klass);
     gadget_klass.declareAcquiredMethod("aq_getAttachment", "jio_getAttachment").declareMethod("render", function(options) {
@@ -15,8 +47,9 @@
             return result_list[1].render(JSON.stringify(JSON.parse(result_list[0]).shift_spreadsheet));
         });
     }).declareMethod("startService", function() {
+        var gadget = this;
         return this.getDeclaredGadget("tableeditor").push(function(tableeditor) {
-            return tableeditor.startService();
+            return RSVP.all([ tableeditor.startService(), waitForSave(gadget) ]);
         });
     });
-})(window, rJS, RSVP, initGadgetMixin);
\ No newline at end of file
+})(window, rJS, RSVP, initGadgetMixin, loopEventListener);
\ No newline at end of file
diff --git a/dream/platform/static/dream/Input_viewSimulation.html b/dream/platform/static/dream/Input_viewSimulation.html
index 0dbcbb7350bb16f17b7c03e9685e7d5ba7ea0a94..e061e22e6d58a60e42ca70372f0d2f500efdedc6 100644
--- a/dream/platform/static/dream/Input_viewSimulation.html
+++ b/dream/platform/static/dream/Input_viewSimulation.html
@@ -19,11 +19,9 @@
     <script src="Input_viewSimulation.js" type="text/javascript"></script>
   </head>
   <body>
-    <form>
+    <form class="save_form">
       <fieldset class="simulation_parameters">
       </fieldset>
-    </form>
-    <form class="run_form">
       <button type="submit" class="ui-btn ui-btn-b ui-btn-inline
         ui-icon-refresh ui-btn-icon-right">Run Simulation</button>
     </form>
diff --git a/dream/platform/static/dream/Input_viewSimulation.js b/dream/platform/static/dream/Input_viewSimulation.js
index a9f16fdc7be8ea9819387b0fc6caa80277b4a57d..a7dbb934b1a5eaaf6e84da6079a7173c462582d0 100644
--- a/dream/platform/static/dream/Input_viewSimulation.js
+++ b/dream/platform/static/dream/Input_viewSimulation.js
@@ -3,6 +3,97 @@
 /*jslint nomen: true */
 (function(window, rJS, RSVP, $, Handlebars, promiseEventListener, initGadgetMixin) {
     "use strict";
+    function saveForm(gadget) {
+        var general = {};
+        return new RSVP.Queue().push(function() {
+            var i, promise_list = [];
+            for (i = 0; i < gadget.props.gadget_list.length; i += 1) {
+                promise_list.push(gadget.props.gadget_list[i].getContent());
+            }
+            return RSVP.all(promise_list);
+        }).push(function(result_list) {
+            var i, result, key;
+            for (i = 0; i < result_list.length; i += 1) {
+                result = result_list[i];
+                for (key in result) {
+                    if (result.hasOwnProperty(key)) {
+                        // Drop empty
+                        if (result[key]) {
+                            general[key] = result[key];
+                        }
+                    }
+                }
+            }
+            // Always get a fresh version, to prevent deleting spreadsheet & co
+            return gadget.aq_getAttachment({
+                _id: gadget.props.jio_key,
+                _attachment: "body.json"
+            });
+        }).push(function(body) {
+            var data = JSON.parse(body);
+            data.general = general;
+            return gadget.aq_putAttachment({
+                _id: gadget.props.jio_key,
+                _attachment: "body.json",
+                _data: JSON.stringify(data, null, 2),
+                _mimetype: "application/json"
+            });
+        });
+    }
+    function runSimulation(gadget) {
+        return new RSVP.Queue().push(function() {
+            return gadget.aq_getAttachment({
+                _id: gadget.props.jio_key,
+                _attachment: "body.json"
+            });
+        }).push(function(body_json) {
+            // XXX Hardcoded relative URL
+            return gadget.aq_ajax({
+                url: "../../runSimulation",
+                type: "POST",
+                data: body_json,
+                headers: {
+                    "Content-Type": "application/json"
+                }
+            });
+        }).push(function(evt) {
+            var json_data = JSON.parse(evt.target.responseText);
+            if (json_data.success !== true) {
+                throw new Error(json_data.error);
+            }
+            return gadget.aq_putAttachment({
+                _id: gadget.props.jio_key,
+                _attachment: "simulation.json",
+                _data: JSON.stringify(json_data.data, null, 2),
+                _mimetype: "application/json"
+            });
+        }).push(function() {
+            return gadget.whoWantToDisplayThisDocument(gadget.props.jio_key, "view_result");
+        }).push(function(url) {
+            return gadget.pleaseRedirectMyHash(url);
+        });
+    }
+    function waitForRunSimulation(gadget) {
+        var submit_evt;
+        return new RSVP.Queue().push(function() {
+            return promiseEventListener(gadget.props.element.getElementsByClassName("save_form")[0], "submit", false);
+        }).push(function(evt) {
+            submit_evt = evt;
+            // Prevent double click
+            evt.target.getElementsByClassName("ui-btn")[0].disabled = true;
+            $.mobile.loading("show");
+            return saveForm(gadget);
+        }).push(function() {
+            return runSimulation(gadget);
+        }).push(undefined, function(error) {
+            // Always drop the loader
+            $.mobile.loading("hide");
+            throw error;
+        }).push(function() {
+            submit_evt.target.getElementsByClassName("ui-btn")[0].disabled = false;
+            $.mobile.loading("hide");
+        });
+    }
     /////////////////////////////////////////////////////////////////
     // Handlebars
     /////////////////////////////////////////////////////////////////
@@ -18,6 +109,9 @@
                 parent_element.insertAdjacentHTML("beforeend", label_template({
                     label: property.name || property.id
                 }));
+                if (property.type === "number") {
+                    return gadget.declareGadget("../number_field/index.html");
+                }
                 return gadget.declareGadget("../string_field/index.html");
             }).push(function(gg) {
                 sub_gadget = gg;
@@ -33,6 +127,7 @@
                 return sub_gadget.getElement();
             }).push(function(sub_element) {
                 parent_element.appendChild(sub_element);
+                gadget.props.gadget_list.push(sub_gadget);
             });
         }
         queue = gadget.aq_getAttachment({
@@ -43,6 +138,7 @@
             return gadget.aq_getConfigurationDict();
         }).push(function(configuration_dict) {
             var property_list = configuration_dict["Dream-Configuration"].property_list;
+            gadget.props.gadget_list = [];
             for (i = 0; i < property_list.length; i += 1) {
                 property = property_list[i];
                 if (property._class === "Dream.Property") {
@@ -53,47 +149,6 @@
         });
         return queue;
     }).declareMethod("startService", function() {
-        var gadget = this;
-        return new RSVP.Queue().push(function() {
-            return promiseEventListener(gadget.props.element.getElementsByClassName("run_form")[0], "submit", false);
-        }).push(function() {
-            // Prevent double click
-            gadget.props.element.getElementsByClassName("ui-btn")[0].disabled = true;
-            return gadget.aq_getAttachment({
-                _id: gadget.props.jio_key,
-                _attachment: "body.json"
-            });
-        }).push(function(body_json) {
-            $.mobile.loading("show");
-            // XXX Hardcoded relative URL
-            return gadget.aq_ajax({
-                url: "../../runSimulation",
-                type: "POST",
-                data: body_json,
-                headers: {
-                    "Content-Type": "application/json"
-                }
-            });
-        }).push(undefined, function(error) {
-            // Always drop the loader
-            $.mobile.loading("hide");
-            throw error;
-        }).push(function(evt) {
-            $.mobile.loading("hide");
-            var json_data = JSON.parse(evt.target.responseText);
-            if (json_data.success !== true) {
-                throw new Error(json_data.error);
-            }
-            return gadget.aq_putAttachment({
-                _id: gadget.props.jio_key,
-                _attachment: "simulation.json",
-                _data: JSON.stringify(json_data.data, null, 2),
-                _mimetype: "application/json"
-            });
-        }).push(function() {
-            return gadget.whoWantToDisplayThisDocument(gadget.props.jio_key, "view_result");
-        }).push(function(url) {
-            return gadget.pleaseRedirectMyHash(url);
-        });
+        return waitForRunSimulation(this);
     });
 })(window, rJS, RSVP, jQuery, Handlebars, promiseEventListener, initGadgetMixin);
\ No newline at end of file
diff --git a/dream/platform/static/dream/Input_viewWipPartSpreadsheet.html b/dream/platform/static/dream/Input_viewWipPartSpreadsheet.html
index 097e9bfe163a5e020d047183a3ef4ecad8db4858..2c5cda54d0131b0c724d58ddbcf6f48d3bf394ac 100644
--- a/dream/platform/static/dream/Input_viewWipPartSpreadsheet.html
+++ b/dream/platform/static/dream/Input_viewWipPartSpreadsheet.html
@@ -6,12 +6,19 @@
     <title>Edit Wip Part Spreadsheet</title>
     <script src="../lib/rsvp.min.js" type="text/javascript"></script>
     <script src="../lib/renderjs.min.js" type="text/javascript"></script>
+    <script src="../lib/jquery.js" type="text/javascript"></script>
+    <script src="../lib/jquerymobile.js" type="text/javascript"></script>
 
     <script src="mixin_gadget.js" type="text/javascript"></script>
+    <script src="mixin_promise.js" type="text/javascript"></script>
     <script src="Input_viewWipPartSpreadsheet.js" type="text/javascript"></script>
   </head>
   <body>
     <div data-gadget-url="../handsontable/index.html"
          data-gadget-scope="tableeditor"></div>
+    <form class="save_form">
+      <button type="submit" class="ui-btn ui-btn-b ui-btn-inline
+        ui-icon-edit ui-btn-icon-right">Save</button>
+    </form>
   </body>
 </html>
diff --git a/dream/platform/static/dream/Input_viewWipPartSpreadsheet.js b/dream/platform/static/dream/Input_viewWipPartSpreadsheet.js
index 196d1e4bdb8874cfbba39d231bb1defa48a948ad..e3d4fd93332b46a08fab80417fc19adcf25f074b 100644
--- a/dream/platform/static/dream/Input_viewWipPartSpreadsheet.js
+++ b/dream/platform/static/dream/Input_viewWipPartSpreadsheet.js
@@ -1,9 +1,41 @@
-/*global rJS, RSVP, initGadgetMixin */
-(function(window, rJS, RSVP, initGadgetMixin) {
+/*global rJS, RSVP, initGadgetMixin, loopEventListener */
+(function(window, rJS, RSVP, initGadgetMixin, loopEventListener) {
     "use strict";
+    function saveSpreadsheet(evt) {
+        var gadget = this, editor_data, editor_gadget;
+        return new RSVP.Queue().push(function() {
+            // Prevent double click
+            evt.target.getElementsByClassName("ui-btn")[0].disabled = true;
+            return gadget.getDeclaredGadget("tableeditor");
+        }).push(function(tablegadget) {
+            editor_gadget = tablegadget;
+            return editor_gadget.getData();
+        }).push(function(data) {
+            editor_data = data;
+            // Always get a fresh version, to prevent deleting spreadsheet & co
+            return gadget.aq_getAttachment({
+                _id: gadget.props.jio_key,
+                _attachment: "body.json"
+            });
+        }).push(function(body) {
+            var data = JSON.parse(body);
+            data.wip_part_spreadsheet = JSON.parse(editor_data);
+            return gadget.aq_putAttachment({
+                _id: gadget.props.jio_key,
+                _attachment: "body.json",
+                _data: JSON.stringify(data, null, 2),
+                _mimetype: "application/json"
+            });
+        }).push(function() {
+            evt.target.getElementsByClassName("ui-btn")[0].disabled = false;
+        });
+    }
+    function waitForSave(gadget) {
+        return loopEventListener(gadget.props.element.getElementsByClassName("save_form")[0], "submit", false, saveSpreadsheet.bind(gadget));
+    }
     var gadget_klass = rJS(window);
     initGadgetMixin(gadget_klass);
-    gadget_klass.declareAcquiredMethod("aq_getAttachment", "jio_getAttachment").declareMethod("render", function(options) {
+    gadget_klass.declareAcquiredMethod("aq_getAttachment", "jio_getAttachment").declareAcquiredMethod("aq_putAttachment", "jio_putAttachment").declareMethod("render", function(options) {
         var jio_key = options.id, gadget = this;
         gadget.props.jio_key = jio_key;
         return new RSVP.Queue().push(function() {
@@ -15,8 +47,9 @@
             return result_list[1].render(JSON.stringify(JSON.parse(result_list[0]).wip_part_spreadsheet));
         });
     }).declareMethod("startService", function() {
+        var gadget = this;
         return this.getDeclaredGadget("tableeditor").push(function(tableeditor) {
-            return tableeditor.startService();
+            return RSVP.all([ tableeditor.startService(), waitForSave(gadget) ]);
         });
     });
-})(window, rJS, RSVP, initGadgetMixin);
\ No newline at end of file
+})(window, rJS, RSVP, initGadgetMixin, loopEventListener);
\ No newline at end of file
diff --git a/dream/platform/static/dream/mixin_promise.js b/dream/platform/static/dream/mixin_promise.js
index e8e39d153c1c5dd508dfe89209426bcdbf808a4e..443ff53fd8e2c7a13c4791b011604ff06b79e30c 100644
--- a/dream/platform/static/dream/mixin_promise.js
+++ b/dream/platform/static/dream/mixin_promise.js
@@ -1,6 +1,42 @@
 /*global RSVP, FileReader */
+/*jslint unparam: true */
 (function(window, RSVP, FileReader) {
     "use strict";
+    window.loopEventListener = function(target, type, useCapture, callback) {
+        //////////////////////////
+        // Infinite event listener (promise is never resolved)
+        // eventListener is removed when promise is cancelled/rejected
+        //////////////////////////
+        var handle_event_callback, callback_promise;
+        function cancelResolver() {
+            if (callback_promise !== undefined && typeof callback_promise.cancel === "function") {
+                callback_promise.cancel();
+            }
+        }
+        function canceller() {
+            if (handle_event_callback !== undefined) {
+                target.removeEventListener(type, handle_event_callback, useCapture);
+            }
+            cancelResolver();
+        }
+        function itsANonResolvableTrap(resolve, reject) {
+            handle_event_callback = function(evt) {
+                evt.stopPropagation();
+                evt.preventDefault();
+                cancelResolver();
+                callback_promise = new RSVP.Queue().push(function() {
+                    return callback(evt);
+                }).push(undefined, function(error) {
+                    if (!(error instanceof RSVP.CancellationError)) {
+                        canceller();
+                        reject(error);
+                    }
+                });
+            };
+            target.addEventListener(type, handle_event_callback, useCapture);
+        }
+        return new RSVP.Promise(itsANonResolvableTrap, canceller);
+    };
     window.promiseEventListener = function(target, type, useCapture) {
         //////////////////////////
         // Resolve the promise as soon as the event is triggered
diff --git a/dream/platform/static/number_field/index.html b/dream/platform/static/number_field/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..c56d721745e55bce02761390c24ee9e4520a30a7
--- /dev/null
+++ b/dream/platform/static/number_field/index.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+    <meta name="viewport" content="width=device-width, user-scalable=no" />
+    <title>Number field</title>
+
+    <!-- renderjs -->
+    <script src="../lib/rsvp.min.js"></script>
+    <script src="../lib/renderjs.min.js"></script>
+    <!-- custom script -->
+    <script src="numberfield.js" type="text/javascript"></script>
+
+  </head>
+  <body>
+    <input type='number' step="any" data-mini="true" />
+  </body>
+</html>
diff --git a/dream/platform/static/number_field/numberfield.js b/dream/platform/static/number_field/numberfield.js
new file mode 100644
index 0000000000000000000000000000000000000000..3fa440391b0a9a373b88a5ebcd0cbcc47e535e95
--- /dev/null
+++ b/dream/platform/static/number_field/numberfield.js
@@ -0,0 +1,22 @@
+/*global window, rJS */
+(function(window, rJS) {
+    "use strict";
+    rJS(window).ready(function(gadget) {
+        return gadget.getElement().push(function(element) {
+            gadget.element = element;
+        });
+    }).declareMethod("render", function(options) {
+        var input = this.element.querySelector("input"), field_json = options.field_json || {};
+        input.setAttribute("value", field_json.value);
+        input.setAttribute("name", field_json.key);
+        input.setAttribute("title", field_json.title);
+    }).declareMethod("getContent", function() {
+        var input = this.element.querySelector("input"), result = {};
+        if (input.value !== "") {
+            result[input.getAttribute("name")] = parseFloat(input.value);
+        } else {
+            result[input.getAttribute("name")] = null;
+        }
+        return result;
+    });
+})(window, rJS);
\ No newline at end of file
diff --git a/dream/platform/static/toolbox/index.html b/dream/platform/static/toolbox/index.html
index 6cf36022f6b26e1299af30d56e10bd3bef65a740..1bf5bbb985a06cfc4bbaa1e10546fc9eadaf7496 100644
--- a/dream/platform/static/toolbox/index.html
+++ b/dream/platform/static/toolbox/index.html
@@ -9,12 +9,19 @@
     <script src="../lib/jquery-ui.js"></script>
     <script src="../lib/rsvp.min.js"></script>
     <script src="../lib/renderjs.min.js"></script>
+    <script src="../lib/handlebars.min.js"></script>
+
+    <script id="tool-template" type="text/x-handlebars-template">
+      <div id="{{key}}"
+           class="tool {{key}}">
+        {{name}}
+        <ul/>
+      </div>
+    </script>
     
     <script src="toolbox.js"></script>
   </head>
   <body>
-    <div id="tools">
-      <div id="tools-container"></div>
-    </div>
+    <div class="tools"></div>
   </body>
 </html>
diff --git a/dream/platform/static/toolbox/toolbox.css b/dream/platform/static/toolbox/toolbox.css
index 702e23c99d75d3edc2303982f0175e239001cbc4..96a2b2cf815de8238ac37b52bba4a3e6da260182 100644
--- a/dream/platform/static/toolbox/toolbox.css
+++ b/dream/platform/static/toolbox/toolbox.css
@@ -1 +1 @@
-#tools{border:1px solid #999;margin:20px 0;border-radius:10px;padding-bottom:20px}#tools-container{margin-bottom:4px}#tools .ui-button{margin:4px 0}.tool{border:1px solid #d3d3d3;box-shadow:1px 1px 2px #aaa;min-width:7em;height:3em;z-index:10001;color:gray;font-family:serif;font-style:italic;font-size:.9em;margin:.8em;display:inline-block;border-radius:5px;text-align:center;padding-top:1.3em}.Dream-Source,.Dream-BatchSource{border:1px solid #bbc;background-color:#ffe;background-image:linear-gradient(to bottom,#ffe 0,#dde 100%)}.Dream-Machine,.Dream-MachineJobShop,.Dream-BatchScrapMachine,.Dream-MachineManagedJob,.Dream-MouldAssembly{border:1px solid #cbc;background-color:#fef;background-image:linear-gradient(to bottom,#fef 0,#ede 100%)}.Dream-Queue,.Dream-QueueJobShop,.Dream-LineClearance,.Dream-QueueManagedJob,.Dream-ConditionalBuffer,.Dream-OrderDecomposition,.Dream-MouldAssemblyBuffer{border:1px solid #bcc;background-color:#eff;background-image:linear-gradient(to bottom,#eff 0,#dee 100%)}.Dream-Exit,.Dream-ExitJobShop{border:1px solid #ccb;background-color:#eef;background-image:linear-gradient(to bottom,#eef 0,#dde 100%)}.Dream-EventGenerator{border:1px solid #cba;background-color:#fdc;background-image:linear-gradient(to bottom,#fdc 0,#ecb 100%)}.Dream-BatchDecomposition,.Dream-BatchDecompositionStartTime,.Dream-BatchReassembly{border:1px solid #bcb;background-color:#dfd;background-image:linear-gradient(to bottom,#dfd 0,#cec 100%)}.Dream-Repairman{border:1px solid #cbb;background-color:#fdd;background-image:linear-gradient(to bottom,#fdd 0,#dcc 100%)}
\ No newline at end of file
+.tools{border:1px solid #999;margin:20px 0;border-radius:10px;padding-bottom:20px}.tools-container{margin-bottom:4px}.tools .ui-button{margin:4px 0}.tool{border:1px solid #d3d3d3;box-shadow:1px 1px 2px #aaa;min-width:7em;height:3em;z-index:10001;color:gray;font-family:serif;font-style:italic;font-size:.9em;margin:.8em;display:inline-block;border-radius:5px;text-align:center;padding-top:1.3em}.Dream-Source,.Dream-BatchSource{border:1px solid #bbc;background-color:#ffe;background-image:linear-gradient(to bottom,#ffe 0,#dde 100%)}.Dream-Machine,.Dream-MachineJobShop,.Dream-BatchScrapMachine,.Dream-MachineManagedJob,.Dream-MouldAssembly{border:1px solid #cbc;background-color:#fef;background-image:linear-gradient(to bottom,#fef 0,#ede 100%)}.Dream-Queue,.Dream-QueueJobShop,.Dream-LineClearance,.Dream-QueueManagedJob,.Dream-ConditionalBuffer,.Dream-OrderDecomposition,.Dream-MouldAssemblyBuffer{border:1px solid #bcc;background-color:#eff;background-image:linear-gradient(to bottom,#eff 0,#dee 100%)}.Dream-Exit,.Dream-ExitJobShop{border:1px solid #ccb;background-color:#eef;background-image:linear-gradient(to bottom,#eef 0,#dde 100%)}.Dream-EventGenerator{border:1px solid #cba;background-color:#fdc;background-image:linear-gradient(to bottom,#fdc 0,#ecb 100%)}.Dream-BatchDecomposition,.Dream-BatchDecompositionStartTime,.Dream-BatchReassembly{border:1px solid #bcb;background-color:#dfd;background-image:linear-gradient(to bottom,#dfd 0,#cec 100%)}.Dream-Repairman{border:1px solid #cbb;background-color:#fdd;background-image:linear-gradient(to bottom,#fdd 0,#dcc 100%)}
\ No newline at end of file
diff --git a/dream/platform/static/toolbox/toolbox.js b/dream/platform/static/toolbox/toolbox.js
index 8ff48200560ee467ea1cb2c16c596f4807d42aa8..f3a6f9c8ad8796efa535fb39611819af57ff2ec8 100644
--- a/dream/platform/static/toolbox/toolbox.js
+++ b/dream/platform/static/toolbox/toolbox.js
@@ -1,16 +1,29 @@
-/*global window, RSVP, $, rJS*/
-(function(window, RSVP, $, rJS) {
+/*global window, document, RSVP, rJS, Handlebars*/
+(function(window, document, RSVP, rJS, Handlebars) {
     "use strict";
-    rJS(window).declareAcquiredMethod("getConfigurationDict", "getConfigurationDict").declareMethod("startService", function() {
+    /*jslint nomen: true*/
+    var gadget_klass = rJS(window), tool_template_source = gadget_klass.__template_element.getElementById("tool-template").innerHTML, tool_template = Handlebars.compile(tool_template_source);
+    gadget_klass.declareAcquiredMethod("getConfigurationDict", "getConfigurationDict").declareMethod("render", function() {
         var g = this;
-        return RSVP.all([ g.getElement(), g.getConfigurationDict() ]).then(function(result) {
-            var render_element = $(result[0]).find("#tools-container");
-            $.each(result[1], function(key, val) {
-                var name = val.name || key.split("-")[1];
+        return new RSVP.Queue().push(function() {
+            return g.getConfigurationDict();
+        }).push(function(config) {
+            g.tools_container = document.createElement("div");
+            g.tools_container.className = "tools-container";
+            Object.keys(config).forEach(function(key) {
+                var name = config[key].name || key.split("-")[1];
                 if (key !== "Dream-Configuration") {
-                    render_element.append('<div id="' + key + '" class="tool ' + key + '">' + name + "<ul/></div>");
+                    g.tools_container.innerHTML += tool_template({
+                        key: key,
+                        name: name
+                    });
                 }
             });
         });
+    }).declareMethod("startService", function() {
+        var g = this;
+        return g.getElement().then(function(element) {
+            element.querySelector(".tools").appendChild(g.tools_container);
+        });
     });
-})(window, RSVP, $, rJS);
\ No newline at end of file
+})(window, document, RSVP, rJS, Handlebars);
\ No newline at end of file