From 55b313d3795097201bcb5663733082e2962e4164 Mon Sep 17 00:00:00 2001
From: Romain Courteaud <romain@nexedi.com>
Date: Mon, 2 Mar 2015 15:03:58 +0000
Subject: [PATCH] Update jIO development version.

---
 .../web_page_module/rjs_gadget_erp5_js.xml    |    6 +-
 .../web_page_module/rjs_jio_js.xml            | 1094 ++++++++++++-----
 2 files changed, 792 insertions(+), 308 deletions(-)

diff --git a/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_js.xml b/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_js.xml
index df3e28c8a9..bf3ef5ac66 100644
--- a/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_js.xml
+++ b/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_js.xml
@@ -327,7 +327,7 @@
           return jio_gadget.getAttachment.apply(jio_gadget, param_list);\n
         })\n
         .push(function (response) {\n
-          return jIO.util.readBlobAsText(response);\n
+          return jIO.util.readBlobAsText(response.data);\n
         })\n
         .push(function (event) {\n
           return {data: JSON.parse(event.target.result)};\n
@@ -726,7 +726,7 @@
             </item>
             <item>
                 <key> <string>serial</string> </key>
-                <value> <string>941.25415.51811.22323</string> </value>
+                <value> <string>941.25428.15657.11059</string> </value>
             </item>
             <item>
                 <key> <string>state</string> </key>
@@ -744,7 +744,7 @@
                     </tuple>
                     <state>
                       <tuple>
-                        <float>1425307454.32</float>
+                        <float>1425307985.31</float>
                         <string>GMT</string>
                       </tuple>
                     </state>
diff --git a/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_jio_js.xml b/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_jio_js.xml
index ba0a003bfc..a9db18b4fb 100644
--- a/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_jio_js.xml
+++ b/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_jio_js.xml
@@ -5659,6 +5659,19 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
   }\n
   util.readBlobAsArrayBuffer = readBlobAsArrayBuffer;\n
 \n
+//   function readBlobAsDataURL(blob) {\n
+//     var fr = new FileReader();\n
+//     return new RSVP.Promise(function (resolve, reject, notify) {\n
+//       fr.addEventListener("load", resolve);\n
+//       fr.addEventListener("error", reject);\n
+//       fr.addEventListener("progress", notify);\n
+//       fr.readAsDataURL(blob);\n
+//     }, function () {\n
+//       fr.abort();\n
+//     });\n
+//   }\n
+//   util.readBlobAsDataURL = readBlobAsDataURL;\n
+\n
 \n
 \n
 \n
@@ -5755,7 +5768,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
         .push(function (result) {\n
           if (post_function !== undefined) {\n
             return post_function.call(\n
-              context.__storage,\n
+              context,\n
               argument_list,\n
               result\n
             );\n
@@ -5781,13 +5794,18 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
     this.__storage = storage;\n
   }\n
 \n
-  declareMethod(JioProxyStorage, "put", checkId);\n
+  declareMethod(JioProxyStorage, "put", checkId, function (argument_list) {\n
+    return argument_list[0]._id;\n
+  });\n
   declareMethod(JioProxyStorage, "get", checkId, function (argument_list, result) {\n
+    // XXX Drop all _ properties\n
     // Put _id properties to the result\n
     result._id = argument_list[0]._id;\n
     return result;\n
   });\n
-  declareMethod(JioProxyStorage, "remove", checkId);\n
+  declareMethod(JioProxyStorage, "remove", checkId, function (argument_list) {\n
+    return argument_list[0]._id;\n
+  });\n
 \n
   // listeners\n
   declareMethod(JioProxyStorage, "post", function (param, storage, method_name) {\n
@@ -5847,6 +5865,14 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
 //     }\n
     checkId(param, storage, method_name);\n
     checkAttachmentId(param, storage, method_name);\n
+  }, function (argument_list, result) {\n
+    if (!(result.data instanceof Blob)) {\n
+      throw new jIO.util.jIOError(\n
+        "\'getAttachment\' (" + argument_list[0]._id + " , " + argument_list[0]._attachment + ") on \'" + this.__type + "\' does not return a Blob.",\n
+        501\n
+      );\n
+    }\n
+    return result;\n
   });\n
 \n
   JioProxyStorage.prototype.buildQuery = function () {\n
@@ -5964,8 +5990,166 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
  * http://www.gnu.org/licenses/lgpl.html\n
  */\n
 \n
+/*jslint nomen: true*/\n
+/*global jIO*/\n
+\n
+/**\n
+ * JIO Memory Storage. Type = \'memory\'.\n
+ * Memory browser "database" storage.\n
+ *\n
+ * Storage Description:\n
+ *\n
+ *     {\n
+ *       "type": "memory"\n
+ *     }\n
+ *\n
+ * @class MemoryStorage\n
+ */\n
+\n
+(function (jIO) {\n
+  "use strict";\n
+\n
+  /**\n
+   * The JIO MemoryStorage extension\n
+   *\n
+   * @class MemoryStorage\n
+   * @constructor\n
+   */\n
+  function MemoryStorage() {\n
+    this._database = {};\n
+  }\n
+\n
+  MemoryStorage.prototype.post = function (metadata) {\n
+    var doc_id = metadata._id;\n
+    if (doc_id === undefined) {\n
+      doc_id = jIO.util.generateUuid();\n
+    }\n
+    if (this._database.hasOwnProperty(doc_id)) {\n
+      // the document already exists\n
+      throw new jIO.util.jIOError("Cannot create a new document", 409);\n
+    }\n
+    metadata._id = doc_id;\n
+    return this.put(metadata);\n
+  };\n
+\n
+  MemoryStorage.prototype.put = function (metadata) {\n
+    if (!this._database.hasOwnProperty(metadata._id)) {\n
+      this._database[metadata._id] = {\n
+        attachments: {}\n
+      };\n
+    }\n
+    this._database[metadata._id].doc = JSON.stringify(metadata);\n
+    return metadata._id;\n
+  };\n
+\n
+  MemoryStorage.prototype.get = function (param) {\n
+    var doc,\n
+      key,\n
+      found = false,\n
+      attachments = {};\n
+\n
+    try {\n
+      doc = JSON.parse(this._database[param._id].doc);\n
+    } catch (error) {\n
+      if (error instanceof TypeError) {\n
+        throw new jIO.util.jIOError(\n
+          "Cannot find document: " + param._id,\n
+          404\n
+        );\n
+      }\n
+      throw error;\n
+    }\n
+\n
+    // XXX NotImplemented: list all attachments\n
+    for (key in this._database[param._id].attachments) {\n
+      if (this._database[param._id].attachments.hasOwnProperty(key)) {\n
+        found = true;\n
+        attachments[key] = {};\n
+      }\n
+    }\n
+    if (found) {\n
+      doc._attachments = attachments;\n
+    }\n
+    return doc;\n
+  };\n
+\n
+  MemoryStorage.prototype.remove = function (param) {\n
+    delete this._database[param._id];\n
+    return param._id;\n
+  };\n
+\n
+  MemoryStorage.prototype.getAttachment = function (param) {\n
+    try {\n
+      return {data: this._database[param._id].attachments[param._attachment]};\n
+    } catch (error) {\n
+      if (error instanceof TypeError) {\n
+        throw new jIO.util.jIOError(\n
+          "Cannot find attachment: " + param._id + " , " + param._attachment,\n
+          404\n
+        );\n
+      }\n
+      throw error;\n
+    }\n
+  };\n
+\n
+  MemoryStorage.prototype.putAttachment = function (param) {\n
+    var attachment_dict;\n
+    try {\n
+      attachment_dict = this._database[param._id].attachments;\n
+    } catch (error) {\n
+      if (error instanceof TypeError) {\n
+        throw new jIO.util.jIOError("Cannot find document: " + param._id, 404);\n
+      }\n
+      throw error;\n
+    }\n
+    attachment_dict[param._attachment] = param._blob;\n
+  };\n
+\n
+  MemoryStorage.prototype.removeAttachment = function (param) {\n
+    try {\n
+      delete this._database[param._id].attachments[param._attachment];\n
+    } catch (error) {\n
+      if (error instanceof TypeError) {\n
+        throw new jIO.util.jIOError(\n
+          "Cannot find document: " + param._id,\n
+          404\n
+        );\n
+      }\n
+      throw error;\n
+    }\n
+  };\n
+\n
+\n
+  MemoryStorage.prototype.hasCapacity = function (name) {\n
+    return (name === "list");\n
+  };\n
+\n
+  MemoryStorage.prototype.buildQuery = function () {\n
+    var rows = [],\n
+      i;\n
+    for (i in this._database) {\n
+      if (this._database.hasOwnProperty(i)) {\n
+        rows.push({\n
+          id: i,\n
+          value: {}\n
+        });\n
+\n
+      }\n
+    }\n
+    return rows;\n
+  };\n
+\n
+  jIO.addStorage(\'memory\', MemoryStorage);\n
+\n
+}(jIO));\n
+;/*\n
+ * Copyright 2013, Nexedi SA\n
+ * Released under the LGPL license.\n
+ * http://www.gnu.org/licenses/lgpl.html\n
+ */\n
+\n
 /*jslint nomen: true */\n
-/*global jIO, localStorage, window, Blob, Uint8Array, RSVP */\n
+/*global jIO, sessionStorage, localStorage, Blob, RSVP */\n
 \n
 /**\n
  * JIO Local Storage. Type = \'local\'.\n
@@ -5974,17 +6158,22 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
  * Storage Description:\n
  *\n
  *     {\n
- *       "type": "local"\n
+ *       "type": "local",\n
+ *       "sessiononly": false\n
  *     }\n
  *\n
  * @class LocalStorage\n
  */\n
 \n
-(function (jIO) {\n
+(function (jIO, sessionStorage, localStorage, Blob, RSVP) {\n
   "use strict";\n
 \n
-  function LocalStorage() {\n
-    return;\n
+  function LocalStorage(spec) {\n
+    if (spec.sessiononly === true) {\n
+      this._storage = sessionStorage;\n
+    } else {\n
+      this._storage = localStorage;\n
+    }\n
   }\n
 \n
   function restrictDocumentId(id) {\n
@@ -5994,50 +6183,42 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
     }\n
   }\n
 \n
-  /**\n
-   * Get a document\n
-   *\n
-   * @method get\n
-   * @param  {Object} param The given parameters\n
-   * @param  {Object} options The command options\n
-   */\n
   LocalStorage.prototype.get = function (param) {\n
     restrictDocumentId(param._id);\n
 \n
     var doc = {},\n
       attachments = {},\n
+      found = false,\n
       key;\n
 \n
-    for (key in localStorage) {\n
-      if (localStorage.hasOwnProperty(key)) {\n
+    for (key in this._storage) {\n
+      if (this._storage.hasOwnProperty(key)) {\n
         attachments[key] = {};\n
+        found = true;\n
       }\n
     }\n
-    if (attachments.length !== 0) {\n
+    if (found) {\n
       doc._attachments = attachments;\n
     }\n
     return doc;\n
   };\n
 \n
-  /**\n
-   * Get an attachment\n
-   *\n
-   * @method getAttachment\n
-   * @param  {Object} param The given parameters\n
-   * @param  {Object} options The command options\n
-   */\n
   LocalStorage.prototype.getAttachment = function (param) {\n
     restrictDocumentId(param._id);\n
 \n
-    var textstring = localStorage.getItem(param._attachment);\n
+    var textstring = this._storage.getItem(param._attachment);\n
 \n
     if (textstring === null) {\n
-      throw new jIO.util.jIOError("Cannot find attachment", 404);\n
+      throw new jIO.util.jIOError(\n
+        "Cannot find attachment " + param._attachment,\n
+        404\n
+      );\n
     }\n
-    return new Blob([textstring]);\n
+    return {data: new Blob([textstring])};\n
   };\n
 \n
   LocalStorage.prototype.putAttachment = function (param) {\n
+    var context = this;\n
     restrictDocumentId(param._id);\n
 \n
     // the document already exists\n
@@ -6047,13 +6228,13 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
         return jIO.util.readBlobAsText(param._blob);\n
       })\n
       .push(function (e) {\n
-        localStorage.setItem(param._attachment, e.target.result);\n
+        context._storage.setItem(param._attachment, e.target.result);\n
       });\n
   };\n
 \n
   LocalStorage.prototype.removeAttachment = function (param) {\n
     restrictDocumentId(param._id);\n
-    return localStorage.removeItem(param._attachment);\n
+    return this._storage.removeItem(param._attachment);\n
   };\n
 \n
 \n
@@ -6070,7 +6251,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
 \n
   jIO.addStorage(\'local\', LocalStorage);\n
 \n
-}(jIO));\n
+}(jIO, sessionStorage, localStorage, Blob, RSVP));\n
 ;/*\n
  * Copyright 2013, Nexedi SA\n
  * Released under the LGPL license.\n
@@ -6078,17 +6259,11 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
  */\n
 \n
 /*jslint nomen: true*/\n
-/*global window, jIO, RSVP, btoa, DOMParser, Blob, console*/\n
+/*global jIO, RSVP, DOMParser, Blob */\n
 \n
 // JIO Dav Storage Description :\n
 // {\n
 //   type: "dav",\n
-//   url: {string}\n
-//   // No Authentication Here\n
-// }\n
-\n
-// {\n
-//   type: "dav",\n
 //   url: {string},\n
 //   basic_login: {string} // Basic authentication\n
 // }\n
@@ -6097,7 +6272,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
 // curl --verbose  -X OPTION http://domain/\n
 // In the headers: "WWW-Authenticate: Basic realm="DAV-upload"\n
 \n
-(function (jIO) {\n
+(function (jIO, RSVP, DOMParser, Blob) {\n
   "use strict";\n
 \n
   function ajax(storage, options) {\n
@@ -6152,23 +6327,33 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
     if (typeof spec.url !== \'string\') {\n
       throw new TypeError("DavStorage \'url\' is not of type string");\n
     }\n
-    // Remove last slash\n
-    this._url = spec.url.replace(/\\/$/, \'\');\n
+    this._url = spec.url;\n
     // XXX digest login\n
     if (typeof spec.basic_login === \'string\') {\n
-//       this._auth_type = \'basic\';\n
       this._authorization = "Basic " + spec.basic_login;\n
-//     } else {\n
-//       this._auth_type = \'none\';\n
     }\n
 \n
   }\n
 \n
   DavStorage.prototype.put = function (param) {\n
-    // XXX Reject if param has other properties than _id\n
+    var id = restrictDocumentId(param._id);\n
+    delete param._id;\n
+    if (Object.getOwnPropertyNames(param).length > 0) {\n
+      // Reject if param has other properties than _id\n
+      throw new jIO.util.jIOError("Can not store properties: " +\n
+                                  Object.getOwnPropertyNames(param), 400);\n
+    }\n
     return ajax(this, {\n
       type: "MKCOL",\n
-      url: this._url + param._id\n
+      url: this._url + id\n
+    });\n
+  };\n
+\n
+  DavStorage.prototype.remove = function (param) {\n
+    var id = restrictDocumentId(param._id);\n
+    return ajax(this, {\n
+      type: "DELETE",\n
+      url: this._url + id\n
     });\n
   };\n
 \n
@@ -6184,6 +6369,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
           url: context._url + id,\n
           dataType: "text",\n
           headers: {\n
+            // Increasing this value is a performance killer\n
             Depth: "1"\n
           }\n
         });\n
@@ -6194,9 +6380,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
         // Extract all meta informations and return them to JSON\n
 \n
         var i,\n
-          result = {\n
-            "title": param._id\n
-          },\n
+          result = {},\n
           attachment = {},\n
           id,\n
           attachment_list = new DOMParser().parseFromString(\n
@@ -6216,7 +6400,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
             attachment[id] = {};\n
           }\n
         }\n
-        if (attachment.length !== 0) {\n
+        if (Object.getOwnPropertyNames(attachment).length > 0) {\n
           result._attachments = attachment;\n
         }\n
         return result;\n
@@ -6235,20 +6419,11 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
   DavStorage.prototype.putAttachment = function (param) {\n
     var id = restrictDocumentId(param._id);\n
     restrictAttachmentId(param._attachment);\n
-\n
     return ajax(this, {\n
       type: "PUT",\n
       url: this._url + id + param._attachment,\n
       data: param._blob\n
-    })\n
-      .push(undefined, function (error) {\n
-        if ((error.target !== undefined) &&\n
-            (error.target.status === 403)) {\n
-          throw new jIO.util.jIOError("Cannot find document", 404);\n
-        }\n
-        throw error;\n
-      });\n
-\n
+    });\n
   };\n
 \n
   DavStorage.prototype.getAttachment = function (param) {\n
@@ -6265,15 +6440,17 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
         });\n
       })\n
       .push(function (response) {\n
-        return new Blob(\n
-          [response.target.response],\n
+        return {data: new Blob(\n
+          [response.target.response || response.target.responseText],\n
           {"type": response.target.getResponseHeader(\'Content-Type\') ||\n
                    "application/octet-stream"}\n
-        );\n
+        )};\n
       }, function (error) {\n
         if ((error.target !== undefined) &&\n
             (error.target.status === 404)) {\n
-          throw new jIO.util.jIOError("Cannot find document", 404);\n
+          throw new jIO.util.jIOError("Cannot find attachment: "\n
+                                      + param._id + " , " + param._attachment,\n
+                                      404);\n
         }\n
         throw error;\n
       });\n
@@ -6295,7 +6472,9 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
       .push(undefined, function (error) {\n
         if ((error.target !== undefined) &&\n
             (error.target.status === 404)) {\n
-          throw new jIO.util.jIOError("Cannot find document", 404);\n
+          throw new jIO.util.jIOError("Cannot find attachment: "\n
+                                      + param._id + " , " + param._attachment,\n
+                                      404);\n
         }\n
         throw error;\n
       });\n
@@ -6330,7 +6509,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
 \n
   jIO.addStorage(\'dav\', DavStorage);\n
 \n
-}(jIO));\n
+}(jIO, RSVP, DOMParser, Blob));\n
 ;/*jslint nomen: true */\n
 /*global RSVP*/\n
 \n
@@ -6356,7 +6535,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
  * @class UnionStorage\n
  */\n
 \n
-(function (jIO) {\n
+(function (jIO, RSVP) {\n
   "use strict";\n
 \n
   /**\n
@@ -6512,7 +6691,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
 \n
   jIO.addStorage(\'union\', UnionStorage);\n
 \n
-}(jIO));\n
+}(jIO, RSVP));\n
 ;/*\n
  * Copyright 2013, Nexedi SA\n
  * Released under the LGPL license.\n
@@ -6525,10 +6704,9 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
 // }\n
 \n
 /*jslint nomen: true */\n
-/*global jIO, UriTemplate, FormData, RSVP, URI,\n
-  Blob, btoa */\n
+/*global jIO, UriTemplate, FormData, RSVP, URI, Blob*/\n
 \n
-(function (jIO, UriTemplate, RSVP, URI, Blob) {\n
+(function (jIO, UriTemplate, FormData, RSVP, URI, Blob) {\n
   "use strict";\n
 \n
   function getSiteDocument(storage) {\n
@@ -6589,8 +6767,6 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
           // action_type;\n
         result._id = param._id;\n
         result.portal_type = result._links.type.name;\n
-\n
-        result._attachments = attachments;\n
 \n
         // Remove all ERP5 hateoas links / convert them into jIO ID\n
         for (key in result) {\n
@@ -6600,6 +6776,8 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
             }\n
           }\n
         }\n
+\n
+        result._attachments = attachments;\n
 \n
         return result;\n
       });\n
@@ -6620,19 +6798,19 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
           // if Base_edit, do put URN\n
           // if others, do post URN (ie, unique new attachment name)\n
           // XXX Except this attachment name should be generated when\n
-          return new Blob(\n
+          return {data: new Blob(\n
             [JSON.stringify(result)],\n
             {"type": \'application/hal+json\'}\n
-          );\n
+          )};\n
         });\n
     }\n
     if (action === "links") {\n
       return getDocumentAndHateoas(this, param)\n
         .push(function (response) {\n
-          return new Blob(\n
+          return {data: new Blob(\n
             [JSON.stringify(JSON.parse(response.target.responseText))],\n
             {"type": \'application/hal+json\'}\n
-          );\n
+          )};\n
         });\n
     }\n
     if (action.indexOf(this._url) === 0) {\n
@@ -6649,20 +6827,21 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
         .push(function (evt) {\n
           var result = JSON.parse(evt.target.responseText);\n
           result._id = param._id;\n
-          return new Blob(\n
+          return {data: new Blob(\n
             [JSON.stringify(result)],\n
             {"type": evt.target.getResponseHeader("Content-Type")}\n
-          );\n
+          )};\n
         });\n
     }\n
-    throw new Error("ERP5: not support get attachment: " + action);\n
+    throw new jIO.util.jIOError("ERP5: not support get attachment: " + action,\n
+                                400);\n
   };\n
 \n
   ERP5Storage.prototype.putAttachment = function (metadata) {\n
     // Assert we use a callable on a document from the ERP5 site\n
     if (metadata._attachment.indexOf(this._url) !== 0) {\n
-      throw new Error("Can not store outside ERP5: " +\n
-                      metadata._attachment);\n
+      throw new jIO.util.jIOError("Can not store outside ERP5: " +\n
+                                  metadata._attachment, 400);\n
     }\n
 \n
     return new RSVP.Queue()\n
@@ -6732,7 +6911,6 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
           delete item._links;\n
           result.push({\n
             id: uri.segment(2),\n
-            doc: {},\n
             value: item\n
           });\n
         }\n
@@ -6742,10 +6920,10 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
 \n
   jIO.addStorage("erp5", ERP5Storage);\n
 \n
-}(jIO, UriTemplate, RSVP, URI, Blob));\n
-;/*jslint nomen: true, maxlen: 200*/\n
+}(jIO, UriTemplate, FormData, RSVP, URI, Blob));\n
+;/*jslint nomen: true*/\n
 /*global RSVP*/\n
-(function (jIO) {\n
+(function (jIO, RSVP) {\n
   "use strict";\n
 \n
   /**\n
@@ -6762,8 +6940,16 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
   QueryStorage.prototype.get = function () {\n
     return this._sub_storage.get.apply(this._sub_storage, arguments);\n
   };\n
-  QueryStorage.prototype.post = function () {\n
-    return this._sub_storage.post.apply(this._sub_storage, arguments);\n
+  QueryStorage.prototype.post = function (metadata) {\n
+//     return this._sub_storage.post.apply(this._sub_storage, arguments);\n
+\n
+    var doc_id = metadata._id;\n
+    if (doc_id === undefined) {\n
+      doc_id = jIO.util.generateUuid();\n
+    }\n
+    metadata._id = doc_id;\n
+    // Ensure there is no conflict?\n
+    return this.put(metadata);\n
   };\n
   QueryStorage.prototype.put = function () {\n
     return this._sub_storage.put.apply(this._sub_storage, arguments);\n
@@ -6778,18 +6964,10 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
     return this._sub_storage.putAttachment.apply(this._sub_storage, arguments);\n
   };\n
   QueryStorage.prototype.removeAttachment = function () {\n
-    return this._sub_storage.removeAttachment.apply(this._sub_storage, arguments);\n
+    return this._sub_storage.removeAttachment.apply(this._sub_storage,\n
+                                                    arguments);\n
   };\n
 \n
-  /**\n
-   * Retrieve documents.\n
-   * This method performs an .allDocs() call on the substorage,\n
-   * retrieving everything, then runs a query on the result.\n
-   *\n
-   * @method allDocs\n
-   * @param  {Object} command The given parameters\n
-   * @param  {Object} options The command options\n
-   */\n
   QueryStorage.prototype.hasCapacity = function (name) {\n
     if (name === "list") {\n
       return this._sub_storage.hasCapacity(name);\n
@@ -6799,7 +6977,6 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
   QueryStorage.prototype.buildQuery = function (options) {\n
     var substorage = this._sub_storage,\n
       context = this,\n
-//       sub_query_result,\n
       sub_options = {},\n
       is_manual_query_needed = false,\n
       is_manual_include_needed = false;\n
@@ -6808,17 +6985,22 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
 \n
       // Can substorage handle the queries if needed?\n
       try {\n
-        if (((options.query === undefined) || (substorage.hasCapacity("query"))) &&\n
-            ((options.sort_on === undefined) || (substorage.hasCapacity("sort"))) &&\n
-            ((options.select_list === undefined) || (substorage.hasCapacity("select"))) &&\n
-            ((options.limit === undefined) || (substorage.hasCapacity("limit")))) {\n
+        if (((options.query === undefined) ||\n
+             (substorage.hasCapacity("query"))) &&\n
+            ((options.sort_on === undefined) ||\n
+             (substorage.hasCapacity("sort"))) &&\n
+            ((options.select_list === undefined) ||\n
+             (substorage.hasCapacity("select"))) &&\n
+            ((options.limit === undefined) ||\n
+             (substorage.hasCapacity("limit")))) {\n
           sub_options.query = options.query;\n
           sub_options.sort_on = options.sort_on;\n
           sub_options.select_list = options.select_list;\n
           sub_options.limit = options.limit;\n
         }\n
       } catch (error) {\n
-        if ((error instanceof jIO.util.jIOError) && (error.status_code === 501)) {\n
+        if ((error instanceof jIO.util.jIOError) &&\n
+            (error.status_code === 501)) {\n
           is_manual_query_needed = true;\n
         } else {\n
           throw error;\n
@@ -6827,17 +7009,19 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
 \n
       // Can substorage include the docs if needed?\n
       try {\n
-        if ((is_manual_query_needed || (options.include_docs === true)) && (!substorage.hasCapacity("include"))) {\n
-          sub_options.include_docs = options.include_docs;\n
+        if ((is_manual_query_needed ||\n
+            (options.include_docs === true)) &&\n
+            (substorage.hasCapacity("include"))) {\n
+          sub_options.include_docs = true;\n
         }\n
       } catch (error) {\n
-        if ((error instanceof jIO.util.jIOError) && (error.status_code === 501)) {\n
+        if ((error instanceof jIO.util.jIOError) &&\n
+            (error.status_code === 501)) {\n
           is_manual_include_needed = true;\n
         } else {\n
           throw error;\n
         }\n
       }\n
-\n
 \n
       return substorage.buildQuery(sub_options)\n
 \n
@@ -6851,7 +7035,8 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
             return substorage.get({"_id": result[j].id})\n
               .push(undefined, function (error) {\n
                 // Document may have been dropped after listing\n
-                if ((error instanceof jIO.util.jIOError) && (error.status_code === 404)) {\n
+                if ((error instanceof jIO.util.jIOError) &&\n
+                    (error.status_code === 404)) {\n
                   return;\n
                 }\n
                 throw error;\n
@@ -6880,6 +7065,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
             result = original_result;\n
           }\n
           return result;\n
+\n
         })\n
 \n
         // Manual query if needed\n
@@ -6888,7 +7074,6 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
             len,\n
             i;\n
           if (is_manual_query_needed) {\n
-//             sub_query_result = result;\n
             len = result.length;\n
             for (i = 0; i < len; i += 1) {\n
               data_rows.push(result[i].doc);\n
@@ -6896,7 +7081,8 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
             if (options.select_list) {\n
               options.select_list.push("_id");\n
             }\n
-            result = jIO.QueryFactory.create(options.query || "", context._key_schema).\n
+            result = jIO.QueryFactory.create(options.query || "",\n
+                                             context._key_schema).\n
               exec(data_rows, options);\n
           }\n
           return result;\n
@@ -6931,143 +7117,15 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
           return result;\n
         });\n
 \n
-//           if (options.include_docs) {\n
-//             for (i = 0, l = filtered_docs.length; i < l; i += 1) {\n
-//               filtered_docs[i] = {\n
-//                 "id": filtered_docs[i]._id,\n
-//                 "doc": docs[filtered_docs[i]._id],\n
-//                 "value": options.select_list ? filtered_docs[i] : {}\n
-//               };\n
-//               delete filtered_docs[i].value._id;\n
-//             }\n
-//           } else {\n
-//             for (i = 0, l = filtered_docs.length; i < l; i += 1) {\n
-//               filtered_docs[i] = {\n
-//                 "id": filtered_docs[i]._id,\n
-//                 "value": options.select_list ? filtered_docs[i] : {}\n
-//               };\n
-//               delete filtered_docs[i].value._id;\n
-//             }\n
-//           }\n
-//           response.data.rows = filtered_docs;\n
-//           response.data.total_rows = filtered_docs.length;\n
-//           return response;\n
-//         });\n
-\n
-//       return jIO.QueryFactory.create(options.query || "", that._key_schema).\n
-//         exec(data_rows, options).\n
-//         then(function (filtered_docs) {\n
-//           // reconstruct filtered rows, preserving the order from docs\n
-//           if (options.include_docs) {\n
-//             for (i = 0, l = filtered_docs.length; i < l; i += 1) {\n
-//               filtered_docs[i] = {\n
-//                 "id": filtered_docs[i]._id,\n
-//                 "doc": docs[filtered_docs[i]._id],\n
-//                 "value": options.select_list ? filtered_docs[i] : {}\n
-//               };\n
-//               delete filtered_docs[i].value._id;\n
-//             }\n
-//           } else {\n
-//             for (i = 0, l = filtered_docs.length; i < l; i += 1) {\n
-//               filtered_docs[i] = {\n
-//                 "id": filtered_docs[i]._id,\n
-//                 "value": options.select_list ? filtered_docs[i] : {}\n
-//               };\n
-//               delete filtered_docs[i].value._id;\n
-//             }\n
-//           }\n
-//           response.data.rows = filtered_docs;\n
-//           response.data.total_rows = filtered_docs.length;\n
-//           return response;\n
-//         });\n
-\n
-    }\n
-\n
-//       }).then(function (response) {\n
-// \n
-//         ((options.include_docs === undefined) || context.hasCapacity("include")) &&\n
-//     }\n
-// \n
-//       return context.buildQuery.apply(context, arguments);\n
-//     }\n
-// \n
-// //       // we need the full documents in order to perform the query, will\n
-// //       // remove them later if they were not required.\n
-// //       include_docs = (options.include_docs || options.query) ? true : false;\n
-// \n
-//     console.log("QueryStorage: calling substorage buildQuery");\n
-//     return substorage.buildQuery.apply(substorage, arguments);\n
-\n
-\n
-//     return substorage.buildQuery.apply(substorage, arguments)\n
-//       .push(function (result) {\n
-//       });\n
-\n
-//     substorage.allDocs({\n
-//       "include_docs": include_docs\n
-//     }).then(function (response) {\n
-// \n
-//       var data_rows = response.data.rows, docs = {}, row, i, l;\n
-// \n
-//       if (!include_docs) {\n
-//         return response;\n
-//       }\n
-// \n
-//       if (options.include_docs) {\n
-//         for (i = 0, l = data_rows.length; i < l; i += 1) {\n
-//           row = data_rows[i];\n
-//           docs[row.id] = JSON.parse(JSON.stringify(row.doc));\n
-//           row.doc._id = row.id;\n
-//           data_rows[i] = row.doc;\n
-//         }\n
-//       } else {\n
-//         for (i = 0, l = data_rows.length; i < l; i += 1) {\n
-//           row = data_rows[i];\n
-//           row.doc._id = row.id;\n
-//           data_rows[i] = row.doc;\n
-//         }\n
-//       }\n
-// \n
-//       if (options.select_list) {\n
-//         options.select_list.push("_id");\n
-//       }\n
-// \n
-//       return jIO.QueryFactory.create(options.query || "", that._key_schema).\n
-//         exec(data_rows, options).\n
-//         then(function (filtered_docs) {\n
-//           // reconstruct filtered rows, preserving the order from docs\n
-//           if (options.include_docs) {\n
-//             for (i = 0, l = filtered_docs.length; i < l; i += 1) {\n
-//               filtered_docs[i] = {\n
-//                 "id": filtered_docs[i]._id,\n
-//                 "doc": docs[filtered_docs[i]._id],\n
-//                 "value": options.select_list ? filtered_docs[i] : {}\n
-//               };\n
-//               delete filtered_docs[i].value._id;\n
-//             }\n
-//           } else {\n
-//             for (i = 0, l = filtered_docs.length; i < l; i += 1) {\n
-//               filtered_docs[i] = {\n
-//                 "id": filtered_docs[i]._id,\n
-//                 "value": options.select_list ? filtered_docs[i] : {}\n
-//               };\n
-//               delete filtered_docs[i].value._id;\n
-//             }\n
-//           }\n
-//           response.data.rows = filtered_docs;\n
-//           response.data.total_rows = filtered_docs.length;\n
-//           return response;\n
-//         });\n
-// \n
-//     }).then(command.success, command.error, command.notify);\n
+    }\n
   };\n
 \n
   jIO.addStorage(\'query\', QueryStorage);\n
 \n
-}(jIO));\n
-;/*jslint nomen: true, maxlen: 200*/\n
-/*global console, RSVP, Blob*/\n
-(function (jIO) {\n
+}(jIO, RSVP));\n
+;/*jslint nomen: true*/\n
+/*global RSVP, Blob*/\n
+(function (jIO, RSVP, Blob) {\n
   "use strict";\n
 \n
   /**\n
@@ -7078,11 +7136,21 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
    */\n
   function FileSystemBridgeStorage(spec) {\n
     this._sub_storage = jIO.createJIO(spec.sub_storage);\n
-    this._document_key = "/.jio_documents/";\n
-    this._attachment_key = "/.jio_attachments/";\n
   }\n
   var DOCUMENT_EXTENSION = ".json",\n
+    DOCUMENT_REGEXP = new RegExp("^([\\\\w=]+)" +\n
+                                 DOCUMENT_EXTENSION + "$"),\n
+    DOCUMENT_KEY = "/.jio_documents/",\n
     ROOT = "/";\n
+\n
+  FileSystemBridgeStorage.prototype.post = function (metadata) {\n
+    var doc_id = metadata._id;\n
+    if (doc_id === undefined) {\n
+      doc_id = jIO.util.generateUuid();\n
+    }\n
+    metadata._id = doc_id;\n
+    return this.put(metadata);\n
+  };\n
 \n
   FileSystemBridgeStorage.prototype.get = function (param) {\n
     var context = this,\n
@@ -7095,21 +7163,22 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
       .push(function () {\n
         // First get the document itself if it exists\n
         return context._sub_storage.getAttachment({\n
-          "_id": context._document_key,\n
+          "_id": DOCUMENT_KEY,\n
           "_attachment": param._id + DOCUMENT_EXTENSION\n
         });\n
       })\n
       .push(function (blob) {\n
         return new RSVP.Queue()\n
           .push(function () {\n
-            return jIO.util.readBlobAsText(blob);\n
+            return jIO.util.readBlobAsText(blob.data);\n
           })\n
           .push(function (text) {\n
             explicit_document = true;\n
             return JSON.parse(text.target.result);\n
           });\n
       }, function (error) {\n
-        if ((error instanceof jIO.util.jIOError) && (error.status_code === 404)) {\n
+        if ((error instanceof jIO.util.jIOError) &&\n
+            (error.status_code === 404)) {\n
           return {};\n
         }\n
         throw error;\n
@@ -7126,52 +7195,44 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
       })\n
 \n
       .push(function (directory_document) {\n
-        if (directory_document._attachments.hasOwnProperty(param._id)) {\n
+        if ((directory_document.hasOwnProperty("_attachments")) &&\n
+            (directory_document._attachments.hasOwnProperty(param._id))) {\n
           json_document._attachments = {\n
             enclosure: {}\n
           };\n
         } else {\n
           if (!explicit_document) {\n
-            throw new jIO.util.jIOError("Cannot find document", 404);\n
+            throw new jIO.util.jIOError("Cannot find document " + param._id,\n
+                                        404);\n
           }\n
         }\n
         return json_document;\n
       });\n
 \n
   };\n
-\n
-  FileSystemBridgeStorage.prototype.post = function (param) {\n
-    var doc_id = param._id;\n
-\n
-    if (doc_id === undefined) {\n
-      doc_id = jIO.util.generateUuid();\n
-    }\n
-\n
-    param._id = doc_id;\n
-    return this.put(param);\n
-  };\n
 \n
   FileSystemBridgeStorage.prototype.put = function (param) {\n
     var context = this,\n
       doc_id = param._id;\n
     // XXX Handle conflict!\n
-    // XXX Put empty enclosure directly if json is empty\n
 \n
     return context._sub_storage.putAttachment({\n
-      "_id": context._document_key,\n
+      "_id": DOCUMENT_KEY,\n
       "_attachment": doc_id + DOCUMENT_EXTENSION,\n
       "_blob": new Blob([JSON.stringify(param)], {type: "application/json"})\n
     })\n
       .push(undefined, function (error) {\n
-        if ((error instanceof jIO.util.jIOError) && (error.status_code === 404)) {\n
+        if ((error instanceof jIO.util.jIOError) &&\n
+            (error.status_code === 404)) {\n
           return context._sub_storage.put({\n
-            "_id": context._document_key\n
+            "_id": DOCUMENT_KEY\n
           })\n
             .push(function () {\n
               return context._sub_storage.putAttachment({\n
-                "_id": context._document_key,\n
+                "_id": DOCUMENT_KEY,\n
                 "_attachment": doc_id + DOCUMENT_EXTENSION,\n
-                "_blob": new Blob([JSON.stringify(param)], {type: "application/json"})\n
+                "_blob": new Blob([JSON.stringify(param)],\n
+                                  {type: "application/json"})\n
               });\n
             });\n
         }\n
@@ -7198,7 +7259,8 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
       })\n
 \n
       .push(undefined, function (error) {\n
-        if ((error instanceof jIO.util.jIOError) && (error.status_code === 404)) {\n
+        if ((error instanceof jIO.util.jIOError) &&\n
+            (error.status_code === 404)) {\n
           got_error = true;\n
           return;\n
         }\n
@@ -7208,14 +7270,15 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
       // Second, try to remove explicit doc\n
       .push(function () {\n
         return context._sub_storage.removeAttachment({\n
-          "_id": context._document_key,\n
+          "_id": DOCUMENT_KEY,\n
           "_attachment": doc_id + DOCUMENT_EXTENSION\n
         });\n
       })\n
 \n
       .push(undefined, function (error) {\n
-        if ((!got_error) && (error instanceof jIO.util.jIOError) && (error.status_code === 404)) {\n
-          return;\n
+        if ((!got_error) && (error instanceof jIO.util.jIOError) &&\n
+            (error.status_code === 404)) {\n
+          return doc_id;\n
         }\n
         throw error;\n
       });\n
@@ -7223,10 +7286,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
   };\n
 \n
   FileSystemBridgeStorage.prototype.hasCapacity = function (capacity) {\n
-    if (capacity === "list") {\n
-      return true;\n
-    }\n
-    return false;\n
+    return (capacity === "list");\n
   };\n
 \n
   FileSystemBridgeStorage.prototype.buildQuery = function () {\n
@@ -7238,18 +7298,21 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
 \n
       .push(function () {\n
         return context._sub_storage.get({\n
-          "_id": context._document_key\n
+          "_id": DOCUMENT_KEY\n
         });\n
       })\n
       .push(function (result) {\n
         var key;\n
         for (key in result._attachments) {\n
           if (result._attachments.hasOwnProperty(key)) {\n
-            result_dict[key.slice(0, key.length - DOCUMENT_EXTENSION.length)] = null;\n
+            if (DOCUMENT_REGEXP.test(key)) {\n
+              result_dict[DOCUMENT_REGEXP.exec(key)[1]] = null;\n
+            }\n
           }\n
         }\n
       }, function (error) {\n
-        if ((error instanceof jIO.util.jIOError) && (error.status_code === 404)) {\n
+        if ((error instanceof jIO.util.jIOError) &&\n
+            (error.status_code === 404)) {\n
           return;\n
         }\n
         throw error;\n
@@ -7291,7 +7354,8 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
 \n
   FileSystemBridgeStorage.prototype.getAttachment = function (param) {\n
     if (param._attachment !== "enclosure") {\n
-      throw new Error("Only support \'enclosure\' attachment");\n
+      throw new jIO.util.jIOError("Only support \'enclosure\' attachment",\n
+                                  400);\n
     }\n
 \n
     return this._sub_storage.getAttachment({\n
@@ -7302,7 +7366,8 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
 \n
   FileSystemBridgeStorage.prototype.putAttachment = function (param) {\n
     if (param._attachment !== "enclosure") {\n
-      throw new Error("Only support \'enclosure\' attachment");\n
+      throw new jIO.util.jIOError("Only support \'enclosure\' attachment",\n
+                                  400);\n
     }\n
 \n
     return this._sub_storage.putAttachment({\n
@@ -7314,7 +7379,8 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
 \n
   FileSystemBridgeStorage.prototype.removeAttachment = function (param) {\n
     if (param._attachment !== "enclosure") {\n
-      throw new Error("Only support \'enclosure\' attachment");\n
+      throw new jIO.util.jIOError("Only support \'enclosure\' attachment",\n
+                                  400);\n
     }\n
 \n
     return this._sub_storage.removeAttachment({\n
@@ -7325,9 +7391,9 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
 \n
   jIO.addStorage(\'drivetojiomapping\', FileSystemBridgeStorage);\n
 \n
-}(jIO));\n
+}(jIO, RSVP, Blob));\n
 ;/*jslint nomen: true*/\n
-/*global console, Blob, atob, btoa*/\n
+/*global Blob, atob, btoa*/\n
 (function (jIO, Blob, atob, btoa) {\n
   "use strict";\n
 \n
@@ -7363,7 +7429,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
       "_attachment": getSubAttachmentIdFromParam(param)\n
     })\n
       .push(function (blob) {\n
-        return jIO.util.readBlobAsText(blob);\n
+        return jIO.util.readBlobAsText(blob.data);\n
       })\n
       .push(function (text) {\n
         return JSON.parse(text.target.result);\n
@@ -7382,8 +7448,15 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
           if (document._attachments.hasOwnProperty(key)) {\n
             if (ATTACHMENT_REGEXP.test(key)) {\n
               exec = ATTACHMENT_REGEXP.exec(key);\n
-              if (atob(exec[1]) === param._id) {\n
-                attachments[atob(exec[2])] = {};\n
+              try {\n
+                if (atob(exec[1]) === param._id) {\n
+                  attachments[atob(exec[2])] = {};\n
+                }\n
+              } catch (error) {\n
+                // Check if unable to decode base64 data\n
+                if (!error instanceof ReferenceError) {\n
+                  throw error;\n
+                }\n
               }\n
             }\n
           }\n
@@ -7394,17 +7467,6 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
         return result;\n
       });\n
   };\n
-\n
-  DocumentStorage.prototype.post = function (param) {\n
-    var doc_id = param._id;\n
-\n
-    if (doc_id === undefined) {\n
-      doc_id = jIO.util.generateUuid();\n
-    }\n
-\n
-    param._id = doc_id;\n
-    return this.put(param);\n
-  };\n
 \n
   DocumentStorage.prototype.put = function (param) {\n
     var doc_id = param._id;\n
@@ -7444,10 +7506,17 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
         for (key in document._attachments) {\n
           if (document._attachments.hasOwnProperty(key)) {\n
             if (DOCUMENT_REGEXP.test(key)) {\n
-              result.push({\n
-                id: atob(DOCUMENT_REGEXP.exec(key)[1]),\n
-                value: {}\n
-              });\n
+              try {\n
+                result.push({\n
+                  id: atob(DOCUMENT_REGEXP.exec(key)[1]),\n
+                  value: {}\n
+                });\n
+              } catch (error) {\n
+                // Check if unable to decode base64 data\n
+                if (!error instanceof ReferenceError) {\n
+                  throw error;\n
+                }\n
+              }\n
             }\n
           }\n
         }\n
@@ -7480,6 +7549,421 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
   jIO.addStorage(\'document\', DocumentStorage);\n
 \n
 }(jIO, Blob, atob, btoa));\n
+;/*\n
+ * Copyright 2014, Nexedi SA\n
+ * Released under the LGPL license.\n
+ * http://www.gnu.org/licenses/lgpl.html\n
+ */\n
+\n
+/**\n
+ * JIO Indexed Database Storage.\n
+ *\n
+ * A local browser "database" storage greatly more powerful than localStorage.\n
+ *\n
+ * Description:\n
+ *\n
+ *    {\n
+ *      "type": "indexeddb",\n
+ *      "database": <string>\n
+ *    }\n
+ *\n
+ * The database name will be prefixed by "jio:", so if the database property is\n
+ * "hello", then you can manually reach this database with\n
+ * `indexedDB.open("jio:hello");`. (Or\n
+ * `indexedDB.deleteDatabase("jio:hello");`.)\n
+ *\n
+ * For more informations:\n
+ *\n
+ * - http://www.w3.org/TR/IndexedDB/\n
+ * - https://developer.mozilla.org/en-US/docs/IndexedDB/Using_IndexedDB\n
+ */\n
+\n
+/*jslint nomen: true */\n
+/*global indexedDB, jIO, RSVP, Blob, Math*/\n
+\n
+(function (indexedDB, jIO, RSVP, Blob, Math) {\n
+  "use strict";\n
+\n
+  // Read only as changing it can lead to data corruption\n
+  var UNITE = 2000000;\n
+\n
+  function IndexedDBStorage(description) {\n
+    if (typeof description.database !== "string" ||\n
+        description.database === "") {\n
+      throw new TypeError("IndexedDBStorage \'database\' description property " +\n
+                          "must be a non-empty string");\n
+    }\n
+    this._database_name = "jio:" + description.database;\n
+  }\n
+\n
+  IndexedDBStorage.prototype.hasCapacity = function (name) {\n
+    return (name === "list");\n
+  };\n
+\n
+  function buildKeyPath(key_list) {\n
+    return key_list.join("_");\n
+  }\n
+\n
+  function handleUpgradeNeeded(evt) {\n
+    var db = evt.target.result,\n
+      store;\n
+\n
+    store = db.createObjectStore("metadata", {\n
+      keyPath: "_id",\n
+      autoIncrement: false\n
+    });\n
+    // It is not possible to use openKeyCursor on keypath directly\n
+    // https://www.w3.org/Bugs/Public/show_bug.cgi?id=19955\n
+    store.createIndex("_id", "_id", {unique: true});\n
+\n
+    store = db.createObjectStore("attachment", {\n
+      keyPath: "_key_path",\n
+      autoIncrement: false\n
+    });\n
+    store.createIndex("_id", "_id", {unique: false});\n
+\n
+    store = db.createObjectStore("blob", {\n
+      keyPath: "_key_path",\n
+      autoIncrement: false\n
+    });\n
+    store.createIndex("_id_attachment",\n
+                      ["_id", "_attachment"], {unique: false});\n
+    store.createIndex("_id", "_id", {unique: false});\n
+  }\n
+\n
+  function openIndexedDB(jio_storage) {\n
+    var db_name = jio_storage._database_name;\n
+    function resolver(resolve, reject) {\n
+      // Open DB //\n
+      var request = indexedDB.open(db_name);\n
+      request.onerror = function (error) {\n
+        if (request.result) {\n
+          request.result.close();\n
+        }\n
+        reject(error);\n
+      };\n
+\n
+      request.onabort = function () {\n
+        request.result.close();\n
+        reject("Aborting connection to: " + db_name);\n
+      };\n
+\n
+      request.ontimeout = function () {\n
+        request.result.close();\n
+        reject("Connection to: " + db_name + " timeout");\n
+      };\n
+\n
+      request.onblocked = function () {\n
+        request.result.close();\n
+        reject("Connection to: " + db_name + " was blocked");\n
+      };\n
+\n
+      // Create DB if necessary //\n
+      request.onupgradeneeded = handleUpgradeNeeded;\n
+\n
+      request.onversionchange = function () {\n
+        request.result.close();\n
+        reject(db_name + " was upgraded");\n
+      };\n
+\n
+      request.onsuccess = function () {\n
+        resolve(request.result);\n
+      };\n
+    }\n
+    // XXX Canceller???\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return new RSVP.Promise(resolver);\n
+      });\n
+  }\n
+\n
+  function openTransaction(db, stores, flag, autoclosedb) {\n
+    var tx = db.transaction(stores, flag);\n
+    if (autoclosedb !== false) {\n
+      tx.oncomplete = function () {\n
+        db.close();\n
+      };\n
+    }\n
+    tx.onabort = function () {\n
+      db.close();\n
+    };\n
+    return tx;\n
+  }\n
+\n
+  function handleCursor(request, callback) {\n
+    function resolver(resolve, reject) {\n
+      // Open DB //\n
+      request.onerror = function (error) {\n
+        if (request.transaction) {\n
+          request.transaction.abort();\n
+        }\n
+        reject(error);\n
+      };\n
+\n
+      request.onsuccess = function (evt) {\n
+        var cursor = evt.target.result;\n
+        if (cursor) {\n
+          // XXX Wait for result\n
+          try {\n
+            callback(cursor);\n
+          } catch (error) {\n
+            reject(error);\n
+          }\n
+\n
+          // continue to next iteration\n
+          cursor["continue"]();\n
+        } else {\n
+          resolve();\n
+        }\n
+      };\n
+    }\n
+    // XXX Canceller???\n
+    return new RSVP.Promise(resolver);\n
+  }\n
+\n
+  IndexedDBStorage.prototype.buildQuery = function () {\n
+    var result_list = [];\n
+\n
+    function pushMetadata(cursor) {\n
+      result_list.push({\n
+        "id": cursor.key,\n
+        "value": {}\n
+      });\n
+    }\n
+    return openIndexedDB(this)\n
+      .push(function (db) {\n
+        var tx = openTransaction(db, ["metadata"], "readonly");\n
+        return handleCursor(tx.objectStore("metadata").index("_id")\n
+                            .openKeyCursor(), pushMetadata);\n
+      })\n
+      .push(function () {\n
+        return result_list;\n
+      });\n
+\n
+  };\n
+\n
+  function handleGet(request) {\n
+    function resolver(resolve, reject) {\n
+      request.onerror = reject;\n
+      request.onsuccess = function () {\n
+        if (request.result) {\n
+          resolve(request.result);\n
+        }\n
+        // XXX How to get ID\n
+        reject(new jIO.util.jIOError("Cannot find document", 404));\n
+      };\n
+    }\n
+    return new RSVP.Promise(resolver);\n
+  }\n
+\n
+  IndexedDBStorage.prototype.get = function (param) {\n
+    var attachment_dict = {};\n
+\n
+    function addEntry(cursor) {\n
+      attachment_dict[cursor.value._attachment] = {};\n
+    }\n
+\n
+    return openIndexedDB(this)\n
+      .push(function (db) {\n
+        var transaction = openTransaction(db, ["metadata", "attachment"],\n
+                                          "readonly");\n
+        return RSVP.all([\n
+          handleGet(transaction.objectStore("metadata").get(param._id)),\n
+          handleCursor(transaction.objectStore("attachment").index("_id")\n
+                       .openCursor(), addEntry)\n
+        ]);\n
+      })\n
+      .push(function (result_list) {\n
+        var result = result_list[0];\n
+        if (Object.getOwnPropertyNames(attachment_dict).length > 0) {\n
+          result._attachments = attachment_dict;\n
+        }\n
+        return result;\n
+      });\n
+  };\n
+\n
+  function handleRequest(request) {\n
+    function resolver(resolve, reject) {\n
+      request.onerror = reject;\n
+      request.onsuccess = function () {\n
+        resolve(request.result);\n
+      };\n
+    }\n
+    return new RSVP.Promise(resolver);\n
+  }\n
+\n
+  IndexedDBStorage.prototype.put = function (metadata) {\n
+    return openIndexedDB(this)\n
+      .push(function (db) {\n
+        var transaction = openTransaction(db, ["metadata"], "readwrite");\n
+        return handleRequest(transaction.objectStore("metadata").put(metadata));\n
+      });\n
+  };\n
+\n
+  function deleteEntry(cursor) {\n
+    cursor["delete"]();\n
+  }\n
+\n
+  IndexedDBStorage.prototype.remove = function (param) {\n
+    return openIndexedDB(this)\n
+      .push(function (db) {\n
+        var transaction = openTransaction(db, ["metadata", "attachment",\n
+                                          "blob"], "readwrite");\n
+        return RSVP.all([\n
+          handleRequest(transaction\n
+                        .objectStore("metadata")["delete"](param._id)),\n
+          // XXX Why not possible to delete with KeyCursor?\n
+          handleCursor(transaction.objectStore("attachment").index("_id")\n
+                       .openCursor(), deleteEntry),\n
+          handleCursor(transaction.objectStore("blob").index("_id")\n
+                       .openCursor(), deleteEntry)\n
+        ]);\n
+      });\n
+  };\n
+\n
+  IndexedDBStorage.prototype.getAttachment = function (param) {\n
+    var transaction,\n
+      start,\n
+      end;\n
+    return openIndexedDB(this)\n
+      .push(function (db) {\n
+        transaction = openTransaction(db, ["attachment", "blob"], "readonly");\n
+        // XXX Should raise if key is not good\n
+        return handleGet(transaction.objectStore("attachment")\n
+                         .get(buildKeyPath([param._id, param._attachment])));\n
+      })\n
+      .push(function (attachment) {\n
+        var total_length = attachment.info.length,\n
+          i,\n
+          promise_list = [],\n
+          store = transaction.objectStore("blob"),\n
+          start_index,\n
+          end_index;\n
+\n
+        start = param._start || 0;\n
+        end = param._end || total_length;\n
+        if (end > total_length) {\n
+          end = total_length;\n
+        }\n
+\n
+        if (start < 0 || end < 0) {\n
+          throw new jIO.util.jIOError("_start and _end must be positive",\n
+                                      400);\n
+        }\n
+        if (start > end) {\n
+          throw new jIO.util.jIOError("_start is greater than _end",\n
+                                      400);\n
+        }\n
+\n
+        start_index = Math.floor(start / UNITE);\n
+        end_index =  Math.floor(end / UNITE);\n
+        if (end % UNITE === 0) {\n
+          end_index -= 1;\n
+        }\n
+\n
+        for (i = start_index; i <= end_index; i += 1) {\n
+          promise_list.push(\n
+            handleGet(store.get(buildKeyPath([param._id,\n
+                                param._attachment, i])))\n
+          );\n
+        }\n
+        return RSVP.all(promise_list);\n
+      })\n
+      .push(function (result_list) {\n
+        var array_buffer_list = [],\n
+          blob,\n
+          i,\n
+          len = result_list.length;\n
+        for (i = 0; i < len; i += 1) {\n
+          array_buffer_list.push(result_list[i].blob);\n
+        }\n
+        blob = new Blob(array_buffer_list, {type: "application/octet-stream"});\n
+        return {data: blob.slice(start, end)};\n
+      });\n
+  };\n
+\n
+  function removeAttachment(transaction, param) {\n
+    return RSVP.all([\n
+      // XXX How to get the right attachment\n
+      handleRequest(transaction.objectStore("attachment")["delete"](\n
+        buildKeyPath([param._id, param._attachment])\n
+      )),\n
+      handleCursor(transaction.objectStore("blob").index("_id_attachment")\n
+                   .openCursor(), deleteEntry)\n
+    ]);\n
+  }\n
+\n
+  IndexedDBStorage.prototype.putAttachment = function (metadata) {\n
+    var blob_part = [],\n
+      transaction,\n
+      db;\n
+\n
+    return openIndexedDB(this)\n
+      .push(function (database) {\n
+        db = database;\n
+\n
+        // Split the blob first\n
+        return jIO.util.readBlobAsArrayBuffer(metadata._blob);\n
+      })\n
+      .push(function (event) {\n
+        var array_buffer = event.target.result,\n
+          total_size = metadata._blob.size,\n
+          handled_size = 0;\n
+\n
+        while (handled_size < total_size) {\n
+          blob_part.push(array_buffer.slice(handled_size,\n
+                                            handled_size + UNITE));\n
+          handled_size += UNITE;\n
+        }\n
+\n
+        // Remove previous attachment\n
+        transaction = openTransaction(db, ["attachment", "blob"], "readwrite");\n
+        return removeAttachment(transaction, metadata);\n
+      })\n
+      .push(function () {\n
+\n
+        var promise_list = [\n
+            handleRequest(transaction.objectStore("attachment").put({\n
+              "_key_path": buildKeyPath([metadata._id, metadata._attachment]),\n
+              "_id": metadata._id,\n
+              "_attachment": metadata._attachment,\n
+              "info": {\n
+                "content_type": metadata._blob.type,\n
+                "length": metadata._blob.size\n
+              }\n
+            }))\n
+          ],\n
+          len = blob_part.length,\n
+          blob_store = transaction.objectStore("blob"),\n
+          i;\n
+        for (i = 0; i < len; i += 1) {\n
+          promise_list.push(\n
+            handleRequest(blob_store.put({\n
+              "_key_path": buildKeyPath([metadata._id, metadata._attachment,\n
+                                         i]),\n
+              "_id" : metadata._id,\n
+              "_attachment" : metadata._attachment,\n
+              "_part" : i,\n
+              "blob": blob_part[i]\n
+            }))\n
+          );\n
+        }\n
+        // Store all new data\n
+        return RSVP.all(promise_list);\n
+      });\n
+  };\n
+\n
+  IndexedDBStorage.prototype.removeAttachment = function (param) {\n
+    return openIndexedDB(this)\n
+      .push(function (db) {\n
+        var transaction = openTransaction(db, ["attachment", "blob"],\n
+                                          "readwrite");\n
+        return removeAttachment(transaction, param);\n
+      });\n
+  };\n
+\n
+  jIO.addStorage("indexeddb", IndexedDBStorage);\n
+}(indexedDB, jIO, RSVP, Blob, Math));\n
 
 
 ]]></string> </value>
@@ -7603,7 +8087,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
             </item>
             <item>
                 <key> <string>actor</string> </key>
-                <value> <string>romain</string> </value>
+                <value> <string>zope</string> </value>
             </item>
             <item>
                 <key> <string>comment</string> </key>
@@ -7617,7 +8101,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
             </item>
             <item>
                 <key> <string>serial</string> </key>
-                <value> <string>940.49252.16382.43810</string> </value>
+                <value> <string>941.25435.3378.34969</string> </value>
             </item>
             <item>
                 <key> <string>state</string> </key>
@@ -7635,7 +8119,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
                     </tuple>
                     <state>
                       <tuple>
-                        <float>1423064228.78</float>
+                        <float>1425307870.56</float>
                         <string>GMT</string>
                       </tuple>
                     </state>
-- 
2.30.9