diff --git a/bt5/erp5_simulation_fast_input/SkinTemplateItem/portal_skins/erp5_simulation_fast_input/gadget_solve_divergence_dialog.html.xml b/bt5/erp5_simulation_fast_input/SkinTemplateItem/portal_skins/erp5_simulation_fast_input/gadget_solve_divergence_dialog.html.xml
index 7aa469b0bb448b9001d309f7ff004a3840d066ff..ee855e4d5c5da5a48ae3a178b446acf8eaa1e5ca 100644
--- a/bt5/erp5_simulation_fast_input/SkinTemplateItem/portal_skins/erp5_simulation_fast_input/gadget_solve_divergence_dialog.html.xml
+++ b/bt5/erp5_simulation_fast_input/SkinTemplateItem/portal_skins/erp5_simulation_fast_input/gadget_solve_divergence_dialog.html.xml
@@ -12,7 +12,7 @@
         </item>
         <item>
             <key> <string>_EtagSupport__etag</string> </key>
-            <value> <string>ts47152215.08</string> </value>
+            <value> <string>ts49844638.75</string> </value>
         </item>
         <item>
             <key> <string>__name__</string> </key>
@@ -36,7 +36,6 @@
 \n
 <script src="rsvp.js" type="text/javascript"></script>\n
 <script src="renderjs.js" type="text/javascript"></script>\n
-<script src="jio_sha256.amd.js" type="text/javascript"></script>\n
 <script src="jio.js" type="text/javascript"></script>\n
 <script src="gadget_solve_divergence_dialog.js" type="text/javascript"></script>\n
 \n
@@ -55,7 +54,7 @@
         </item>
         <item>
             <key> <string>size</string> </key>
-            <value> <int>648</int> </value>
+            <value> <int>583</int> </value>
         </item>
         <item>
             <key> <string>title</string> </key>
diff --git a/bt5/erp5_simulation_fast_input/SkinTemplateItem/portal_skins/erp5_simulation_fast_input/gadget_solve_divergence_dialog.js.xml b/bt5/erp5_simulation_fast_input/SkinTemplateItem/portal_skins/erp5_simulation_fast_input/gadget_solve_divergence_dialog.js.xml
index 9dd27189c80a153668f695d66309ed4bfe9cd675..55801eba23fab859cdaa13981b59b5e6767a8701 100644
--- a/bt5/erp5_simulation_fast_input/SkinTemplateItem/portal_skins/erp5_simulation_fast_input/gadget_solve_divergence_dialog.js.xml
+++ b/bt5/erp5_simulation_fast_input/SkinTemplateItem/portal_skins/erp5_simulation_fast_input/gadget_solve_divergence_dialog.js.xml
@@ -12,7 +12,7 @@
         </item>
         <item>
             <key> <string>_EtagSupport__etag</string> </key>
-            <value> <string>ts47172081.7</string> </value>
+            <value> <string>ts49844688.61</string> </value>
         </item>
         <item>
             <key> <string>__name__</string> </key>
@@ -33,7 +33,6 @@
 // modify an existing dialog, we do not have nice structure of a full\n
 // renderjs web page\n
 \n
-\n
 // horrible copy/paste, this code is now present in latest renderjs,\n
 // until we can use it, just duplicate the code and remove it when\n
 // renderjs is up to date\n
@@ -45,7 +44,6 @@ function loopEventListener(target, type, useCapture, callback) {\n
   //////////////////////////\n
   var handle_event_callback,\n
     callback_promise;\n
-\n
   function cancelResolver() {\n
     if ((callback_promise !== undefined) &&\n
         (typeof callback_promise.cancel === "function")) {\n
@@ -153,7 +151,7 @@ function loopEventListener(target, type, useCapture, callback) {\n
         </item>
         <item>
             <key> <string>size</string> </key>
-            <value> <int>4104</int> </value>
+            <value> <int>4102</int> </value>
         </item>
         <item>
             <key> <string>title</string> </key>
diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/ComponentTool_viewLiveTestDialog.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/ComponentTool_viewLiveTestDialog.xml
index 3adb2b1a8f3defacdff8256d61932b1af69dfa3a..ee4f471ab6220277279b8d062b6472505c35e899 100644
--- a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/ComponentTool_viewLiveTestDialog.xml
+++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/ComponentTool_viewLiveTestDialog.xml
@@ -76,7 +76,7 @@
                     <value>
                       <list>
                         <string>your_text_output</string>
-                        <string>test_watcher_gadget</string>
+                        <string>live_test_gadget</string>
                       </list>
                     </value>
                 </item>
diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_activity_watcher.html.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_activity_watcher.html.xml
index 2e0f455e0dc1a66591b06bd0fea513543fc61ce5..31ca9280ab45be2b2ab1f5b21a43f5e66ee45931 100644
--- a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_activity_watcher.html.xml
+++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_activity_watcher.html.xml
@@ -12,7 +12,7 @@
         </item>
         <item>
             <key> <string>_EtagSupport__etag</string> </key>
-            <value> <string>ts16502968.34</string> </value>
+            <value> <string>ts49761304.12</string> </value>
         </item>
         <item>
             <key> <string>__name__</string> </key>
@@ -96,7 +96,6 @@
 \n
 <script src="rsvp.js" type="text/javascript"></script>\n
 <script src="renderjs.js" type="text/javascript"></script>\n
-<script src="jio_sha256.amd.js" type="text/javascript"></script>\n
 <script src="jio.js" type="text/javascript"></script>\n
 <script src="gadget_activity_watcher.js" type="text/javascript"></script>\n
 \n
@@ -115,7 +114,7 @@
         </item>
         <item>
             <key> <string>size</string> </key>
-            <value> <int>2109</int> </value>
+            <value> <int>2044</int> </value>
         </item>
         <item>
             <key> <string>title</string> </key>
diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_activity_watcher.js.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_activity_watcher.js.xml
index 12458e4934e285708d6f047e85035dbbe47458db..efb3a3da7fb6af415409458c2ecce0d8247fecea 100644
--- a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_activity_watcher.js.xml
+++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_activity_watcher.js.xml
@@ -12,7 +12,7 @@
         </item>
         <item>
             <key> <string>_EtagSupport__etag</string> </key>
-            <value> <string>ts16394919.58</string> </value>
+            <value> <string>ts49077029.26</string> </value>
         </item>
         <item>
             <key> <string>__name__</string> </key>
@@ -33,7 +33,6 @@
   var gk = rJS(window),\n
     data_source = gk.__template_element.getElementById(\'getData\').innerHTML,\n
     get_data_template = Handlebars.compile(data_source);\n
-\n
   function putMessageType(data, messagetype, string) {\n
     var i;\n
     if (data[string] && data[string].line_list) {\n
@@ -106,7 +105,7 @@
         </item>
         <item>
             <key> <string>size</string> </key>
-            <value> <int>2318</int> </value>
+            <value> <int>2317</int> </value>
         </item>
         <item>
             <key> <string>title</string> </key>
diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_live_tests.html.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_live_tests.html.xml
index 8b079f545620d8371ca634339d061507f8ea120a..7893b83143805e2ddd938dede5f625d1337b1ae2 100644
--- a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_live_tests.html.xml
+++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_live_tests.html.xml
@@ -38,7 +38,6 @@
 
 <html>\n
   <head>\n
-    <script src="jio_sha256.amd.js" type="text/javascript"></script>\n
     <script src="jio.js" type="text/javascript"></script>\n
     <script src="gadget_live_tests.js" type="text/javascript"></script>\n
   </head>\n
diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_live_tests.js.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_live_tests.js.xml
index 7c8fe2007aedeb6531309b11c944376b245b6979..b4a8ec5a25fc0d55a85f65ebfe69d7347179306f 100644
--- a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_live_tests.js.xml
+++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_live_tests.js.xml
@@ -36,12 +36,11 @@
             <key> <string>_text</string> </key>
             <value> <unicode encoding="cdata"><![CDATA[
 
-/*global window, rJS, jIO, RSVP, location, document , FormData, console */\n
+/*global window, rJS, jIO, RSVP, location, document, FormData */\n
 /*jslint indent: 2, maxlen: 80, nomen: true */\n
-(function (rJS, jIO, RSVP, window, document, FormData, console) {\n
+(function (rJS, jIO, RSVP, window, document, FormData) {\n
   "use strict";\n
-  var live_test_js_already_loaded = false,\n
-    my_url_run_test = document.baseURI + \'runLiveTest\',\n
+  var my_url_run_test = document.baseURI + \'runLiveTest\',\n
     my_url_read_test = document.baseURI + \'readTestOutput\',\n
     paused = false,\n
     data_textarea =\n
@@ -51,12 +50,6 @@
     last_call = false,\n
     data_size = 0,\n
     form_data = new FormData();\n
-\n
-  if (live_test_js_already_loaded) {\n
-    console.error("Live test already loaded");\n
-    return;\n
-  }\n
-  live_test_js_already_loaded = true;\n
 \n
   data_textarea.value = "";\n
 \n
@@ -88,7 +81,7 @@
   rJS(window).declareService(function () {\n
     var queue = new RSVP.Queue();\n
 \n
-    function readDataExamine() {\n
+    function launchLiveTest() {\n
       queue.push(function () {\n
         return jIO.util.ajax({\n
           type: "POST",\n
@@ -105,12 +98,12 @@
       });\n
     }\n
     return queue.push(function () {\n
-      return readDataExamine();\n
+      return launchLiveTest();\n
     });\n
   }).declareService(function () {\n
     var queue = new RSVP.Queue();\n
 \n
-    function getDataExamine() {\n
+    function getLiveTestOutput() {\n
       queue.push(function () {\n
         return jIO.util.ajax({\n
           type: "POST",\n
@@ -130,21 +123,23 @@
         return RSVP.delay(1000);\n
       }).push(function () {\n
         if (continue_loop) {\n
-          return getDataExamine();\n
+          return getLiveTestOutput();\n
         }\n
         if (!continue_loop) {\n
           if (!last_call) {\n
             last_call = true;\n
-            return getDataExamine();\n
+            return getLiveTestOutput();\n
           }\n
         }\n
       });\n
     }\n
     return queue.push(function () {\n
-      return getDataExamine();\n
-    });\n
+      // a delay of 2 seconds so the test can be launched\n
+      // before results are read\n
+      return RSVP.delay(2000);\n
+    }).push(getLiveTestOutput());\n
   });\n
-}(rJS, jIO, RSVP, window, document, FormData, console));
+}(rJS, jIO, RSVP, window, document, FormData));
 
 ]]></unicode> </value>
         </item>
diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/jio.js.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/jio.js.xml
index 161e6aab2ca16eca4d2ac174aad30d7dd29e8354..4bd720052cb6456ab89114b41aa0c28758f1798a 100644
--- a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/jio.js.xml
+++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/jio.js.xml
@@ -12,7 +12,7 @@
         </item>
         <item>
             <key> <string>_EtagSupport__etag</string> </key>
-            <value> <string>ts12610905.15</string> </value>
+            <value> <string>ts49761126.82</string> </value>
         </item>
         <item>
             <key> <string>__name__</string> </key>
@@ -26,3898 +26,3905 @@
             <key> <string>data</string> </key>
             <value> <string encoding="cdata"><![CDATA[
 
-(function (dependencies, module) {\n
-  "use strict";\n
-  if (typeof define === \'function\' && define.amd) {\n
-    return define(dependencies, module);\n
-  }\n
-  window.jIO = {};\n
-  module(window.jIO, RSVP, {hex_sha256: hex_sha256});\n
-}([\'exports\', \'rsvp\', \'sha256\'], function (exports, RSVP, sha256) {\n
-  "use strict";\n
-\n
-  var hex_sha256 = sha256.hex_sha256;\n
+/*! URI.js v1.12.0 http://medialize.github.com/URI.js/ */\n
+/* build contains: IPv6.js, punycode.js, SecondLevelDomains.js, URI.js, URI.fragmentQuery.js */\n
+(function(e,k){"object"===typeof exports?module.exports=k():"function"===typeof define&&define.amd?define(k):e.IPv6=k(e)})(this,function(e){var k=e&&e.IPv6;return{best:function(e){e=e.toLowerCase().split(":");var k=e.length,d=8;""===e[0]&&""===e[1]&&""===e[2]?(e.shift(),e.shift()):""===e[0]&&""===e[1]?e.shift():""===e[k-1]&&""===e[k-2]&&e.pop();k=e.length;-1!==e[k-1].indexOf(".")&&(d=7);var g;for(g=0;g<k&&""!==e[g];g++);if(g<d)for(e.splice(g,1,"0000");e.length<d;)e.splice(g,0,"0000");for(g=0;g<d;g++){for(var k=\n
+e[g].split(""),q=0;3>q;q++)if("0"===k[0]&&1<k.length)k.splice(0,1);else break;e[g]=k.join("")}var k=-1,l=q=0,r=-1,z=!1;for(g=0;g<d;g++)z?"0"===e[g]?l+=1:(z=!1,l>q&&(k=r,q=l)):"0"==e[g]&&(z=!0,r=g,l=1);l>q&&(k=r,q=l);1<q&&e.splice(k,q,"");k=e.length;d="";""===e[0]&&(beststr=":");for(g=0;g<k;g++){d+=e[g];if(g===k-1)break;d+=":"}""===e[k-1]&&(d+=":");return d},noConflict:function(){e.IPv6===this&&(e.IPv6=k);return this}}});\n
+(function(e){function k(a){throw RangeError(p[a]);}function u(a,b){for(var c=a.length;c--;)a[c]=b(a[c]);return a}function m(a,b){return u(a.split(h),b).join(".")}function d(a){for(var b=[],c=0,d=a.length,h,p;c<d;)h=a.charCodeAt(c++),55296<=h&&56319>=h&&c<d?(p=a.charCodeAt(c++),56320==(p&64512)?b.push(((h&1023)<<10)+(p&1023)+65536):(b.push(h),c--)):b.push(h);return b}function g(a){return u(a,function(a){var b="";65535<a&&(a-=65536,b+=x(a>>>10&1023|55296),a=56320|a&1023);return b+=x(a)}).join("")}function q(a,\n
+b){return a+22+75*(26>a)-((0!=b)<<5)}function l(a,b,c){var d=0;a=c?A(a/H):a>>1;for(a+=A(a/b);a>n*y>>1;d+=s)a=A(a/n);return A(d+(n+1)*a/(a+I))}function r(b){var c=[],d=b.length,h,p=0,e=F,f=G,n,x,q,t,m;n=b.lastIndexOf(a);0>n&&(n=0);for(x=0;x<n;++x)128<=b.charCodeAt(x)&&k("not-basic"),c.push(b.charCodeAt(x));for(n=0<n?n+1:0;n<d;){x=p;h=1;for(q=s;;q+=s){n>=d&&k("invalid-input");t=b.charCodeAt(n++);t=10>t-48?t-22:26>t-65?t-65:26>t-97?t-97:s;(t>=s||t>A((w-p)/h))&&k("overflow");p+=t*h;m=q<=f?v:q>=f+y?y:\n
+q-f;if(t<m)break;t=s-m;h>A(w/t)&&k("overflow");h*=t}h=c.length+1;f=l(p-x,h,0==x);A(p/h)>w-e&&k("overflow");e+=A(p/h);p%=h;c.splice(p++,0,e)}return g(c)}function z(b){var c,h,p,e,f,n,g,m,r,t=[],B,u,z;b=d(b);B=b.length;c=F;h=0;f=G;for(n=0;n<B;++n)r=b[n],128>r&&t.push(x(r));for((p=e=t.length)&&t.push(a);p<B;){g=w;for(n=0;n<B;++n)r=b[n],r>=c&&r<g&&(g=r);u=p+1;g-c>A((w-h)/u)&&k("overflow");h+=(g-c)*u;c=g;for(n=0;n<B;++n)if(r=b[n],r<c&&++h>w&&k("overflow"),r==c){m=h;for(g=s;;g+=s){r=g<=f?v:g>=f+y?y:g-f;\n
+if(m<r)break;z=m-r;m=s-r;t.push(x(q(r+z%m,0)));m=A(z/m)}t.push(x(q(m,0)));f=l(h,u,p==e);h=0;++p}++h;++c}return t.join("")}var D="object"==typeof exports&&exports,E="object"==typeof module&&module&&module.exports==D&&module,C="object"==typeof global&&global;if(C.global===C||C.window===C)e=C;var f,w=2147483647,s=36,v=1,y=26,I=38,H=700,G=72,F=128,a="-",b=/^xn--/,c=/[^ -~]/,h=/\\x2E|\\u3002|\\uFF0E|\\uFF61/g,p={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)",\n
+"invalid-input":"Invalid input"},n=s-v,A=Math.floor,x=String.fromCharCode,B;f={version:"1.2.3",ucs2:{decode:d,encode:g},decode:r,encode:z,toASCII:function(a){return m(a,function(a){return c.test(a)?"xn--"+z(a):a})},toUnicode:function(a){return m(a,function(a){return b.test(a)?r(a.slice(4).toLowerCase()):a})}};if("function"==typeof define&&"object"==typeof define.amd&&define.amd)define(function(){return f});else if(D&&!D.nodeType)if(E)E.exports=f;else for(B in f)f.hasOwnProperty(B)&&(D[B]=f[B]);else e.punycode=\n
+f})(this);\n
+(function(e,k){"object"===typeof exports?module.exports=k():"function"===typeof define&&define.amd?define(k):e.SecondLevelDomains=k(e)})(this,function(e){var k=e&&e.SecondLevelDomains,u=Object.prototype.hasOwnProperty,m={list:{ac:"com|gov|mil|net|org",ae:"ac|co|gov|mil|name|net|org|pro|sch",af:"com|edu|gov|net|org",al:"com|edu|gov|mil|net|org",ao:"co|ed|gv|it|og|pb",ar:"com|edu|gob|gov|int|mil|net|org|tur",at:"ac|co|gv|or",au:"asn|com|csiro|edu|gov|id|net|org",ba:"co|com|edu|gov|mil|net|org|rs|unbi|unmo|unsa|untz|unze",bb:"biz|co|com|edu|gov|info|net|org|store|tv",\n
+bh:"biz|cc|com|edu|gov|info|net|org",bn:"com|edu|gov|net|org",bo:"com|edu|gob|gov|int|mil|net|org|tv",br:"adm|adv|agr|am|arq|art|ato|b|bio|blog|bmd|cim|cng|cnt|com|coop|ecn|edu|eng|esp|etc|eti|far|flog|fm|fnd|fot|fst|g12|ggf|gov|imb|ind|inf|jor|jus|lel|mat|med|mil|mus|net|nom|not|ntr|odo|org|ppg|pro|psc|psi|qsl|rec|slg|srv|tmp|trd|tur|tv|vet|vlog|wiki|zlg",bs:"com|edu|gov|net|org",bz:"du|et|om|ov|rg",ca:"ab|bc|mb|nb|nf|nl|ns|nt|nu|on|pe|qc|sk|yk",ck:"biz|co|edu|gen|gov|info|net|org",cn:"ac|ah|bj|com|cq|edu|fj|gd|gov|gs|gx|gz|ha|hb|he|hi|hl|hn|jl|js|jx|ln|mil|net|nm|nx|org|qh|sc|sd|sh|sn|sx|tj|tw|xj|xz|yn|zj",\n
+co:"com|edu|gov|mil|net|nom|org",cr:"ac|c|co|ed|fi|go|or|sa",cy:"ac|biz|com|ekloges|gov|ltd|name|net|org|parliament|press|pro|tm","do":"art|com|edu|gob|gov|mil|net|org|sld|web",dz:"art|asso|com|edu|gov|net|org|pol",ec:"com|edu|fin|gov|info|med|mil|net|org|pro",eg:"com|edu|eun|gov|mil|name|net|org|sci",er:"com|edu|gov|ind|mil|net|org|rochest|w",es:"com|edu|gob|nom|org",et:"biz|com|edu|gov|info|name|net|org",fj:"ac|biz|com|info|mil|name|net|org|pro",fk:"ac|co|gov|net|nom|org",fr:"asso|com|f|gouv|nom|prd|presse|tm",\n
+gg:"co|net|org",gh:"com|edu|gov|mil|org",gn:"ac|com|gov|net|org",gr:"com|edu|gov|mil|net|org",gt:"com|edu|gob|ind|mil|net|org",gu:"com|edu|gov|net|org",hk:"com|edu|gov|idv|net|org",id:"ac|co|go|mil|net|or|sch|web",il:"ac|co|gov|idf|k12|muni|net|org","in":"ac|co|edu|ernet|firm|gen|gov|i|ind|mil|net|nic|org|res",iq:"com|edu|gov|i|mil|net|org",ir:"ac|co|dnssec|gov|i|id|net|org|sch",it:"edu|gov",je:"co|net|org",jo:"com|edu|gov|mil|name|net|org|sch",jp:"ac|ad|co|ed|go|gr|lg|ne|or",ke:"ac|co|go|info|me|mobi|ne|or|sc",\n
+kh:"com|edu|gov|mil|net|org|per",ki:"biz|com|de|edu|gov|info|mob|net|org|tel",km:"asso|com|coop|edu|gouv|k|medecin|mil|nom|notaires|pharmaciens|presse|tm|veterinaire",kn:"edu|gov|net|org",kr:"ac|busan|chungbuk|chungnam|co|daegu|daejeon|es|gangwon|go|gwangju|gyeongbuk|gyeonggi|gyeongnam|hs|incheon|jeju|jeonbuk|jeonnam|k|kg|mil|ms|ne|or|pe|re|sc|seoul|ulsan",kw:"com|edu|gov|net|org",ky:"com|edu|gov|net|org",kz:"com|edu|gov|mil|net|org",lb:"com|edu|gov|net|org",lk:"assn|com|edu|gov|grp|hotel|int|ltd|net|ngo|org|sch|soc|web",\n
+lr:"com|edu|gov|net|org",lv:"asn|com|conf|edu|gov|id|mil|net|org",ly:"com|edu|gov|id|med|net|org|plc|sch",ma:"ac|co|gov|m|net|org|press",mc:"asso|tm",me:"ac|co|edu|gov|its|net|org|priv",mg:"com|edu|gov|mil|nom|org|prd|tm",mk:"com|edu|gov|inf|name|net|org|pro",ml:"com|edu|gov|net|org|presse",mn:"edu|gov|org",mo:"com|edu|gov|net|org",mt:"com|edu|gov|net|org",mv:"aero|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro",mw:"ac|co|com|coop|edu|gov|int|museum|net|org",mx:"com|edu|gob|net|org",my:"com|edu|gov|mil|name|net|org|sch",\n
+nf:"arts|com|firm|info|net|other|per|rec|store|web",ng:"biz|com|edu|gov|mil|mobi|name|net|org|sch",ni:"ac|co|com|edu|gob|mil|net|nom|org",np:"com|edu|gov|mil|net|org",nr:"biz|com|edu|gov|info|net|org",om:"ac|biz|co|com|edu|gov|med|mil|museum|net|org|pro|sch",pe:"com|edu|gob|mil|net|nom|org|sld",ph:"com|edu|gov|i|mil|net|ngo|org",pk:"biz|com|edu|fam|gob|gok|gon|gop|gos|gov|net|org|web",pl:"art|bialystok|biz|com|edu|gda|gdansk|gorzow|gov|info|katowice|krakow|lodz|lublin|mil|net|ngo|olsztyn|org|poznan|pwr|radom|slupsk|szczecin|torun|warszawa|waw|wroc|wroclaw|zgora",\n
+pr:"ac|biz|com|edu|est|gov|info|isla|name|net|org|pro|prof",ps:"com|edu|gov|net|org|plo|sec",pw:"belau|co|ed|go|ne|or",ro:"arts|com|firm|info|nom|nt|org|rec|store|tm|www",rs:"ac|co|edu|gov|in|org",sb:"com|edu|gov|net|org",sc:"com|edu|gov|net|org",sh:"co|com|edu|gov|net|nom|org",sl:"com|edu|gov|net|org",st:"co|com|consulado|edu|embaixada|gov|mil|net|org|principe|saotome|store",sv:"com|edu|gob|org|red",sz:"ac|co|org",tr:"av|bbs|bel|biz|com|dr|edu|gen|gov|info|k12|name|net|org|pol|tel|tsk|tv|web",tt:"aero|biz|cat|co|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel",\n
+tw:"club|com|ebiz|edu|game|gov|idv|mil|net|org",mu:"ac|co|com|gov|net|or|org",mz:"ac|co|edu|gov|org",na:"co|com",nz:"ac|co|cri|geek|gen|govt|health|iwi|maori|mil|net|org|parliament|school",pa:"abo|ac|com|edu|gob|ing|med|net|nom|org|sld",pt:"com|edu|gov|int|net|nome|org|publ",py:"com|edu|gov|mil|net|org",qa:"com|edu|gov|mil|net|org",re:"asso|com|nom",ru:"ac|adygeya|altai|amur|arkhangelsk|astrakhan|bashkiria|belgorod|bir|bryansk|buryatia|cbg|chel|chelyabinsk|chita|chukotka|chuvashia|com|dagestan|e-burg|edu|gov|grozny|int|irkutsk|ivanovo|izhevsk|jar|joshkar-ola|kalmykia|kaluga|kamchatka|karelia|kazan|kchr|kemerovo|khabarovsk|khakassia|khv|kirov|koenig|komi|kostroma|kranoyarsk|kuban|kurgan|kursk|lipetsk|magadan|mari|mari-el|marine|mil|mordovia|mosreg|msk|murmansk|nalchik|net|nnov|nov|novosibirsk|nsk|omsk|orenburg|org|oryol|penza|perm|pp|pskov|ptz|rnd|ryazan|sakhalin|samara|saratov|simbirsk|smolensk|spb|stavropol|stv|surgut|tambov|tatarstan|tom|tomsk|tsaritsyn|tsk|tula|tuva|tver|tyumen|udm|udmurtia|ulan-ude|vladikavkaz|vladimir|vladivostok|volgograd|vologda|voronezh|vrn|vyatka|yakutia|yamal|yekaterinburg|yuzhno-sakhalinsk",\n
+rw:"ac|co|com|edu|gouv|gov|int|mil|net",sa:"com|edu|gov|med|net|org|pub|sch",sd:"com|edu|gov|info|med|net|org|tv",se:"a|ac|b|bd|c|d|e|f|g|h|i|k|l|m|n|o|org|p|parti|pp|press|r|s|t|tm|u|w|x|y|z",sg:"com|edu|gov|idn|net|org|per",sn:"art|com|edu|gouv|org|perso|univ",sy:"com|edu|gov|mil|net|news|org",th:"ac|co|go|in|mi|net|or",tj:"ac|biz|co|com|edu|go|gov|info|int|mil|name|net|nic|org|test|web",tn:"agrinet|com|defense|edunet|ens|fin|gov|ind|info|intl|mincom|nat|net|org|perso|rnrt|rns|rnu|tourism",tz:"ac|co|go|ne|or",\n
+ua:"biz|cherkassy|chernigov|chernovtsy|ck|cn|co|com|crimea|cv|dn|dnepropetrovsk|donetsk|dp|edu|gov|if|in|ivano-frankivsk|kh|kharkov|kherson|khmelnitskiy|kiev|kirovograd|km|kr|ks|kv|lg|lugansk|lutsk|lviv|me|mk|net|nikolaev|od|odessa|org|pl|poltava|pp|rovno|rv|sebastopol|sumy|te|ternopil|uzhgorod|vinnica|vn|zaporizhzhe|zhitomir|zp|zt",ug:"ac|co|go|ne|or|org|sc",uk:"ac|bl|british-library|co|cym|gov|govt|icnet|jet|lea|ltd|me|mil|mod|national-library-scotland|nel|net|nhs|nic|nls|org|orgn|parliament|plc|police|sch|scot|soc",\n
+us:"dni|fed|isa|kids|nsn",uy:"com|edu|gub|mil|net|org",ve:"co|com|edu|gob|info|mil|net|org|web",vi:"co|com|k12|net|org",vn:"ac|biz|com|edu|gov|health|info|int|name|net|org|pro",ye:"co|com|gov|ltd|me|net|org|plc",yu:"ac|co|edu|gov|org",za:"ac|agric|alt|bourse|city|co|cybernet|db|edu|gov|grondar|iaccess|imt|inca|landesign|law|mil|net|ngo|nis|nom|olivetti|org|pix|school|tm|web",zm:"ac|co|com|edu|gov|net|org|sch"},has_expression:null,is_expression:null,has:function(d){return!!d.match(m.has_expression)},\n
+is:function(d){return!!d.match(m.is_expression)},get:function(d){return(d=d.match(m.has_expression))&&d[1]||null},noConflict:function(){e.SecondLevelDomains===this&&(e.SecondLevelDomains=k);return this},init:function(){var d="",e;for(e in m.list)u.call(m.list,e)&&(d+="|("+("("+m.list[e]+")."+e)+")");m.has_expression=RegExp("\\\\.("+d.substr(1)+")$","i");m.is_expression=RegExp("^("+d.substr(1)+")$","i")}};m.init();return m});\n
+(function(e,k){"object"===typeof exports?module.exports=k(require("./punycode"),require("./IPv6"),require("./SecondLevelDomains")):"function"===typeof define&&define.amd?define(["./punycode","./IPv6","./SecondLevelDomains"],k):e.URI=k(e.punycode,e.IPv6,e.SecondLevelDomains,e)})(this,function(e,k,u,m){function d(a,b){if(!(this instanceof d))return new d(a,b);void 0===a&&(a="undefined"!==typeof location?location.href+"":"");this.href(a);return void 0!==b?this.absoluteTo(b):this}function g(a){return a.replace(/([.*+?^=!:${}()|[\\]\\/\\\\])/g,\n
+"\\\\$1")}function q(a){return void 0===a?"Undefined":String(Object.prototype.toString.call(a)).slice(8,-1)}function l(a){return"Array"===q(a)}function r(a,b){var c,d;if(l(b)){c=0;for(d=b.length;c<d;c++)if(!r(a,b[c]))return!1;return!0}var p=q(b);c=0;for(d=a.length;c<d;c++)if("RegExp"===p){if("string"===typeof a[c]&&a[c].match(b))return!0}else if(a[c]===b)return!0;return!1}function z(a,b){if(!l(a)||!l(b)||a.length!==b.length)return!1;a.sort();b.sort();for(var c=0,d=a.length;c<d;c++)if(a[c]!==b[c])return!1;\n
+return!0}function D(a){return escape(a)}function E(a){return encodeURIComponent(a).replace(/[!\'()*]/g,D).replace(/\\*/g,"%2A")}var C=m&&m.URI;d.version="1.12.0";var f=d.prototype,w=Object.prototype.hasOwnProperty;d._parts=function(){return{protocol:null,username:null,password:null,hostname:null,urn:null,port:null,path:null,query:null,fragment:null,duplicateQueryParameters:d.duplicateQueryParameters,escapeQuerySpace:d.escapeQuerySpace}};d.duplicateQueryParameters=!1;d.escapeQuerySpace=!0;d.protocol_expression=\n
+/^[a-z][a-z0-9.+-]*$/i;d.idn_expression=/[^a-z0-9\\.-]/i;d.punycode_expression=/(xn--)/i;d.ip4_expression=/^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/;d.ip6_expression=/^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$/;\n
+d.find_uri_expression=/\\b((?:[a-z][\\w-]+:(?:\\/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}\\/)(?:[^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(?:\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:\'".,<>?\\u00ab\\u00bb\\u201c\\u201d\\u2018\\u2019]))/ig;d.findUri={start:/\\b(?:([a-z][a-z0-9.+-]*:\\/\\/)|www\\.)/gi,end:/[\\s\\r\\n]|$/,trim:/[`!()\\[\\]{};:\'".,<>?\\u00ab\\u00bb\\u201c\\u201d\\u201e\\u2018\\u2019]+$/};d.defaultPorts={http:"80",https:"443",ftp:"21",gopher:"70",ws:"80",wss:"443"};d.invalid_hostname_characters=\n
+/[^a-zA-Z0-9\\.-]/;d.domAttributes={a:"href",blockquote:"cite",link:"href",base:"href",script:"src",form:"action",img:"src",area:"href",iframe:"src",embed:"src",source:"src",track:"src",input:"src"};d.getDomAttribute=function(a){if(a&&a.nodeName){var b=a.nodeName.toLowerCase();return"input"===b&&"image"!==a.type?void 0:d.domAttributes[b]}};d.encode=E;d.decode=decodeURIComponent;d.iso8859=function(){d.encode=escape;d.decode=unescape};d.unicode=function(){d.encode=E;d.decode=decodeURIComponent};d.characters=\n
+{pathname:{encode:{expression:/%(24|26|2B|2C|3B|3D|3A|40)/ig,map:{"%24":"$","%26":"&","%2B":"+","%2C":",","%3B":";","%3D":"=","%3A":":","%40":"@"}},decode:{expression:/[\\/\\?#]/g,map:{"/":"%2F","?":"%3F","#":"%23"}}},reserved:{encode:{expression:/%(21|23|24|26|27|28|29|2A|2B|2C|2F|3A|3B|3D|3F|40|5B|5D)/ig,map:{"%3A":":","%2F":"/","%3F":"?","%23":"#","%5B":"[","%5D":"]","%40":"@","%21":"!","%24":"$","%26":"&","%27":"\'","%28":"(","%29":")","%2A":"*","%2B":"+","%2C":",","%3B":";","%3D":"="}}}};d.encodeQuery=\n
+function(a,b){var c=d.encode(a+"");return b?c.replace(/%20/g,"+"):c};d.decodeQuery=function(a,b){a+="";try{return d.decode(b?a.replace(/\\+/g,"%20"):a)}catch(c){return a}};d.recodePath=function(a){a=(a+"").split("/");for(var b=0,c=a.length;b<c;b++)a[b]=d.encodePathSegment(d.decode(a[b]));return a.join("/")};d.decodePath=function(a){a=(a+"").split("/");for(var b=0,c=a.length;b<c;b++)a[b]=d.decodePathSegment(a[b]);return a.join("/")};var s={encode:"encode",decode:"decode"},v,y=function(a,b){return function(c){return d[b](c+\n
+"").replace(d.characters[a][b].expression,function(c){return d.characters[a][b].map[c]})}};for(v in s)d[v+"PathSegment"]=y("pathname",s[v]);d.encodeReserved=y("reserved","encode");d.parse=function(a,b){var c;b||(b={});c=a.indexOf("#");-1<c&&(b.fragment=a.substring(c+1)||null,a=a.substring(0,c));c=a.indexOf("?");-1<c&&(b.query=a.substring(c+1)||null,a=a.substring(0,c));"//"===a.substring(0,2)?(b.protocol=null,a=a.substring(2),a=d.parseAuthority(a,b)):(c=a.indexOf(":"),-1<c&&(b.protocol=a.substring(0,\n
+c)||null,b.protocol&&!b.protocol.match(d.protocol_expression)?b.protocol=void 0:"file"===b.protocol?a=a.substring(c+3):"//"===a.substring(c+1,c+3)?(a=a.substring(c+3),a=d.parseAuthority(a,b)):(a=a.substring(c+1),b.urn=!0)));b.path=a;return b};d.parseHost=function(a,b){var c=a.indexOf("/"),d;-1===c&&(c=a.length);"["===a.charAt(0)?(d=a.indexOf("]"),b.hostname=a.substring(1,d)||null,b.port=a.substring(d+2,c)||null):a.indexOf(":")!==a.lastIndexOf(":")?(b.hostname=a.substring(0,c)||null,b.port=null):(d=\n
+a.substring(0,c).split(":"),b.hostname=d[0]||null,b.port=d[1]||null);b.hostname&&"/"!==a.substring(c).charAt(0)&&(c++,a="/"+a);return a.substring(c)||"/"};d.parseAuthority=function(a,b){a=d.parseUserinfo(a,b);return d.parseHost(a,b)};d.parseUserinfo=function(a,b){var c=a.indexOf("/"),h=-1<c?a.lastIndexOf("@",c):a.indexOf("@");-1<h&&(-1===c||h<c)?(c=a.substring(0,h).split(":"),b.username=c[0]?d.decode(c[0]):null,c.shift(),b.password=c[0]?d.decode(c.join(":")):null,a=a.substring(h+1)):(b.username=null,\n
+b.password=null);return a};d.parseQuery=function(a,b){if(!a)return{};a=a.replace(/&+/g,"&").replace(/^\\?*&*|&+$/g,"");if(!a)return{};for(var c={},h=a.split("&"),p=h.length,n,e,f=0;f<p;f++)n=h[f].split("="),e=d.decodeQuery(n.shift(),b),n=n.length?d.decodeQuery(n.join("="),b):null,c[e]?("string"===typeof c[e]&&(c[e]=[c[e]]),c[e].push(n)):c[e]=n;return c};d.build=function(a){var b="";a.protocol&&(b+=a.protocol+":");a.urn||!b&&!a.hostname||(b+="//");b+=d.buildAuthority(a)||"";"string"===typeof a.path&&\n
+("/"!==a.path.charAt(0)&&"string"===typeof a.hostname&&(b+="/"),b+=a.path);"string"===typeof a.query&&a.query&&(b+="?"+a.query);"string"===typeof a.fragment&&a.fragment&&(b+="#"+a.fragment);return b};d.buildHost=function(a){var b="";if(a.hostname)d.ip6_expression.test(a.hostname)?b=a.port?b+("["+a.hostname+"]:"+a.port):b+a.hostname:(b+=a.hostname,a.port&&(b+=":"+a.port));else return"";return b};d.buildAuthority=function(a){return d.buildUserinfo(a)+d.buildHost(a)};d.buildUserinfo=function(a){var b=\n
+"";a.username&&(b+=d.encode(a.username),a.password&&(b+=":"+d.encode(a.password)),b+="@");return b};d.buildQuery=function(a,b,c){var h="",p,e,f,k;for(e in a)if(w.call(a,e)&&e)if(l(a[e]))for(p={},f=0,k=a[e].length;f<k;f++)void 0!==a[e][f]&&void 0===p[a[e][f]+""]&&(h+="&"+d.buildQueryParameter(e,a[e][f],c),!0!==b&&(p[a[e][f]+""]=!0));else void 0!==a[e]&&(h+="&"+d.buildQueryParameter(e,a[e],c));return h.substring(1)};d.buildQueryParameter=function(a,b,c){return d.encodeQuery(a,c)+(null!==b?"="+d.encodeQuery(b,\n
+c):"")};d.addQuery=function(a,b,c){if("object"===typeof b)for(var h in b)w.call(b,h)&&d.addQuery(a,h,b[h]);else if("string"===typeof b)void 0===a[b]?a[b]=c:("string"===typeof a[b]&&(a[b]=[a[b]]),l(c)||(c=[c]),a[b]=a[b].concat(c));else throw new TypeError("URI.addQuery() accepts an object, string as the name parameter");};d.removeQuery=function(a,b,c){var h;if(l(b))for(c=0,h=b.length;c<h;c++)a[b[c]]=void 0;else if("object"===typeof b)for(h in b)w.call(b,h)&&d.removeQuery(a,h,b[h]);else if("string"===\n
+typeof b)if(void 0!==c)if(a[b]===c)a[b]=void 0;else{if(l(a[b])){h=a[b];var p={},e,f;if(l(c))for(e=0,f=c.length;e<f;e++)p[c[e]]=!0;else p[c]=!0;e=0;for(f=h.length;e<f;e++)void 0!==p[h[e]]&&(h.splice(e,1),f--,e--);a[b]=h}}else a[b]=void 0;else throw new TypeError("URI.addQuery() accepts an object, string as the first parameter");};d.hasQuery=function(a,b,c,h){if("object"===typeof b){for(var e in b)if(w.call(b,e)&&!d.hasQuery(a,e,b[e]))return!1;return!0}if("string"!==typeof b)throw new TypeError("URI.hasQuery() accepts an object, string as the name parameter");\n
+switch(q(c)){case "Undefined":return b in a;case "Boolean":return a=Boolean(l(a[b])?a[b].length:a[b]),c===a;case "Function":return!!c(a[b],b,a);case "Array":return l(a[b])?(h?r:z)(a[b],c):!1;case "RegExp":return l(a[b])?h?r(a[b],c):!1:Boolean(a[b]&&a[b].match(c));case "Number":c=String(c);case "String":return l(a[b])?h?r(a[b],c):!1:a[b]===c;default:throw new TypeError("URI.hasQuery() accepts undefined, boolean, string, number, RegExp, Function as the value parameter");}};d.commonPath=function(a,b){var c=\n
+Math.min(a.length,b.length),d;for(d=0;d<c;d++)if(a.charAt(d)!==b.charAt(d)){d--;break}if(1>d)return a.charAt(0)===b.charAt(0)&&"/"===a.charAt(0)?"/":"";if("/"!==a.charAt(d)||"/"!==b.charAt(d))d=a.substring(0,d).lastIndexOf("/");return a.substring(0,d+1)};d.withinString=function(a,b,c){c||(c={});var h=c.start||d.findUri.start,e=c.end||d.findUri.end,f=c.trim||d.findUri.trim,k=/[a-z0-9-]=["\']?$/i;for(h.lastIndex=0;;){var g=h.exec(a);if(!g)break;g=g.index;if(c.ignoreHtml){var l=a.slice(Math.max(g-3,0),\n
+g);if(l&&k.test(l))continue}var l=g+a.slice(g).search(e),q=a.slice(g,l).replace(f,"");c.ignore&&c.ignore.test(q)||(l=g+q.length,q=b(q,g,l,a),a=a.slice(0,g)+q+a.slice(l),h.lastIndex=g+q.length)}h.lastIndex=0;return a};d.ensureValidHostname=function(a){if(a.match(d.invalid_hostname_characters)){if(!e)throw new TypeError("Hostname \'"+a+"\' contains characters other than [A-Z0-9.-] and Punycode.js is not available");if(e.toASCII(a).match(d.invalid_hostname_characters))throw new TypeError("Hostname \'"+\n
+a+"\' contains characters other than [A-Z0-9.-]");}};d.noConflict=function(a){if(a)return a={URI:this.noConflict()},URITemplate&&"function"==typeof URITemplate.noConflict&&(a.URITemplate=URITemplate.noConflict()),k&&"function"==typeof k.noConflict&&(a.IPv6=k.noConflict()),SecondLevelDomains&&"function"==typeof SecondLevelDomains.noConflict&&(a.SecondLevelDomains=SecondLevelDomains.noConflict()),a;m.URI===this&&(m.URI=C);return this};f.build=function(a){if(!0===a)this._deferred_build=!0;else if(void 0===\n
+a||this._deferred_build)this._string=d.build(this._parts),this._deferred_build=!1;return this};f.clone=function(){return new d(this)};f.valueOf=f.toString=function(){return this.build(!1)._string};s={protocol:"protocol",username:"username",password:"password",hostname:"hostname",port:"port"};y=function(a){return function(b,c){if(void 0===b)return this._parts[a]||"";this._parts[a]=b||null;this.build(!c);return this}};for(v in s)f[v]=y(s[v]);s={query:"?",fragment:"#"};y=function(a,b){return function(c,\n
+d){if(void 0===c)return this._parts[a]||"";null!==c&&(c+="",c.charAt(0)===b&&(c=c.substring(1)));this._parts[a]=c;this.build(!d);return this}};for(v in s)f[v]=y(v,s[v]);s={search:["?","query"],hash:["#","fragment"]};y=function(a,b){return function(c,d){var e=this[a](c,d);return"string"===typeof e&&e.length?b+e:e}};for(v in s)f[v]=y(s[v][1],s[v][0]);f.pathname=function(a,b){if(void 0===a||!0===a){var c=this._parts.path||(this._parts.hostname?"/":"");return a?d.decodePath(c):c}this._parts.path=a?d.recodePath(a):\n
+"/";this.build(!b);return this};f.path=f.pathname;f.href=function(a,b){var c;if(void 0===a)return this.toString();this._string="";this._parts=d._parts();var h=a instanceof d,e="object"===typeof a&&(a.hostname||a.path||a.pathname);a.nodeName&&(e=d.getDomAttribute(a),a=a[e]||"",e=!1);!h&&e&&void 0!==a.pathname&&(a=a.toString());if("string"===typeof a)this._parts=d.parse(a,this._parts);else if(h||e)for(c in h=h?a._parts:a,h)w.call(this._parts,c)&&(this._parts[c]=h[c]);else throw new TypeError("invalid input");\n
+this.build(!b);return this};f.is=function(a){var b=!1,c=!1,h=!1,e=!1,f=!1,g=!1,k=!1,l=!this._parts.urn;this._parts.hostname&&(l=!1,c=d.ip4_expression.test(this._parts.hostname),h=d.ip6_expression.test(this._parts.hostname),b=c||h,f=(e=!b)&&u&&u.has(this._parts.hostname),g=e&&d.idn_expression.test(this._parts.hostname),k=e&&d.punycode_expression.test(this._parts.hostname));switch(a.toLowerCase()){case "relative":return l;case "absolute":return!l;case "domain":case "name":return e;case "sld":return f;\n
+case "ip":return b;case "ip4":case "ipv4":case "inet4":return c;case "ip6":case "ipv6":case "inet6":return h;case "idn":return g;case "url":return!this._parts.urn;case "urn":return!!this._parts.urn;case "punycode":return k}return null};var I=f.protocol,H=f.port,G=f.hostname;f.protocol=function(a,b){if(void 0!==a&&a&&(a=a.replace(/:(\\/\\/)?$/,""),!a.match(d.protocol_expression)))throw new TypeError("Protocol \'"+a+"\' contains characters other than [A-Z0-9.+-] or doesn\'t start with [A-Z]");return I.call(this,\n
+a,b)};f.scheme=f.protocol;f.port=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0!==a&&(0===a&&(a=null),a&&(a+="",":"===a.charAt(0)&&(a=a.substring(1)),a.match(/[^0-9]/))))throw new TypeError("Port \'"+a+"\' contains characters other than [0-9]");return H.call(this,a,b)};f.hostname=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0!==a){var c={};d.parseHost(a,c);a=c.hostname}return G.call(this,a,b)};f.host=function(a,b){if(this._parts.urn)return void 0===a?"":this;\n
+if(void 0===a)return this._parts.hostname?d.buildHost(this._parts):"";d.parseHost(a,this._parts);this.build(!b);return this};f.authority=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a)return this._parts.hostname?d.buildAuthority(this._parts):"";d.parseAuthority(a,this._parts);this.build(!b);return this};f.userinfo=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a){if(!this._parts.username)return"";var c=d.buildUserinfo(this._parts);return c.substring(0,\n
+c.length-1)}"@"!==a[a.length-1]&&(a+="@");d.parseUserinfo(a,this._parts);this.build(!b);return this};f.resource=function(a,b){var c;if(void 0===a)return this.path()+this.search()+this.hash();c=d.parse(a);this._parts.path=c.path;this._parts.query=c.query;this._parts.fragment=c.fragment;this.build(!b);return this};f.subdomain=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a){if(!this._parts.hostname||this.is("IP"))return"";var c=this._parts.hostname.length-this.domain().length-\n
+1;return this._parts.hostname.substring(0,c)||""}c=this._parts.hostname.length-this.domain().length;c=this._parts.hostname.substring(0,c);c=RegExp("^"+g(c));a&&"."!==a.charAt(a.length-1)&&(a+=".");a&&d.ensureValidHostname(a);this._parts.hostname=this._parts.hostname.replace(c,a);this.build(!b);return this};f.domain=function(a,b){if(this._parts.urn)return void 0===a?"":this;"boolean"===typeof a&&(b=a,a=void 0);if(void 0===a){if(!this._parts.hostname||this.is("IP"))return"";var c=this._parts.hostname.match(/\\./g);\n
+if(c&&2>c.length)return this._parts.hostname;c=this._parts.hostname.length-this.tld(b).length-1;c=this._parts.hostname.lastIndexOf(".",c-1)+1;return this._parts.hostname.substring(c)||""}if(!a)throw new TypeError("cannot set domain empty");d.ensureValidHostname(a);!this._parts.hostname||this.is("IP")?this._parts.hostname=a:(c=RegExp(g(this.domain())+"$"),this._parts.hostname=this._parts.hostname.replace(c,a));this.build(!b);return this};f.tld=function(a,b){if(this._parts.urn)return void 0===a?"":\n
+this;"boolean"===typeof a&&(b=a,a=void 0);if(void 0===a){if(!this._parts.hostname||this.is("IP"))return"";var c=this._parts.hostname.lastIndexOf("."),c=this._parts.hostname.substring(c+1);return!0!==b&&u&&u.list[c.toLowerCase()]?u.get(this._parts.hostname)||c:c}if(a)if(a.match(/[^a-zA-Z0-9-]/))if(u&&u.is(a))c=RegExp(g(this.tld())+"$"),this._parts.hostname=this._parts.hostname.replace(c,a);else throw new TypeError("TLD \'"+a+"\' contains characters other than [A-Z0-9]");else{if(!this._parts.hostname||\n
+this.is("IP"))throw new ReferenceError("cannot set TLD on non-domain host");c=RegExp(g(this.tld())+"$");this._parts.hostname=this._parts.hostname.replace(c,a)}else throw new TypeError("cannot set TLD empty");this.build(!b);return this};f.directory=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a||!0===a){if(!this._parts.path&&!this._parts.hostname)return"";if("/"===this._parts.path)return"/";var c=this._parts.path.length-this.filename().length-1,c=this._parts.path.substring(0,\n
+c)||(this._parts.hostname?"/":"");return a?d.decodePath(c):c}c=this._parts.path.length-this.filename().length;c=this._parts.path.substring(0,c);c=RegExp("^"+g(c));this.is("relative")||(a||(a="/"),"/"!==a.charAt(0)&&(a="/"+a));a&&"/"!==a.charAt(a.length-1)&&(a+="/");a=d.recodePath(a);this._parts.path=this._parts.path.replace(c,a);this.build(!b);return this};f.filename=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a||!0===a){if(!this._parts.path||"/"===this._parts.path)return"";\n
+var c=this._parts.path.lastIndexOf("/"),c=this._parts.path.substring(c+1);return a?d.decodePathSegment(c):c}c=!1;"/"===a.charAt(0)&&(a=a.substring(1));a.match(/\\.?\\//)&&(c=!0);var h=RegExp(g(this.filename())+"$");a=d.recodePath(a);this._parts.path=this._parts.path.replace(h,a);c?this.normalizePath(b):this.build(!b);return this};f.suffix=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a||!0===a){if(!this._parts.path||"/"===this._parts.path)return"";var c=this.filename(),h=c.lastIndexOf(".");\n
+if(-1===h)return"";c=c.substring(h+1);c=/^[a-z0-9%]+$/i.test(c)?c:"";return a?d.decodePathSegment(c):c}"."===a.charAt(0)&&(a=a.substring(1));if(c=this.suffix())h=a?RegExp(g(c)+"$"):RegExp(g("."+c)+"$");else{if(!a)return this;this._parts.path+="."+d.recodePath(a)}h&&(a=d.recodePath(a),this._parts.path=this._parts.path.replace(h,a));this.build(!b);return this};f.segment=function(a,b,c){var d=this._parts.urn?":":"/",e=this.path(),f="/"===e.substring(0,1),e=e.split(d);void 0!==a&&"number"!==typeof a&&\n
+(c=b,b=a,a=void 0);if(void 0!==a&&"number"!==typeof a)throw Error("Bad segment \'"+a+"\', must be 0-based integer");f&&e.shift();0>a&&(a=Math.max(e.length+a,0));if(void 0===b)return void 0===a?e:e[a];if(null===a||void 0===e[a])if(l(b)){e=[];a=0;for(var g=b.length;a<g;a++)if(b[a].length||e.length&&e[e.length-1].length)e.length&&!e[e.length-1].length&&e.pop(),e.push(b[a])}else{if(b||"string"===typeof b)""===e[e.length-1]?e[e.length-1]=b:e.push(b)}else b||"string"===typeof b&&b.length?e[a]=b:e.splice(a,\n
+1);f&&e.unshift("");return this.path(e.join(d),c)};f.segmentCoded=function(a,b,c){var e,f;"number"!==typeof a&&(c=b,b=a,a=void 0);if(void 0===b){a=this.segment(a,b,c);if(l(a))for(e=0,f=a.length;e<f;e++)a[e]=d.decode(a[e]);else a=void 0!==a?d.decode(a):void 0;return a}if(l(b))for(e=0,f=b.length;e<f;e++)b[e]=d.decode(b[e]);else b="string"===typeof b?d.encode(b):b;return this.segment(a,b,c)};var F=f.query;f.query=function(a,b){if(!0===a)return d.parseQuery(this._parts.query,this._parts.escapeQuerySpace);\n
+if("function"===typeof a){var c=d.parseQuery(this._parts.query,this._parts.escapeQuerySpace),e=a.call(this,c);this._parts.query=d.buildQuery(e||c,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);this.build(!b);return this}return void 0!==a&&"string"!==typeof a?(this._parts.query=d.buildQuery(a,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace),this.build(!b),this):F.call(this,a,b)};f.setQuery=function(a,b,c){var e=d.parseQuery(this._parts.query,this._parts.escapeQuerySpace);\n
+if("object"===typeof a)for(var f in a)w.call(a,f)&&(e[f]=a[f]);else if("string"===typeof a)e[a]=void 0!==b?b:null;else throw new TypeError("URI.addQuery() accepts an object, string as the name parameter");this._parts.query=d.buildQuery(e,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);"string"!==typeof a&&(c=b);this.build(!c);return this};f.addQuery=function(a,b,c){var e=d.parseQuery(this._parts.query,this._parts.escapeQuerySpace);d.addQuery(e,a,void 0===b?null:b);this._parts.query=\n
+d.buildQuery(e,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);"string"!==typeof a&&(c=b);this.build(!c);return this};f.removeQuery=function(a,b,c){var e=d.parseQuery(this._parts.query,this._parts.escapeQuerySpace);d.removeQuery(e,a,b);this._parts.query=d.buildQuery(e,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);"string"!==typeof a&&(c=b);this.build(!c);return this};f.hasQuery=function(a,b,c){var e=d.parseQuery(this._parts.query,this._parts.escapeQuerySpace);\n
+return d.hasQuery(e,a,b,c)};f.setSearch=f.setQuery;f.addSearch=f.addQuery;f.removeSearch=f.removeQuery;f.hasSearch=f.hasQuery;f.normalize=function(){return this._parts.urn?this.normalizeProtocol(!1).normalizeQuery(!1).normalizeFragment(!1).build():this.normalizeProtocol(!1).normalizeHostname(!1).normalizePort(!1).normalizePath(!1).normalizeQuery(!1).normalizeFragment(!1).build()};f.normalizeProtocol=function(a){"string"===typeof this._parts.protocol&&(this._parts.protocol=this._parts.protocol.toLowerCase(),\n
+this.build(!a));return this};f.normalizeHostname=function(a){this._parts.hostname&&(this.is("IDN")&&e?this._parts.hostname=e.toASCII(this._parts.hostname):this.is("IPv6")&&k&&(this._parts.hostname=k.best(this._parts.hostname)),this._parts.hostname=this._parts.hostname.toLowerCase(),this.build(!a));return this};f.normalizePort=function(a){"string"===typeof this._parts.protocol&&this._parts.port===d.defaultPorts[this._parts.protocol]&&(this._parts.port=null,this.build(!a));return this};f.normalizePath=\n
+function(a){if(this._parts.urn||!this._parts.path||"/"===this._parts.path)return this;var b,c=this._parts.path,e="",f,g;"/"!==c.charAt(0)&&(b=!0,c="/"+c);c=c.replace(/(\\/(\\.\\/)+)|(\\/\\.$)/g,"/").replace(/\\/{2,}/g,"/");b&&(e=c.substring(1).match(/^(\\.\\.\\/)+/)||"")&&(e=e[0]);for(;;){f=c.indexOf("/..");if(-1===f)break;else if(0===f){c=c.substring(3);continue}g=c.substring(0,f).lastIndexOf("/");-1===g&&(g=f);c=c.substring(0,g)+c.substring(f+3)}b&&this.is("relative")&&(c=e+c.substring(1));c=d.recodePath(c);\n
+this._parts.path=c;this.build(!a);return this};f.normalizePathname=f.normalizePath;f.normalizeQuery=function(a){"string"===typeof this._parts.query&&(this._parts.query.length?this.query(d.parseQuery(this._parts.query,this._parts.escapeQuerySpace)):this._parts.query=null,this.build(!a));return this};f.normalizeFragment=function(a){this._parts.fragment||(this._parts.fragment=null,this.build(!a));return this};f.normalizeSearch=f.normalizeQuery;f.normalizeHash=f.normalizeFragment;f.iso8859=function(){var a=\n
+d.encode,b=d.decode;d.encode=escape;d.decode=decodeURIComponent;this.normalize();d.encode=a;d.decode=b;return this};f.unicode=function(){var a=d.encode,b=d.decode;d.encode=E;d.decode=unescape;this.normalize();d.encode=a;d.decode=b;return this};f.readable=function(){var a=this.clone();a.username("").password("").normalize();var b="";a._parts.protocol&&(b+=a._parts.protocol+"://");a._parts.hostname&&(a.is("punycode")&&e?(b+=e.toUnicode(a._parts.hostname),a._parts.port&&(b+=":"+a._parts.port)):b+=a.host());\n
+a._parts.hostname&&a._parts.path&&"/"!==a._parts.path.charAt(0)&&(b+="/");b+=a.path(!0);if(a._parts.query){for(var c="",f=0,g=a._parts.query.split("&"),k=g.length;f<k;f++){var l=(g[f]||"").split("="),c=c+("&"+d.decodeQuery(l[0],this._parts.escapeQuerySpace).replace(/&/g,"%26"));void 0!==l[1]&&(c+="="+d.decodeQuery(l[1],this._parts.escapeQuerySpace).replace(/&/g,"%26"))}b+="?"+c.substring(1)}return b+=d.decodeQuery(a.hash(),!0)};f.absoluteTo=function(a){var b=this.clone(),c=["protocol","username",\n
+"password","hostname","port"],e,f;if(this._parts.urn)throw Error("URNs do not have any generally defined hierarchical components");a instanceof d||(a=new d(a));b._parts.protocol||(b._parts.protocol=a._parts.protocol);if(this._parts.hostname)return b;for(e=0;f=c[e];e++)b._parts[f]=a._parts[f];b._parts.path?".."===b._parts.path.substring(-2)&&(b._parts.path+="/"):(b._parts.path=a._parts.path,b._parts.query||(b._parts.query=a._parts.query));"/"!==b.path().charAt(0)&&(a=a.directory(),b._parts.path=(a?\n
+a+"/":"")+b._parts.path,b.normalizePath());b.build();return b};f.relativeTo=function(a){var b=this.clone().normalize(),c,e,f,g;if(b._parts.urn)throw Error("URNs do not have any generally defined hierarchical components");a=(new d(a)).normalize();c=b._parts;e=a._parts;f=b.path();g=a.path();if("/"!==f.charAt(0))throw Error("URI is already relative");if("/"!==g.charAt(0))throw Error("Cannot calculate a URI relative to another relative URI");c.protocol===e.protocol&&(c.protocol=null);if(c.username===\n
+e.username&&c.password===e.password&&null===c.protocol&&null===c.username&&null===c.password&&c.hostname===e.hostname&&c.port===e.port)c.hostname=null,c.port=null;else return b.build();if(f===g)return c.path="",b.build();a=d.commonPath(b.path(),a.path());if(!a)return b.build();e=e.path.substring(a.length).replace(/[^\\/]*$/,"").replace(/.*?\\//g,"../");c.path=e+c.path.substring(a.length);return b.build()};f.equals=function(a){var b=this.clone();a=new d(a);var c={},e={},f={},g;b.normalize();a.normalize();\n
+if(b.toString()===a.toString())return!0;c=b.query();e=a.query();b.query("");a.query("");if(b.toString()!==a.toString()||c.length!==e.length)return!1;c=d.parseQuery(c,this._parts.escapeQuerySpace);e=d.parseQuery(e,this._parts.escapeQuerySpace);for(g in c)if(w.call(c,g)){if(!l(c[g])){if(c[g]!==e[g])return!1}else if(!z(c[g],e[g]))return!1;f[g]=!0}for(g in e)if(w.call(e,g)&&!f[g])return!1;return!0};f.duplicateQueryParameters=function(a){this._parts.duplicateQueryParameters=!!a;return this};f.escapeQuerySpace=\n
+function(a){this._parts.escapeQuerySpace=!!a;return this};return d});\n
+(function(e,k){"object"===typeof exports?module.exports=k(require("./URI")):"function"===typeof define&&define.amd?define(["./URI"],k):k(e.URI)})(this,function(e){var k=e.prototype,u=k.fragment;e.fragmentPrefix="?";var m=e._parts;e._parts=function(){var d=m();d.fragmentPrefix=e.fragmentPrefix;return d};k.fragmentPrefix=function(d){this._parts.fragmentPrefix=d;return this};k.fragment=function(d,g){var k=this._parts.fragmentPrefix,l=this._parts.fragment||"";return!0===d?l.substring(0,k.length)!==k?\n
+{}:e.parseQuery(l.substring(k.length)):void 0!==d&&"string"!==typeof d?(this._parts.fragment=k+e.buildQuery(d),this.build(!g),this):u.call(this,d,g)};k.addFragment=function(d,g,k){var l=this._parts.fragmentPrefix,m=e.parseQuery((this._parts.fragment||"").substring(l.length));e.addQuery(m,d,g);this._parts.fragment=l+e.buildQuery(m);"string"!==typeof d&&(k=g);this.build(!k);return this};k.removeFragment=function(d,g,k){var l=this._parts.fragmentPrefix,m=e.parseQuery((this._parts.fragment||"").substring(l.length));\n
+e.removeQuery(m,d,g);this._parts.fragment=l+e.buildQuery(m);"string"!==typeof d&&(k=g);this.build(!k);return this};k.addHash=k.addFragment;k.removeHash=k.removeFragment;return{}});\n
+;/*global unescape, module, define, window, global*/\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */\n
-/*global uniqueJSONStringify, methodType */\n
-\n
-var defaults = {}, constants = {};\n
-\n
-defaults.storage_types = {};\n
+/*\n
+ UriTemplate Copyright (c) 2012-2013 Franz Antesberger. All Rights Reserved.\n
+ Available via the MIT license.\n
+*/\n
 \n
-constants.dcmi_types = {\n
-  \'Collection\': \'Collection\',\n
-  \'Dataset\': \'Dataset\',\n
-  \'Event\': \'Event\',\n
-  \'Image\': \'Image\',\n
-  \'InteractiveResource\': \'InteractiveResource\',\n
-  \'MovingImage\': \'MovingImage\',\n
-  \'PhysicalObject\': \'PhysicalObject\',\n
-  \'Service\': \'Service\',\n
-  \'Software\': \'Software\',\n
-  \'Sound\': \'Sound\',\n
-  \'StillImage\': \'StillImage\',\n
-  \'Text\': \'Text\'\n
-};\n
-// if (dcmi_types.Collection === \'Collection\') { is a DCMI type }\n
-// if (typeof dcmi_types[name] === \'string\')   { is a DCMI type }\n
-\n
-constants.http_status_text = {\n
-  "0": "Unknown",\n
-  "550": "Internal JIO Error",\n
-  "551": "Internal Storage Error",\n
-  "555": "Cancelled",\n
-  "Unknown": "Unknown",\n
-  "Internal JIO Error": "Internal JIO Error",\n
-  "Internal Storage Error": "Internal Storage Error",\n
-  "Cancelled": "Cancelled",\n
-  "unknown": "Unknown",\n
-  "internal_jio_error": "Internal JIO Error",\n
-  "internal_storage_error": "Internal Storage Error",\n
-  "cancelled": "Cancelled",\n
-\n
-  "200": "Ok",\n
-  "201": "Created",\n
-  "204": "No Content",\n
-  "205": "Reset Content",\n
-  "206": "Partial Content",\n
-  "304": "Not Modified",\n
-  "400": "Bad Request",\n
-  "401": "Unauthorized",\n
-  "402": "Payment Required",\n
-  "403": "Forbidden",\n
-  "404": "Not Found",\n
-  "405": "Method Not Allowed",\n
-  "406": "Not Acceptable",\n
-  "407": "Proxy Authentication Required",\n
-  "408": "Request Timeout",\n
-  "409": "Conflict",\n
-  "410": "Gone",\n
-  "411": "Length Required",\n
-  "412": "Precondition Failed",\n
-  "413": "Request Entity Too Large",\n
-  "414": "Request-URI Too Long",\n
-  "415": "Unsupported Media Type",\n
-  "416": "Requested Range Not Satisfiable",\n
-  "417": "Expectation Failed",\n
-  "418": "I\'m a teapot",\n
-  "419": "Authentication Timeout",\n
-  "500": "Internal Server Error",\n
-  "501": "Not Implemented",\n
-  "502": "Bad Gateway",\n
-  "503": "Service Unavailable",\n
-  "504": "Gateway Timeout",\n
-  "507": "Insufficient Storage",\n
-\n
-  "Ok": "Ok",\n
-  "OK": "Ok",\n
-  "Created": "Created",\n
-  "No Content": "No Content",\n
-  "Reset Content": "Reset Content",\n
-  "Partial Content": "Partial Content",\n
-  "Not Modified": "Not Modified",\n
-  "Bad Request": "Bad Request",\n
-  "Unauthorized": "Unauthorized",\n
-  "Payment Required": "Payment Required",\n
-  "Forbidden": "Forbidden",\n
-  "Not Found": "Not Found",\n
-  "Method Not Allowed": "Method Not Allowed",\n
-  "Not Acceptable": "Not Acceptable",\n
-  "Proxy Authentication Required": "Proxy Authentication Required",\n
-  "Request Timeout": "Request Timeout",\n
-  "Conflict": "Conflict",\n
-  "Gone": "Gone",\n
-  "Length Required": "Length Required",\n
-  "Precondition Failed": "Precondition Failed",\n
-  "Request Entity Too Large": "Request Entity Too Large",\n
-  "Request-URI Too Long": "Request-URI Too Long",\n
-  "Unsupported Media Type": "Unsupported Media Type",\n
-  "Requested Range Not Satisfiable": "Requested Range Not Satisfiable",\n
-  "Expectation Failed": "Expectation Failed",\n
-  "I\'m a teapot": "I\'m a teapot",\n
-  "Authentication Timeout": "Authentication Timeout",\n
-  "Internal Server Error": "Internal Server Error",\n
-  "Not Implemented": "Not Implemented",\n
-  "Bad Gateway": "Bad Gateway",\n
-  "Service Unavailable": "Service Unavailable",\n
-  "Gateway Timeout": "Gateway Timeout",\n
-  "Insufficient Storage": "Insufficient Storage",\n
-\n
-  "ok": "Ok",\n
-  "created": "Created",\n
-  "no_content": "No Content",\n
-  "reset_content": "Reset Content",\n
-  "partial_content": "Partial Content",\n
-  "not_modified": "Not Modified",\n
-  "bad_request": "Bad Request",\n
-  "unauthorized": "Unauthorized",\n
-  "payment_required": "Payment Required",\n
-  "forbidden": "Forbidden",\n
-  "not_found": "Not Found",\n
-  "method_not_allowed": "Method Not Allowed",\n
-  "not_acceptable": "Not Acceptable",\n
-  "proxy_authentication_required": "Proxy Authentication Required",\n
-  "request_timeout": "Request Timeout",\n
-  "conflict": "Conflict",\n
-  "gone": "Gone",\n
-  "length_required": "Length Required",\n
-  "precondition_failed": "Precondition Failed",\n
-  "request_entity_too_large": "Request Entity Too Large",\n
-  "request-uri_too_long": "Request-URI Too Long",\n
-  "unsupported_media_type": "Unsupported Media Type",\n
-  "requested_range_not_satisfiable": "Requested Range Not Satisfiable",\n
-  "expectation_failed": "Expectation Failed",\n
-  "im_a_teapot": "I\'m a teapot",\n
-  "authentication_timeout": "Authentication Timeout",\n
-  "internal_server_error": "Internal Server Error",\n
-  "not_implemented": "Not Implemented",\n
-  "bad_gateway": "Bad Gateway",\n
-  "service_unavailable": "Service Unavailable",\n
-  "gateway_timeout": "Gateway Timeout",\n
-  "insufficient_storage": "Insufficient Storage"\n
-};\n
+(function (exportCallback) {\n
+    "use strict";\n
 \n
-constants.http_status = {\n
-  "0": 0,\n
-  "550": 550,\n
-  "551": 551,\n
-  "555": 555,\n
-  "Unknown": 0,\n
-  "Internal JIO Error": 550,\n
-  "Internal Storage Error": 551,\n
-  "Cancelled": 555,\n
-  "unknown": 0,\n
-  "internal_jio_error": 550,\n
-  "internal_storage_error": 551,\n
-  "cancelled": 555,\n
-\n
-  "200": 200,\n
-  "201": 201,\n
-  "204": 204,\n
-  "205": 205,\n
-  "206": 206,\n
-  "304": 304,\n
-  "400": 400,\n
-  "401": 401,\n
-  "402": 402,\n
-  "403": 403,\n
-  "404": 404,\n
-  "405": 405,\n
-  "406": 406,\n
-  "407": 407,\n
-  "408": 408,\n
-  "409": 409,\n
-  "410": 410,\n
-  "411": 411,\n
-  "412": 412,\n
-  "413": 413,\n
-  "414": 414,\n
-  "415": 415,\n
-  "416": 416,\n
-  "417": 417,\n
-  "418": 418,\n
-  "419": 419,\n
-  "500": 500,\n
-  "501": 501,\n
-  "502": 502,\n
-  "503": 503,\n
-  "504": 504,\n
-  "507": 507,\n
-\n
-  "Ok": 200,\n
-  "OK": 200,\n
-  "Created": 201,\n
-  "No Content": 204,\n
-  "Reset Content": 205,\n
-  "Partial Content": 206,\n
-  "Not Modified": 304,\n
-  "Bad Request": 400,\n
-  "Unauthorized": 401,\n
-  "Payment Required": 402,\n
-  "Forbidden": 403,\n
-  "Not Found": 404,\n
-  "Method Not Allowed": 405,\n
-  "Not Acceptable": 406,\n
-  "Proxy Authentication Required": 407,\n
-  "Request Timeout": 408,\n
-  "Conflict": 409,\n
-  "Gone": 410,\n
-  "Length Required": 411,\n
-  "Precondition Failed": 412,\n
-  "Request Entity Too Large": 413,\n
-  "Request-URI Too Long": 414,\n
-  "Unsupported Media Type": 415,\n
-  "Requested Range Not Satisfiable": 416,\n
-  "Expectation Failed": 417,\n
-  "I\'m a teapot": 418,\n
-  "Authentication Timeout": 419,\n
-  "Internal Server Error": 500,\n
-  "Not Implemented": 501,\n
-  "Bad Gateway": 502,\n
-  "Service Unavailable": 503,\n
-  "Gateway Timeout": 504,\n
-  "Insufficient Storage": 507,\n
-\n
-  "ok": 200,\n
-  "created": 201,\n
-  "no_content": 204,\n
-  "reset_content": 205,\n
-  "partial_content": 206,\n
-  "not_modified": 304,\n
-  "bad_request": 400,\n
-  "unauthorized": 401,\n
-  "payment_required": 402,\n
-  "forbidden": 403,\n
-  "not_found": 404,\n
-  "method_not_allowed": 405,\n
-  "not_acceptable": 406,\n
-  "proxy_authentication_required": 407,\n
-  "request_timeout": 408,\n
-  "conflict": 409,\n
-  "gone": 410,\n
-  "length_required": 411,\n
-  "precondition_failed": 412,\n
-  "request_entity_too_large": 413,\n
-  "request-uri_too_long": 414,\n
-  "unsupported_media_type": 415,\n
-  "requested_range_not_satisfiable": 416,\n
-  "expectation_failed": 417,\n
-  "im_a_teapot": 418,\n
-  "authentication_timeout": 419,\n
-  "internal_server_error": 500,\n
-  "not_implemented": 501,\n
-  "bad_gateway": 502,\n
-  "service_unavailable": 503,\n
-  "gateway_timeout": 504,\n
-  "insufficient_storage": 507\n
-};\n
+var UriTemplateError = (function () {\n
 \n
-constants.http_action = {\n
-  "0": "error",\n
-  "550": "error",\n
-  "551": "error",\n
-  "555": "error",\n
-  "Unknown": "error",\n
-  "Internal JIO Error": "error",\n
-  "Internal Storage Error": "error",\n
-  "Cancelled": "error",\n
-  "unknown": "error",\n
-  "internal_jio_error": "error",\n
-  "internal_storage_error": "error",\n
-  "cancelled": "error",\n
-\n
-  "200": "success",\n
-  "201": "success",\n
-  "204": "success",\n
-  "205": "success",\n
-  "206": "success",\n
-  "304": "success",\n
-  "400": "error",\n
-  "401": "error",\n
-  "402": "error",\n
-  "403": "error",\n
-  "404": "error",\n
-  "405": "error",\n
-  "406": "error",\n
-  "407": "error",\n
-  "408": "error",\n
-  "409": "error",\n
-  "410": "error",\n
-  "411": "error",\n
-  "412": "error",\n
-  "413": "error",\n
-  "414": "error",\n
-  "415": "error",\n
-  "416": "error",\n
-  "417": "error",\n
-  "418": "error",\n
-  "419": "retry",\n
-  "500": "retry",\n
-  "501": "error",\n
-  "502": "error",\n
-  "503": "retry",\n
-  "504": "retry",\n
-  "507": "error",\n
-\n
-  "Ok": "success",\n
-  "OK": "success",\n
-  "Created": "success",\n
-  "No Content": "success",\n
-  "Reset Content": "success",\n
-  "Partial Content": "success",\n
-  "Not Modified": "success",\n
-  "Bad Request": "error",\n
-  "Unauthorized": "error",\n
-  "Payment Required": "error",\n
-  "Forbidden": "error",\n
-  "Not Found": "error",\n
-  "Method Not Allowed": "error",\n
-  "Not Acceptable": "error",\n
-  "Proxy Authentication Required": "error",\n
-  "Request Timeout": "error",\n
-  "Conflict": "error",\n
-  "Gone": "error",\n
-  "Length Required": "error",\n
-  "Precondition Failed": "error",\n
-  "Request Entity Too Large": "error",\n
-  "Request-URI Too Long": "error",\n
-  "Unsupported Media Type": "error",\n
-  "Requested Range Not Satisfiable": "error",\n
-  "Expectation Failed": "error",\n
-  "I\'m a teapot": "error",\n
-  "Authentication Timeout": "retry",\n
-  "Internal Server Error": "retry",\n
-  "Not Implemented": "error",\n
-  "Bad Gateway": "error",\n
-  "Service Unavailable": "retry",\n
-  "Gateway Timeout": "retry",\n
-  "Insufficient Storage": "error",\n
-\n
-  "ok": "success",\n
-  "created": "success",\n
-  "no_content": "success",\n
-  "reset_content": "success",\n
-  "partial_content": "success",\n
-  "not_modified": "success",\n
-  "bad_request": "error",\n
-  "unauthorized": "error",\n
-  "payment_required": "error",\n
-  "forbidden": "error",\n
-  "not_found": "error",\n
-  "method_not_allowed": "error",\n
-  "not_acceptable": "error",\n
-  "proxy_authentication_required": "error",\n
-  "request_timeout": "error",\n
-  "conflict": "error",\n
-  "gone": "error",\n
-  "length_required": "error",\n
-  "precondition_failed": "error",\n
-  "request_entity_too_large": "error",\n
-  "request-uri_too_long": "error",\n
-  "unsupported_media_type": "error",\n
-  "requested_range_not_satisfiable": "error",\n
-  "expectation_failed": "error",\n
-  "im_a_teapot": "error",\n
-  "authentication_timeout": "retry",\n
-  "internal_server_error": "retry",\n
-  "not_implemented": "error",\n
-  "bad_gateway": "error",\n
-  "service_unavailable": "retry",\n
-  "gateway_timeout": "retry",\n
-  "insufficient_storage": "error"\n
-};\n
+    function UriTemplateError (options) {\n
+        this.options = options;\n
+    }\n
 \n
-constants.content_type_re =\n
-  /^([a-z]+\\/[a-zA-Z0-9\\+\\-\\.]+)(?:\\s*;\\s*charset\\s*=\\s*([a-zA-Z0-9\\-]+))?$/;\n
+    UriTemplateError.prototype.toString = function () {\n
+        if (JSON && JSON.stringify) {\n
+            return JSON.stringify(this.options);\n
+        }\n
+        else {\n
+            return this.options;\n
+        }\n
+    };\n
 \n
-/**\n
- * Function that does nothing\n
- */\n
-constants.emptyFunction = function () {\n
-  return;\n
-};\n
+    return UriTemplateError;\n
+}());\n
 \n
-defaults.job_rule_conditions = {};\n
+var objectHelper = (function () {\n
+    function isArray (value) {\n
+        return Object.prototype.toString.apply(value) === \'[object Array]\';\n
+    }\n
 \n
-/**\n
- * Adds some job rule conditions\n
- */\n
-(function () {\n
+    function isString (value) {\n
+        return Object.prototype.toString.apply(value) === \'[object String]\';\n
+    }\n
+    \n
+    function isNumber (value) {\n
+        return Object.prototype.toString.apply(value) === \'[object Number]\';\n
+    }\n
+    \n
+    function isBoolean (value) {\n
+        return Object.prototype.toString.apply(value) === \'[object Boolean]\';\n
+    }\n
+    \n
+    function join (arr, separator) {\n
+        var\n
+            result = \'\',\n
+            first = true,\n
+            index;\n
+        for (index = 0; index < arr.length; index += 1) {\n
+            if (first) {\n
+                first = false;\n
+            }\n
+            else {\n
+                result += separator;\n
+            }\n
+            result += arr[index];\n
+        }\n
+        return result;\n
+    }\n
 \n
-  /**\n
-   * Compare two jobs and test if they use the same storage description\n
-   *\n
-   * @param  {Object} a The first job to compare\n
-   * @param  {Object} b The second job to compare\n
-   * @return {Boolean} True if equal, else false\n
-   */\n
-  function sameStorageDescription(a, b) {\n
-    return uniqueJSONStringify(a.storage_spec) ===\n
-      uniqueJSONStringify(b.storage_spec);\n
-  }\n
+    function map (arr, mapper) {\n
+        var\n
+            result = [],\n
+            index = 0;\n
+        for (; index < arr.length; index += 1) {\n
+            result.push(mapper(arr[index]));\n
+        }\n
+        return result;\n
+    }\n
 \n
-  /**\n
-   * Compare two jobs and test if they are writers\n
-   *\n
-   * @param  {Object} a The first job to compare\n
-   * @param  {Object} b The second job to compare\n
-   * @return {Boolean} True if equal, else false\n
-   */\n
-  function areWriters(a, b) {\n
-    return methodType(a.method) === \'writer\' &&\n
-      methodType(b.method) === \'writer\';\n
-  }\n
+    function filter (arr, predicate) {\n
+        var\n
+            result = [],\n
+            index = 0;\n
+        for (; index < arr.length; index += 1) {\n
+            if (predicate(arr[index])) {\n
+                result.push(arr[index]);\n
+            }\n
+        }\n
+        return result;\n
+    }\n
 \n
-  /**\n
-   * Compare two jobs and test if they use metadata only\n
-   *\n
-   * @param  {Object} a The first job to compare\n
-   * @param  {Object} b The second job to compare\n
-   * @return {Boolean} True if equal, else false\n
-   */\n
-  function useMetadataOnly(a, b) {\n
-    if ([\'post\', \'put\', \'get\', \'remove\', \'allDocs\'].indexOf(a.method) === -1) {\n
-      return false;\n
+    function deepFreezeUsingObjectFreeze (object) {\n
+        if (typeof object !== "object" || object === null) {\n
+            return object;\n
+        }\n
+        Object.freeze(object);\n
+        var property, propertyName;\n
+        for (propertyName in object) {\n
+            if (object.hasOwnProperty(propertyName)) {\n
+                property = object[propertyName];\n
+                // be aware, arrays are \'object\', too\n
+                if (typeof property === "object") {\n
+                    deepFreeze(property);\n
+                }\n
+            }\n
+        }\n
+        return object;\n
     }\n
-    if ([\'post\', \'put\', \'get\', \'remove\', \'allDocs\'].indexOf(b.method) === -1) {\n
-      return false;\n
+\n
+    function deepFreeze (object) {\n
+        if (typeof Object.freeze === \'function\') {\n
+            return deepFreezeUsingObjectFreeze(object);\n
+        }\n
+        return object;\n
     }\n
-    return true;\n
-  }\n
 \n
-  /**\n
-   * Compare two jobs and test if they are readers\n
-   *\n
-   * @param  {Object} a The first job to compare\n
-   * @param  {Object} b The second job to compare\n
-   * @return {Boolean} True if equal, else false\n
-   */\n
-  function areReaders(a, b) {\n
-    return methodType(a.method) === \'reader\' &&\n
-      methodType(b.method) === \'reader\';\n
-  }\n
 \n
-  /**\n
-   * Compare two jobs and test if their methods are the same\n
-   *\n
-   * @param  {Object} a The first job to compare\n
-   * @param  {Object} b The second job to compare\n
-   * @return {Boolean} True if equal, else false\n
-   */\n
-  function sameMethod(a, b) {\n
-    return a.method === b.method;\n
-  }\n
+    return {\n
+        isArray: isArray,\n
+        isString: isString,\n
+        isNumber: isNumber,\n
+        isBoolean: isBoolean,\n
+        join: join,\n
+        map: map,\n
+        filter: filter,\n
+        deepFreeze: deepFreeze\n
+    };\n
+}());\n
 \n
-  /**\n
-   * Compare two jobs and test if their document ids are the same\n
-   *\n
-   * @param  {Object} a The first job to compare\n
-   * @param  {Object} b The second job to compare\n
-   * @return {Boolean} True if equal, else false\n
-   */\n
-  function sameDocumentId(a, b) {\n
-    return a.kwargs._id === b.kwargs._id;\n
-  }\n
+var charHelper = (function () {\n
 \n
-  /**\n
-   * Test if the jobs have a document id.\n
-   *\n
-   * @param  {Object} a The first job to test\n
-   * @param  {Object} b The second job to test\n
-   * @return {Boolean} True if ids exist, else false\n
-   */\n
-  function haveDocumentIds(a, b) {\n
-    if (typeof a.kwargs._id !== "string" || a.kwargs._id === "") {\n
-      return false;\n
-    }\n
-    if (typeof b.kwargs._id !== "string" || b.kwargs._id === "") {\n
-      return false;\n
+    function isAlpha (chr) {\n
+        return (chr >= \'a\' && chr <= \'z\') || ((chr >= \'A\' && chr <= \'Z\'));\n
     }\n
-    return true;\n
-  }\n
-\n
-  /**\n
-   * Compare two jobs and test if their kwargs are equal\n
-   *\n
-   * @param  {Object} a The first job to compare\n
-   * @param  {Object} b The second job to compare\n
-   * @return {Boolean} True if equal, else false\n
-   */\n
-  function sameParameters(a, b) {\n
-    return uniqueJSONStringify(a.kwargs) ===\n
-      uniqueJSONStringify(b.kwargs);\n
-  }\n
 \n
-  /**\n
-   * Compare two jobs and test if their options are equal\n
-   *\n
-   * @param  {Object} a The first job to compare\n
-   * @param  {Object} b The second job to compare\n
-   * @return {Boolean} True if equal, else false\n
-   */\n
-  function sameOptions(a, b) {\n
-    return uniqueJSONStringify(a.options) ===\n
-      uniqueJSONStringify(b.options);\n
-  }\n
+    function isDigit (chr) {\n
+        return chr >= \'0\' && chr <= \'9\';\n
+    }\n
 \n
-  defaults.job_rule_conditions = {\n
-    "sameStorageDescription": sameStorageDescription,\n
-    "areWriters": areWriters,\n
-    "areReaders": areReaders,\n
-    "useMetadataOnly": useMetadataOnly,\n
-    "sameMethod": sameMethod,\n
-    "sameDocumentId": sameDocumentId,\n
-    "sameParameters": sameParameters,\n
-    "sameOptions": sameOptions,\n
-    "haveDocumentIds": haveDocumentIds\n
-  };\n
+    function isHexDigit (chr) {\n
+        return isDigit(chr) || (chr >= \'a\' && chr <= \'f\') || (chr >= \'A\' && chr <= \'F\');\n
+    }\n
 \n
+    return {\n
+        isAlpha: isAlpha,\n
+        isDigit: isDigit,\n
+        isHexDigit: isHexDigit\n
+    };\n
 }());\n
 \n
-/*jslint indent: 2, maxlen: 80, nomen: true, sloppy: true */\n
-/*global exports, Blob, FileReader, RSVP, hex_sha256, XMLHttpRequest,\n
-  constants */\n
+var pctEncoder = (function () {\n
+    var utf8 = {\n
+        encode: function (chr) {\n
+            // see http://ecmanaut.blogspot.de/2006/07/encoding-decoding-utf8-in-javascript.html\n
+            return unescape(encodeURIComponent(chr));\n
+        },\n
+        numBytes: function (firstCharCode) {\n
+            if (firstCharCode <= 0x7F) {\n
+                return 1;\n
+            }\n
+            else if (0xC2 <= firstCharCode && firstCharCode <= 0xDF) {\n
+                return 2;\n
+            }\n
+            else if (0xE0 <= firstCharCode && firstCharCode <= 0xEF) {\n
+                return 3;\n
+            }\n
+            else if (0xF0 <= firstCharCode && firstCharCode <= 0xF4) {\n
+                return 4;\n
+            }\n
+            // no valid first octet\n
+            return 0;\n
+        },\n
+        isValidFollowingCharCode: function (charCode) {\n
+            return 0x80 <= charCode && charCode <= 0xBF;\n
+        }\n
+    };\n
 \n
-/**\n
- * Do not exports these tools unless they are not writable, not configurable.\n
- */\n
+    /**\n
+     * encodes a character, if needed or not.\n
+     * @param chr\n
+     * @return pct-encoded character\n
+     */\n
+    function encodeCharacter (chr) {\n
+        var\n
+            result = \'\',\n
+            octets = utf8.encode(chr),\n
+            octet,\n
+            index;\n
+        for (index = 0; index < octets.length; index += 1) {\n
+            octet = octets.charCodeAt(index);\n
+            result += \'%\' + (octet < 0x10 ? \'0\' : \'\') + octet.toString(16).toUpperCase();\n
+        }\n
+        return result;\n
+    }\n
+\n
+    /**\n
+     * Returns, whether the given text at start is in the form \'percent hex-digit hex-digit\', like \'%3F\'\n
+     * @param text\n
+     * @param start\n
+     * @return {boolean|*|*}\n
+     */\n
+    function isPercentDigitDigit (text, start) {\n
+        return text.charAt(start) === \'%\' && charHelper.isHexDigit(text.charAt(start + 1)) && charHelper.isHexDigit(text.charAt(start + 2));\n
+    }\n
+\n
+    /**\n
+     * Parses a hex number from start with length 2.\n
+     * @param text a string\n
+     * @param start the start index of the 2-digit hex number\n
+     * @return {Number}\n
+     */\n
+    function parseHex2 (text, start) {\n
+        return parseInt(text.substr(start, 2), 16);\n
+    }\n
+\n
+    /**\n
+     * Returns whether or not the given char sequence is a correctly pct-encoded sequence.\n
+     * @param chr\n
+     * @return {boolean}\n
+     */\n
+    function isPctEncoded (chr) {\n
+        if (!isPercentDigitDigit(chr, 0)) {\n
+            return false;\n
+        }\n
+        var firstCharCode = parseHex2(chr, 1);\n
+        var numBytes = utf8.numBytes(firstCharCode);\n
+        if (numBytes === 0) {\n
+            return false;\n
+        }\n
+        for (var byteNumber = 1; byteNumber < numBytes; byteNumber += 1) {\n
+            if (!isPercentDigitDigit(chr, 3*byteNumber) || !utf8.isValidFollowingCharCode(parseHex2(chr, 3*byteNumber + 1))) {\n
+                return false;\n
+            }\n
+        }\n
+        return true;\n
+    }\n
+\n
+    /**\n
+     * Reads as much as needed from the text, e.g. \'%20\' or \'%C3%B6\'. It does not decode!\n
+     * @param text\n
+     * @param startIndex\n
+     * @return the character or pct-string of the text at startIndex\n
+     */\n
+    function pctCharAt(text, startIndex) {\n
+        var chr = text.charAt(startIndex);\n
+        if (!isPercentDigitDigit(text, startIndex)) {\n
+            return chr;\n
+        }\n
+        var utf8CharCode = parseHex2(text, startIndex + 1);\n
+        var numBytes = utf8.numBytes(utf8CharCode);\n
+        if (numBytes === 0) {\n
+            return chr;\n
+        }\n
+        for (var byteNumber = 1; byteNumber < numBytes; byteNumber += 1) {\n
+            if (!isPercentDigitDigit(text, startIndex + 3 * byteNumber) || !utf8.isValidFollowingCharCode(parseHex2(text, startIndex + 3 * byteNumber + 1))) {\n
+                return chr;\n
+            }\n
+        }\n
+        return text.substr(startIndex, 3 * numBytes);\n
+    }\n
 \n
-exports.util = {};\n
+    return {\n
+        encodeCharacter: encodeCharacter,\n
+        isPctEncoded: isPctEncoded,\n
+        pctCharAt: pctCharAt\n
+    };\n
+}());\n
 \n
-/**\n
- * Inherits the prototype methods from one constructor into another. The\n
- * prototype of `constructor` will be set to a new object created from\n
- * `superConstructor`.\n
- *\n
- * @param  {Function} constructor The constructor which inherits the super\n
- *   one\n
- * @param  {Function} superConstructor The super constructor\n
- */\n
-function inherits(constructor, superConstructor) {\n
-  constructor.super_ = superConstructor;\n
-  constructor.prototype = Object.create(superConstructor.prototype, {\n
-    "constructor": {\n
-      "configurable": true,\n
-      "enumerable": false,\n
-      "writable": true,\n
-      "value": constructor\n
-    }\n
-  });\n
-}\n
+var rfcCharHelper = (function () {\n
+\n
+    /**\n
+     * Returns if an character is an varchar character according 2.3 of rfc 6570\n
+     * @param chr\n
+     * @return (Boolean)\n
+     */\n
+    function isVarchar (chr) {\n
+        return charHelper.isAlpha(chr) || charHelper.isDigit(chr) || chr === \'_\' || pctEncoder.isPctEncoded(chr);\n
+    }\n
+\n
+    /**\n
+     * Returns if chr is an unreserved character according 1.5 of rfc 6570\n
+     * @param chr\n
+     * @return {Boolean}\n
+     */\n
+    function isUnreserved (chr) {\n
+        return charHelper.isAlpha(chr) || charHelper.isDigit(chr) || chr === \'-\' || chr === \'.\' || chr === \'_\' || chr === \'~\';\n
+    }\n
+\n
+    /**\n
+     * Returns if chr is an reserved character according 1.5 of rfc 6570\n
+     * or the percent character mentioned in 3.2.1.\n
+     * @param chr\n
+     * @return {Boolean}\n
+     */\n
+    function isReserved (chr) {\n
+        return chr === \':\' || chr === \'/\' || chr === \'?\' || chr === \'#\' || chr === \'[\' || chr === \']\' || chr === \'@\' || chr === \'!\' || chr === \'$\' || chr === \'&\' || chr === \'(\' ||\n
+            chr === \')\' || chr === \'*\' || chr === \'+\' || chr === \',\' || chr === \';\' || chr === \'=\' || chr === "\'";\n
+    }\n
+\n
+    return {\n
+        isVarchar: isVarchar,\n
+        isUnreserved: isUnreserved,\n
+        isReserved: isReserved\n
+    };\n
 \n
-/**\n
- * Clones jsonable object in depth\n
- *\n
- * @param  {A} object The jsonable object to clone\n
- * @return {A} The cloned object\n
- */\n
-function jsonDeepClone(object) {\n
-  var tmp = JSON.stringify(object);\n
-  if (tmp === undefined) {\n
-    return undefined;\n
-  }\n
-  return JSON.parse(tmp);\n
-}\n
-exports.util.jsonDeepClone = jsonDeepClone;\n
+}());\n
 \n
 /**\n
- * Clones all native object in deep. Managed types: Object, Array, String,\n
- * Number, Boolean, Function, null.\n
- *\n
- * It can also clone object which are serializable, like Date.\n
- *\n
- * To make a class serializable, you need to implement the `toJSON` function\n
- * which returns a JSON representation of the object. The returned value is used\n
- * as first parameter of the object constructor.\n
- *\n
- * @param  {A} object The object to clone\n
- * @return {A} The cloned object\n
+ * encoding of rfc 6570\n
  */\n
-function deepClone(object) {\n
-  var i, cloned;\n
-  if (Array.isArray(object)) {\n
-    cloned = [];\n
-    for (i = 0; i < object.length; i += 1) {\n
-      cloned[i] = deepClone(object[i]);\n
-    }\n
-    return cloned;\n
-  }\n
-  if (object === null) {\n
-    return null;\n
-  }\n
-  if (typeof object === \'object\') {\n
-    if (Object.getPrototypeOf(object) === Object.prototype) {\n
-      cloned = {};\n
-      for (i in object) {\n
-        if (object.hasOwnProperty(i)) {\n
-          cloned[i] = deepClone(object[i]);\n
+var encodingHelper = (function () {\n
+\n
+    function encode (text, passReserved) {\n
+        var\n
+            result = \'\',\n
+            index,\n
+            chr = \'\';\n
+        if (typeof text === "number" || typeof text === "boolean") {\n
+            text = text.toString();\n
         }\n
-      }\n
-      return cloned;\n
+        for (index = 0; index < text.length; index += chr.length) {\n
+            chr = text.charAt(index);\n
+            result += rfcCharHelper.isUnreserved(chr) || (passReserved && rfcCharHelper.isReserved(chr)) ? chr : pctEncoder.encodeCharacter(chr);\n
+        }\n
+        return result;\n
     }\n
-    if (object instanceof Date) {\n
-      // XXX this block is to enable phantomjs and browsers compatibility with\n
-      // Date.prototype.toJSON when it is an invalid date. In phantomjs, it\n
-      // returns `"Invalid Date"` but in browsers it returns `null`. In\n
-      // browsers, giving `null` as parameter to `new Date()` doesn\'t return an\n
-      // invalid date.\n
 \n
-      // Cloning a date with `return new Date(object)` has problems on Firefox.\n
-      // I don\'t know why...  (Tested on Firefox 23)\n
-\n
-      if (isFinite(object.getTime())) {\n
-        return new Date(object.toJSON());\n
-      }\n
-      return new Date("Invalid Date");\n
+    function encodePassReserved (text) {\n
+        return encode(text, true);\n
     }\n
-    // clone serializable objects\n
-    if (typeof object.toJSON === \'function\') {\n
-      return new (Object.getPrototypeOf(object).constructor)(object.toJSON());\n
-    }\n
-    // cannot clone\n
-    return object;\n
-  }\n
-  return object;\n
-}\n
-exports.util.deepClone = deepClone;\n
 \n
-/**\n
- * Update a dictionary by adding/replacing key values from another dict.\n
- * Enumerable values equal to undefined are also used.\n
- *\n
- * @param  {Object} original The dict to update\n
- * @param  {Object} other The other dict\n
- * @return {Object} The updated original dict\n
- */\n
-function dictUpdate(original, other) {\n
-  var k;\n
-  for (k in other) {\n
-    if (other.hasOwnProperty(k)) {\n
-      original[k] = other[k];\n
+    function encodeLiteralCharacter (literal, index) {\n
+        var chr = pctEncoder.pctCharAt(literal, index);\n
+        if (chr.length > 1) {\n
+            return chr;\n
+        }\n
+        else {\n
+            return rfcCharHelper.isReserved(chr) || rfcCharHelper.isUnreserved(chr) ? chr : pctEncoder.encodeCharacter(chr);\n
+        }\n
     }\n
-  }\n
-  return original;\n
-}\n
-exports.util.dictUpdate = dictUpdate;\n
 \n
-/**\n
- * Like \'dict.clear()\' in python. Delete all dict entries.\n
- *\n
- * @method dictClear\n
- * @param  {Object} self The dict to clear\n
- */\n
-function dictClear(dict) {\n
-  var i;\n
-  for (i in dict) {\n
-    if (dict.hasOwnProperty(i)) {\n
-      delete dict[i];\n
-      // dictClear(dict);\n
-      // break;\n
+    function encodeLiteral (literal) {\n
+        var\n
+            result = \'\',\n
+            index,\n
+            chr = \'\';\n
+        for (index = 0; index < literal.length; index += chr.length) {\n
+            chr = pctEncoder.pctCharAt(literal, index);\n
+            if (chr.length > 1) {\n
+                result += chr;\n
+            }\n
+            else {\n
+                result += rfcCharHelper.isReserved(chr) || rfcCharHelper.isUnreserved(chr) ? chr : pctEncoder.encodeCharacter(chr);\n
+            }\n
+        }\n
+        return result;\n
     }\n
-  }\n
-}\n
-exports.util.dictClear = dictClear;\n
 \n
-/**\n
- * Filter a dict to keep only values which keys are in `keys` list.\n
- *\n
- * @param  {Object} dict The dict to filter\n
- * @param  {Array} keys The key list to keep\n
- */\n
-function dictFilter(dict, keys) {\n
-  var i, buffer = [];\n
-  for (i = 0; i < keys.length; i += 1) {\n
-    buffer[i] = dict[keys[i]];\n
-  }\n
-  dictClear(dict);\n
-  for (i = 0; i < buffer.length; i += 1) {\n
-    dict[keys[i]] = buffer[i];\n
-  }\n
-}\n
-exports.util.dictFilter = dictFilter;\n
+    return {\n
+        encode: encode,\n
+        encodePassReserved: encodePassReserved,\n
+        encodeLiteral: encodeLiteral,\n
+        encodeLiteralCharacter: encodeLiteralCharacter\n
+    };\n
 \n
-/**\n
- * Gets all elements of an array and classifies them in a dict of array.\n
- * Dict keys are element types, and values are list of element of type \'key\'.\n
- *\n
- * @param  {Array} array The array of elements to pop\n
- * @return {Object} The type dict\n
- */\n
-function arrayValuesToTypeDict(array) {\n
-  var i, l, type_object = {}, type, v;\n
-  for (i = 0, l = array.length; i < l; i += 1) {\n
-    v = array[i];\n
-    type = Array.isArray(v) ? "array" : typeof v;\n
-    /*jslint ass: true */\n
-    (type_object[type] = type_object[type] || []).push(v);\n
-  }\n
-  return type_object;\n
-}\n
+}());\n
 \n
-/**\n
- * An Universal Unique ID generator\n
- *\n
- * @return {String} The new UUID.\n
- */\n
-function generateUuid() {\n
-  function S4() {\n
-    return (\'0000\' + Math.floor(\n
-      Math.random() * 0x10000 /* 65536 */\n
-    ).toString(16)).slice(-4);\n
-  }\n
-  return S4() + S4() + "-" +\n
-    S4() + "-" +\n
-    S4() + "-" +\n
-    S4() + "-" +\n
-    S4() + S4() + S4();\n
-}\n
-exports.util.generateUuid = generateUuid;\n
 \n
-/**\n
- * Concatenate a `string` `n` times.\n
- *\n
- * @param  {String} string The string to concat\n
- * @param  {Number} n The number of time to concat\n
- * @return {String} The concatenated string\n
- */\n
-function concatStringNTimes(string, n) {\n
-  /*jslint plusplus: true */\n
-  var res = "";\n
-  while (--n >= 0) { res += string; }\n
-  return res;\n
-}\n
+// the operators defined by rfc 6570\n
+var operators = (function () {\n
+\n
+    var\n
+        bySymbol = {};\n
+\n
+    function create (symbol) {\n
+        bySymbol[symbol] = {\n
+            symbol: symbol,\n
+            separator: (symbol === \'?\') ? \'&\' : (symbol === \'\' || symbol === \'+\' || symbol === \'#\') ? \',\' : symbol,\n
+            named: symbol === \';\' || symbol === \'&\' || symbol === \'?\',\n
+            ifEmpty: (symbol === \'&\' || symbol === \'?\') ? \'=\' : \'\',\n
+            first: (symbol === \'+\' ) ? \'\' : symbol,\n
+            encode: (symbol === \'+\' || symbol === \'#\') ? encodingHelper.encodePassReserved : encodingHelper.encode,\n
+            toString: function () {\n
+                return this.symbol;\n
+            }\n
+        };\n
+    }\n
+\n
+    create(\'\');\n
+    create(\'+\');\n
+    create(\'#\');\n
+    create(\'.\');\n
+    create(\'/\');\n
+    create(\';\');\n
+    create(\'?\');\n
+    create(\'&\');\n
+    return {\n
+        valueOf: function (chr) {\n
+            if (bySymbol[chr]) {\n
+                return bySymbol[chr];\n
+            }\n
+            if ("=,!@|".indexOf(chr) >= 0) {\n
+                return null;\n
+            }\n
+            return bySymbol[\'\'];\n
+        }\n
+    };\n
+}());\n
+\n
 \n
 /**\n
- * JSON stringify a value. Object keys are sorted in order to make a kind of\n
- * deepEqual thanks to a simple string comparison.\n
- *\n
- *     JSON.stringify({"a": "b", "c": "d"}) ===\n
- *       JSON.stringify({"c": "d", "a": "b"})                 // false\n
- *\n
- *     deepEqual({"a": "b", "c": "d"}, {"c": "d", "a": "b"}); // true\n
- *\n
- *     uniqueJSONStringify({"a": "b", "c": "d"}) ===\n
- *       uniqueJSONStringify({"c": "d", "a": "b"})            // true\n
- *\n
- * @param  {Any} value The value to stringify\n
- * @param  {Function,Array} [replacer] A function to replace values during parse\n
- * @param  {String,Number} [space] Causes the result to be pretty-printed\n
- * @return {String} The unique JSON stringified value\n
+ * Detects, whether a given element is defined in the sense of rfc 6570\n
+ * Section 2.3 of the RFC makes clear defintions:\n
+ * * undefined and null are not defined.\n
+ * * the empty string is defined\n
+ * * an array ("list") is defined, if it is not empty (even if all elements are not defined)\n
+ * * an object ("map") is defined, if it contains at least one property with defined value\n
+ * @param object\n
+ * @return {Boolean}\n
  */\n
-function uniqueJSONStringify(value, replacer, space) {\n
-  var indent, key_value_space = "";\n
-  if (typeof space === "string") {\n
-    if (space !== "") {\n
-      indent = space;\n
-      key_value_space = " ";\n
-    }\n
-  } else if (typeof space === "number") {\n
-    if (isFinite(space) && space > 0) {\n
-      indent = concatStringNTimes(" ", space);\n
-      key_value_space = " ";\n
+function isDefined (object) {\n
+    var\n
+        propertyName;\n
+    if (object === null || object === undefined) {\n
+        return false;\n
     }\n
-  }\n
-\n
-  function uniqueJSONStringifyRec(key, value, deep) {\n
-    var i, l, res, my_space;\n
-    if (value && typeof value.toJSON === "function") {\n
-      value = value.toJSON();\n
+    if (objectHelper.isArray(object)) {\n
+        // Section 2.3: A variable defined as a list value is considered undefined if the list contains zero members\n
+        return object.length > 0;\n
+    }\n
+    if (typeof object === "string" || typeof object === "number" || typeof object === "boolean") {\n
+        // falsy values like empty strings, false or 0 are "defined"\n
+        return true;\n
     }\n
-    if (typeof replacer === "function") {\n
-      value = replacer(key, value);\n
+    // else Object\n
+    for (propertyName in object) {\n
+        if (object.hasOwnProperty(propertyName) && isDefined(object[propertyName])) {\n
+            return true;\n
+        }\n
     }\n
+    return false;\n
+}\n
 \n
-    if (indent) {\n
-      my_space = concatStringNTimes(indent, deep);\n
+var LiteralExpression = (function () {\n
+    function LiteralExpression (literal) {\n
+        this.literal = encodingHelper.encodeLiteral(literal);\n
     }\n
-    if (Array.isArray(value)) {\n
-      res = [];\n
-      for (i = 0; i < value.length; i += 1) {\n
-        res[res.length] = uniqueJSONStringifyRec(i, value[i], deep + 1);\n
-        if (res[res.length - 1] === undefined) {\n
-          res[res.length - 1] = "null";\n
+\n
+    LiteralExpression.prototype.expand = function () {\n
+        return this.literal;\n
+    };\n
+\n
+    LiteralExpression.prototype.toString = LiteralExpression.prototype.expand;\n
+\n
+    return LiteralExpression;\n
+}());\n
+\n
+var parse = (function () {\n
+\n
+    function parseExpression (expressionText) {\n
+        var\n
+            operator,\n
+            varspecs = [],\n
+            varspec = null,\n
+            varnameStart = null,\n
+            maxLengthStart = null,\n
+            index,\n
+            chr = \'\';\n
+\n
+        function closeVarname () {\n
+            var varname = expressionText.substring(varnameStart, index);\n
+            if (varname.length === 0) {\n
+                throw new UriTemplateError({expressionText: expressionText, message: "a varname must be specified", position: index});\n
+            }\n
+            varspec = {varname: varname, exploded: false, maxLength: null};\n
+            varnameStart = null;\n
         }\n
-      }\n
-      if (res.length === 0) { return "[]"; }\n
-      if (indent) {\n
-        return "[\\n" + my_space + indent +\n
-          res.join(",\\n" + my_space + indent) +\n
-          "\\n" + my_space + "]";\n
-      }\n
-      return "[" + res.join(",") + "]";\n
-    }\n
-    if (typeof value === "object" && value !== null) {\n
-      if (Array.isArray(replacer)) {\n
-        res = replacer.reduce(function (p, c) {\n
-          p.push(c);\n
-          return p;\n
-        }, []);\n
-      } else {\n
-        res = Object.keys(value);\n
-      }\n
-      res.sort();\n
-      for (i = 0, l = res.length; i < l; i += 1) {\n
-        key = res[i];\n
-        res[i] = uniqueJSONStringifyRec(key, value[key], deep + 1);\n
-        if (res[i] !== undefined) {\n
-          res[i] = JSON.stringify(key) + ":" + key_value_space + res[i];\n
-        } else {\n
-          res.splice(i, 1);\n
-          l -= 1;\n
-          i -= 1;\n
+\n
+        function closeMaxLength () {\n
+            if (maxLengthStart === index) {\n
+                throw new UriTemplateError({expressionText: expressionText, message: "after a \':\' you have to specify the length", position: index});\n
+            }\n
+            varspec.maxLength = parseInt(expressionText.substring(maxLengthStart, index), 10);\n
+            maxLengthStart = null;\n
         }\n
-      }\n
-      if (res.length === 0) { return "{}"; }\n
-      if (indent) {\n
-        return "{\\n" + my_space + indent +\n
-          res.join(",\\n" + my_space + indent) +\n
-          "\\n" + my_space + "}";\n
-      }\n
-      return "{" + res.join(",") + "}";\n
-    }\n
-    return JSON.stringify(value);\n
-  }\n
-  return uniqueJSONStringifyRec("", value, 0);\n
-}\n
-exports.util.uniqueJSONStringify = uniqueJSONStringify;\n
 \n
-function makeBinaryStringDigest(string) {\n
-  return \'sha256-\' + hex_sha256(string);\n
-}\n
-exports.util.makeBinaryStringDigest = makeBinaryStringDigest;\n
-\n
-function readBlobAsBinaryString(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.readAsBinaryString(blob);\n
-  }, function () {\n
-    fr.abort();\n
-  });\n
-}\n
-exports.util.readBlobAsBinaryString = readBlobAsBinaryString;\n
-\n
-function readBlobAsArrayBuffer(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.readAsArrayBuffer(blob);\n
-  }, function () {\n
-    fr.abort();\n
-  });\n
-}\n
-exports.util.readBlobAsArrayBuffer = readBlobAsArrayBuffer;\n
-\n
-function readBlobAsText(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.readAsText(blob);\n
-  }, function () {\n
-    fr.abort();\n
-  });\n
-}\n
-exports.util.readBlobAsText = readBlobAsText;\n
+        operator = (function (operatorText) {\n
+            var op = operators.valueOf(operatorText);\n
+            if (op === null) {\n
+                throw new UriTemplateError({expressionText: expressionText, message: "illegal use of reserved operator", position: index, operator: operatorText});\n
+            }\n
+            return op;\n
+        }(expressionText.charAt(0)));\n
+        index = operator.symbol.length;\n
+\n
+        varnameStart = index;\n
+\n
+        for (; index < expressionText.length; index += chr.length) {\n
+            chr = pctEncoder.pctCharAt(expressionText, index);\n
+\n
+            if (varnameStart !== null) {\n
+                // the spec says: varname =  varchar *( ["."] varchar )\n
+                // so a dot is allowed except for the first char\n
+                if (chr === \'.\') {\n
+                    if (varnameStart === index) {\n
+                        throw new UriTemplateError({expressionText: expressionText, message: "a varname MUST NOT start with a dot", position: index});\n
+                    }\n
+                    continue;\n
+                }\n
+                if (rfcCharHelper.isVarchar(chr)) {\n
+                    continue;\n
+                }\n
+                closeVarname();\n
+            }\n
+            if (maxLengthStart !== null) {\n
+                if (index === maxLengthStart && chr === \'0\') {\n
+                    throw new UriTemplateError({expressionText: expressionText, message: "A :prefix must not start with digit 0", position: index});\n
+                }\n
+                if (charHelper.isDigit(chr)) {\n
+                    if (index - maxLengthStart >= 4) {\n
+                        throw new UriTemplateError({expressionText: expressionText, message: "A :prefix must have max 4 digits", position: index});\n
+                    }\n
+                    continue;\n
+                }\n
+                closeMaxLength();\n
+            }\n
+            if (chr === \':\') {\n
+                if (varspec.maxLength !== null) {\n
+                    throw new UriTemplateError({expressionText: expressionText, message: "only one :maxLength is allowed per varspec", position: index});\n
+                }\n
+                if (varspec.exploded) {\n
+                    throw new UriTemplateError({expressionText: expressionText, message: "an exploeded varspec MUST NOT be varspeced", position: index});\n
+                }\n
+                maxLengthStart = index + 1;\n
+                continue;\n
+            }\n
+            if (chr === \'*\') {\n
+                if (varspec === null) {\n
+                    throw new UriTemplateError({expressionText: expressionText, message: "exploded without varspec", position: index});\n
+                }\n
+                if (varspec.exploded) {\n
+                    throw new UriTemplateError({expressionText: expressionText, message: "exploded twice", position: index});\n
+                }\n
+                if (varspec.maxLength) {\n
+                    throw new UriTemplateError({expressionText: expressionText, message: "an explode (*) MUST NOT follow to a prefix", position: index});\n
+                }\n
+                varspec.exploded = true;\n
+                continue;\n
+            }\n
+            // the only legal character now is the comma\n
+            if (chr === \',\') {\n
+                varspecs.push(varspec);\n
+                varspec = null;\n
+                varnameStart = index + 1;\n
+                continue;\n
+            }\n
+            throw new UriTemplateError({expressionText: expressionText, message: "illegal character", character: chr, position: index});\n
+        } // for chr\n
+        if (varnameStart !== null) {\n
+            closeVarname();\n
+        }\n
+        if (maxLengthStart !== null) {\n
+            closeMaxLength();\n
+        }\n
+        varspecs.push(varspec);\n
+        return new VariableExpression(expressionText, operator, varspecs);\n
+    }\n
+\n
+    function escape_regexp_string(string) {\n
+      // http://simonwillison.net/2006/Jan/20/escape/\n
+      return string.replace(/[\\-\\[\\]{}()*+?.,\\\\\\^$|#\\s]/g, "\\\\$&");\n
+    }\n
+\n
+    function parse (uriTemplateText) {\n
+        // assert filled string\n
+        var\n
+            index,\n
+            chr,\n
+            expressions = [],\n
+            expression,\n
+            braceOpenIndex = null,\n
+            regexp_string = \'\',\n
+            can_match = true,\n
+            literalStart = 0;\n
+        for (index = 0; index < uriTemplateText.length; index += 1) {\n
+            chr = uriTemplateText.charAt(index);\n
+            if (literalStart !== null) {\n
+                if (chr === \'}\') {\n
+                    throw new UriTemplateError({templateText: uriTemplateText, message: "unopened brace closed", position: index});\n
+                }\n
+                if (chr === \'{\') {\n
+                    if (literalStart < index) {\n
+                        expression = new LiteralExpression(uriTemplateText.substring(literalStart, index));\n
+                        expressions.push(expression);\n
+                        regexp_string += escape_regexp_string(\n
+                            expression.literal);\n
+                    }\n
+                    literalStart = null;\n
+                    braceOpenIndex = index;\n
+                }\n
+                continue;\n
+            }\n
 \n
-/**\n
- * Send request with XHR and return a promise. xhr.onload: The promise is\n
- * resolved when the status code is lower than 400 with the xhr object as first\n
- * parameter. xhr.onerror: reject with xhr object as first\n
- * parameter. xhr.onprogress: notifies the xhr object.\n
- *\n
- * @param  {Object} param The parameters\n
- * @param  {String} [param.type="GET"] The request method\n
- * @param  {String} [param.dataType=""] The data type to retrieve\n
- * @param  {String} param.url The url\n
- * @param  {Any} [param.data] The data to send\n
- * @param  {Function} [param.beforeSend] A function called just before the send\n
- *   request. The first parameter of this function is the XHR object.\n
- * @return {Promise} The promise\n
- */\n
-function ajax(param) {\n
-  var xhr = new XMLHttpRequest();\n
-  return new RSVP.Promise(function (resolve, reject, notify) {\n
-    var k;\n
-    xhr.open(param.type || "GET", param.url, true);\n
-    xhr.responseType = param.dataType || "";\n
-    if (typeof param.headers === \'object\' && param.headers !== null) {\n
-      for (k in param.headers) {\n
-        if (param.headers.hasOwnProperty(k)) {\n
-          xhr.setRequestHeader(k, param.headers[k]);\n
+            if (braceOpenIndex !== null) {\n
+                // here just { is forbidden\n
+                if (chr === \'{\') {\n
+                    throw new UriTemplateError({templateText: uriTemplateText, message: "brace already opened", position: index});\n
+                }\n
+                if (chr === \'}\') {\n
+                    if (braceOpenIndex + 1 === index) {\n
+                        throw new UriTemplateError({templateText: uriTemplateText, message: "empty braces", position: braceOpenIndex});\n
+                    }\n
+                    try {\n
+                        expression = parseExpression(uriTemplateText.substring(braceOpenIndex + 1, index));\n
+                    }\n
+                    catch (error) {\n
+                        if (error.prototype === UriTemplateError.prototype) {\n
+                            throw new UriTemplateError({templateText: uriTemplateText, message: error.options.message, position: braceOpenIndex + error.options.position, details: error.options});\n
+                        }\n
+                        throw error;\n
+                    }\n
+                    expressions.push(expression);\n
+                    if (expression.operator.symbol.length === 0) {\n
+                      regexp_string += "([^/]+)";\n
+                    } else {\n
+                      can_match = false;\n
+                    }\n
+                    braceOpenIndex = null;\n
+                    literalStart = index + 1;\n
+                }\n
+                continue;\n
+            }\n
+            throw new Error(\'reached unreachable code\');\n
         }\n
-      }\n
-    }\n
-    xhr.addEventListener("load", function (e) {\n
-      if (e.target.status >= 400) {\n
-        return reject(e);\n
-      }\n
-      resolve(e);\n
-    });\n
-    xhr.addEventListener("error", reject);\n
-    xhr.addEventListener("progress", notify);\n
-    if (typeof param.xhrFields === \'object\' && param.xhrFields !== null) {\n
-      for (k in param.xhrFields) {\n
-        if (param.xhrFields.hasOwnProperty(k)) {\n
-          xhr[k] = param.xhrFields[k];\n
+        if (braceOpenIndex !== null) {\n
+            throw new UriTemplateError({templateText: uriTemplateText, message: "unclosed brace", position: braceOpenIndex});\n
         }\n
-      }\n
-    }\n
-    if (typeof param.beforeSend === \'function\') {\n
-      param.beforeSend(xhr);\n
+        if (literalStart < uriTemplateText.length) {\n
+            expression = new LiteralExpression(uriTemplateText.substring(literalStart));\n
+            expressions.push(expression);\n
+            regexp_string += escape_regexp_string(expression.literal);\n
+        }\n
+        if (can_match === false) {\n
+          regexp_string = undefined;\n
+        }\n
+        return new UriTemplate(uriTemplateText, expressions, regexp_string);\n
     }\n
-    xhr.send(param.data);\n
-  }, function () {\n
-    xhr.abort();\n
-  });\n
-}\n
-exports.util.ajax = ajax;\n
 \n
-/**\n
- * Acts like `Array.prototype.concat` but does not create a copy of the original\n
- * array. It extends the original array and return it.\n
- *\n
- * @param  {Array} array The array to extend\n
- * @param  {Any} [args]* Values to add in the array\n
- * @return {Array} The original array\n
- */\n
-function arrayExtend(array) { // args*\n
-  var i, j;\n
-  for (i = 1; i < arguments.length; i += 1) {\n
-    if (Array.isArray(arguments[i])) {\n
-      for (j = 0; j < arguments[i].length; j += 1) {\n
-        array[array.length] = arguments[i][j];\n
-      }\n
-    } else {\n
-      array[array.length] = arguments[i];\n
+    return parse;\n
+}());\n
+\n
+var VariableExpression = (function () {\n
+    // helper function if JSON is not available\n
+    function prettyPrint (value) {\n
+        return (JSON && JSON.stringify) ? JSON.stringify(value) : value;\n
     }\n
-  }\n
-  return array;\n
-}\n
-exports.util.arrayExtend = arrayExtend;\n
 \n
-/**\n
- * Acts like `Array.prototype.concat` but does not create a copy of the original\n
- * array. It extends the original array from a specific position and return it.\n
- *\n
- * @param  {Array} array The array to extend\n
- * @param  {Number} position The position where to extend\n
- * @param  {Any} [args]* Values to add in the array\n
- * @return {Array} The original array\n
- */\n
-function arrayInsert(array, position) { // args*\n
-  var array_part = array.splice(position, array.length - position);\n
-  arrayExtend.apply(null, arrayExtend([\n
-  ], [array], Array.prototype.slice.call(arguments, 2)));\n
-  return arrayExtend(array, array_part);\n
-}\n
-exports.util.arrayInsert = arrayInsert;\n
+    function isEmpty (value) {\n
+        if (!isDefined(value)) {\n
+            return true;\n
+        }\n
+        if (objectHelper.isString(value)) {\n
+            return value === \'\';\n
+        }\n
+        if (objectHelper.isNumber(value) || objectHelper.isBoolean(value)) {\n
+            return false;\n
+        }\n
+        if (objectHelper.isArray(value)) {\n
+            return value.length === 0;\n
+        }\n
+        for (var propertyName in value) {\n
+            if (value.hasOwnProperty(propertyName)) {\n
+                return false;\n
+            }\n
+        }\n
+        return true;\n
+    }\n
 \n
-/**\n
- * Guess if the method is a writer or a reader.\n
- *\n
- * @param  {String} method The method name\n
- * @return {String} "writer", "reader" or "unknown"\n
- */\n
-function methodType(method) {\n
-  switch (method) {\n
-  case "post":\n
-  case "put":\n
-  case "putAttachment":\n
-  case "remove":\n
-  case "removeAttachment":\n
-  case "repair":\n
-    return \'writer\';\n
-  case "get":\n
-  case "getAttachment":\n
-  case "allDocs":\n
-  case "check":\n
-    return \'reader\';\n
-  default:\n
-    return \'unknown\';\n
-  }\n
-}\n
+    function propertyArray (object) {\n
+        var\n
+            result = [],\n
+            propertyName;\n
+        for (propertyName in object) {\n
+            if (object.hasOwnProperty(propertyName)) {\n
+                result.push({name: propertyName, value: object[propertyName]});\n
+            }\n
+        }\n
+        return result;\n
+    }\n
 \n
-/**\n
- *     forEach(array, callback[, thisArg]): Promise\n
- *\n
- * It executes the provided `callback` once for each element of the array with\n
- * an assigned value asynchronously. If the `callback` returns a promise, then\n
- * the function will wait for its fulfillment before executing the next\n
- * iteration.\n
- *\n
- * `callback` is invoked with three arguments:\n
- *\n
- * - the element value\n
- * - the element index\n
- * - the array being traversed\n
- *\n
- * If a `thisArg` parameter is provided to `forEach`, it will be passed to\n
- * `callback` when invoked, for use as its `this` value.  Otherwise, the value\n
- * `undefined` will be passed for use as its `this` value.\n
- *\n
- * Unlike `Array.prototype.forEach`, you can stop the iteration by throwing\n
- * something, or by doing a `cancel` to the returned promise if it is\n
- * cancellable promise.\n
- *\n
- * Inspired by `Array.prototype.forEach` from Mozilla Developer Network.\n
- *\n
- * @param  {Array} array The array to parse\n
- * @param  {Function} callback Function to execute for each element.\n
- * @param  {Any} [thisArg] Value to use as `this` when executing `callback`.\n
- * @param  {Promise} A new promise.\n
- */\n
-function forEach(array, fn, thisArg) {\n
-  if (arguments.length === 0) {\n
-    throw new TypeError("missing argument 0 when calling function forEach");\n
-  }\n
-  if (!Array.isArray(array)) {\n
-    throw new TypeError(array + " is not an array");\n
-  }\n
-  if (arguments.length === 1) {\n
-    throw new TypeError("missing argument 1 when calling function forEach");\n
-  }\n
-  if (typeof fn !== "function") {\n
-    throw new TypeError(fn + " is not a function");\n
-  }\n
-  var cancelled, current_promise = RSVP.resolve();\n
-  return new RSVP.Promise(function (done, fail, notify) {\n
-    var i = 0;\n
-    function next() {\n
-      if (cancelled) {\n
-        fail(new Error("Cancelled"));\n
-        return;\n
-      }\n
-      if (i < array.length) {\n
-        current_promise =\n
-          current_promise.then(fn.bind(thisArg, array[i], i, array));\n
-        current_promise.then(next, fail, notify);\n
-        i += 1;\n
-        return;\n
-      }\n
-      done();\n
+    function VariableExpression (templateText, operator, varspecs) {\n
+        this.templateText = templateText;\n
+        this.operator = operator;\n
+        this.varspecs = varspecs;\n
     }\n
-    next();\n
-  }, function () {\n
-    cancelled = true;\n
-    if (typeof current_promise.cancel === "function") {\n
-      current_promise.cancel();\n
+\n
+    VariableExpression.prototype.toString = function () {\n
+        return this.templateText;\n
+    };\n
+\n
+    function expandSimpleValue(varspec, operator, value) {\n
+        var result = \'\';\n
+        value = value.toString();\n
+        if (operator.named) {\n
+            result += encodingHelper.encodeLiteral(varspec.varname);\n
+            if (value === \'\') {\n
+                result += operator.ifEmpty;\n
+                return result;\n
+            }\n
+            result += \'=\';\n
+        }\n
+        if (varspec.maxLength !== null) {\n
+            value = value.substr(0, varspec.maxLength);\n
+        }\n
+        result += operator.encode(value);\n
+        return result;\n
     }\n
-  });\n
-}\n
-exports.util.forEach = forEach;\n
 \n
-/**\n
- *     range(stop, callback): Promise\n
- *     range(start, stop[, step], callback): Promise\n
- *\n
- * It executes the provided `callback` once for each step between `start` and\n
- * `stop`. If the `callback` returns a promise, then the function will wait\n
- * for its fulfillment before executing the next iteration.\n
- *\n
- * `callback` is invoked with one argument:\n
- *\n
- * - the index of the step\n
- *\n
- * `start`, `stop` and `step` must be finite numbers. If `step` is not\n
- * provided, then the default step will be `1`. If `start` and `step` are not\n
- * provided, `start` will be `0` and `step` will be `1`.\n
- *\n
- * Inspired by `range()` from Python 3 built-in functions.\n
- *\n
- *     range(10, function (index) {\n
- *       return notifyIndex(index);\n
- *     }).then(onDone, onError, onNotify);\n
- *\n
- * @param  {Number} [start=0] The start index\n
- * @param  {Number} stop The stop index\n
- * @param  {Number} [step=1] One step\n
- * @param  {Function} callback Function to execute on each iteration.\n
- * @param  {Promise} A new promise with no fulfillment value.\n
- */\n
-function range(start, stop, step, callback) {\n
-  var type_object, cancelled, current_promise;\n
-  type_object = arrayValuesToTypeDict([start, stop, step, callback]);\n
+    function valueDefined (nameValue) {\n
+        return isDefined(nameValue.value);\n
+    }\n
 \n
-  if (type_object["function"].length !== 1) {\n
-    throw new TypeError("range(): only one callback is needed");\n
-  }\n
-  start = type_object.number.length;\n
-  if (start < 1) {\n
-    throw new TypeError("range(): 1, 2 or 3 numbers are needed");\n
-  }\n
-  if (start > 3) {\n
-    throw new TypeError("range(): only 1, 2 or 3 numbers are needed");\n
-  }\n
+    function expandNotExploded(varspec, operator, value) {\n
+        var\n
+            arr = [],\n
+            result = \'\';\n
+        if (operator.named) {\n
+            result += encodingHelper.encodeLiteral(varspec.varname);\n
+            if (isEmpty(value)) {\n
+                result += operator.ifEmpty;\n
+                return result;\n
+            }\n
+            result += \'=\';\n
+        }\n
+        if (objectHelper.isArray(value)) {\n
+            arr = value;\n
+            arr = objectHelper.filter(arr, isDefined);\n
+            arr = objectHelper.map(arr, operator.encode);\n
+            result += objectHelper.join(arr, \',\');\n
+        }\n
+        else {\n
+            arr = propertyArray(value);\n
+            arr = objectHelper.filter(arr, valueDefined);\n
+            arr = objectHelper.map(arr, function (nameValue) {\n
+                return operator.encode(nameValue.name) + \',\' + operator.encode(nameValue.value);\n
+            });\n
+            result += objectHelper.join(arr, \',\');\n
+        }\n
+        return result;\n
+    }\n
+\n
+    function expandExplodedNamed (varspec, operator, value) {\n
+        var\n
+            isArray = objectHelper.isArray(value),\n
+            arr = [];\n
+        if (isArray) {\n
+            arr = value;\n
+            arr = objectHelper.filter(arr, isDefined);\n
+            arr = objectHelper.map(arr, function (listElement) {\n
+                var tmp = encodingHelper.encodeLiteral(varspec.varname);\n
+                if (isEmpty(listElement)) {\n
+                    tmp += operator.ifEmpty;\n
+                }\n
+                else {\n
+                    tmp += \'=\' + operator.encode(listElement);\n
+                }\n
+                return tmp;\n
+            });\n
+        }\n
+        else {\n
+            arr = propertyArray(value);\n
+            arr = objectHelper.filter(arr, valueDefined);\n
+            arr = objectHelper.map(arr, function (nameValue) {\n
+                var tmp = encodingHelper.encodeLiteral(nameValue.name);\n
+                if (isEmpty(nameValue.value)) {\n
+                    tmp += operator.ifEmpty;\n
+                }\n
+                else {\n
+                    tmp += \'=\' + operator.encode(nameValue.value);\n
+                }\n
+                return tmp;\n
+            });\n
+        }\n
+        return objectHelper.join(arr, operator.separator);\n
+    }\n
+\n
+    function expandExplodedUnnamed (operator, value) {\n
+        var\n
+            arr = [],\n
+            result = \'\';\n
+        if (objectHelper.isArray(value)) {\n
+            arr = value;\n
+            arr = objectHelper.filter(arr, isDefined);\n
+            arr = objectHelper.map(arr, operator.encode);\n
+            result += objectHelper.join(arr, operator.separator);\n
+        }\n
+        else {\n
+            arr = propertyArray(value);\n
+            arr = objectHelper.filter(arr, function (nameValue) {\n
+                return isDefined(nameValue.value);\n
+            });\n
+            arr = objectHelper.map(arr, function (nameValue) {\n
+                return operator.encode(nameValue.name) + \'=\' + operator.encode(nameValue.value);\n
+            });\n
+            result += objectHelper.join(arr, operator.separator);\n
+        }\n
+        return result;\n
+    }\n
+\n
+\n
+    VariableExpression.prototype.expand = function (variables) {\n
+        var\n
+            expanded = [],\n
+            index,\n
+            varspec,\n
+            value,\n
+            valueIsArr,\n
+            oneExploded = false,\n
+            operator = this.operator;\n
+\n
+        // expand each varspec and join with operator\'s separator\n
+        for (index = 0; index < this.varspecs.length; index += 1) {\n
+            varspec = this.varspecs[index];\n
+            value = variables[varspec.varname];\n
+            // if (!isDefined(value)) {\n
+            // if (variables.hasOwnProperty(varspec.name)) {\n
+            if (value === null || value === undefined) {\n
+                continue;\n
+            }\n
+            if (varspec.exploded) {\n
+                oneExploded = true;\n
+            }\n
+            valueIsArr = objectHelper.isArray(value);\n
+            if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {\n
+                expanded.push(expandSimpleValue(varspec, operator, value));\n
+            }\n
+            else if (varspec.maxLength && isDefined(value)) {\n
+                // 2.4.1 of the spec says: "Prefix modifiers are not applicable to variables that have composite values."\n
+                throw new Error(\'Prefix modifiers are not applicable to variables that have composite values. You tried to expand \' + this + " with " + prettyPrint(value));\n
+            }\n
+            else if (!varspec.exploded) {\n
+                if (operator.named || !isEmpty(value)) {\n
+                    expanded.push(expandNotExploded(varspec, operator, value));\n
+                }\n
+            }\n
+            else if (isDefined(value)) {\n
+                if (operator.named) {\n
+                    expanded.push(expandExplodedNamed(varspec, operator, value));\n
+                }\n
+                else {\n
+                    expanded.push(expandExplodedUnnamed(operator, value));\n
+                }\n
+            }\n
+        }\n
+\n
+        if (expanded.length === 0) {\n
+            return "";\n
+        }\n
+        else {\n
+            return operator.first + objectHelper.join(expanded, operator.separator);\n
+        }\n
+    };\n
 \n
-  callback = type_object["function"][0];\n
+    return VariableExpression;\n
+}());\n
 \n
-  if (start === 1) {\n
-    start = 0;\n
-    stop = type_object.number[0];\n
-    step = 1;\n
-  }\n
+var UriTemplate = (function () {\n
+    function UriTemplate (templateText, expressions, regexp_string) {\n
+        this.templateText = templateText;\n
+        this.expressions = expressions;\n
 \n
-  if (start === 2) {\n
-    start = type_object.number[0];\n
-    stop = type_object.number[1];\n
-    step = 1;\n
-  }\n
+        if (regexp_string !== undefined) {\n
+          this.regexp = new RegExp("^" + regexp_string + "$");\n
+        }\n
 \n
-  if (start === 3) {\n
-    start = type_object.number[0];\n
-    stop = type_object.number[1];\n
-    step = type_object.number[2];\n
-    if (step === 0) {\n
-      throw new TypeError("range(): step must not be zero");\n
+        objectHelper.deepFreeze(this);\n
     }\n
-  }\n
 \n
-  type_object = undefined;\n
-  current_promise = RSVP.resolve();\n
-  return new RSVP.Promise(function (done, fail, notify) {\n
-    var i = start, test;\n
-    function next() {\n
-      if (cancelled) {\n
-        fail(new Error("Cancelled"));\n
-        return;\n
-      }\n
-      test = step > 0 ? i < stop : i > stop;\n
-      if (test) {\n
-        current_promise = current_promise.then(callback.bind(null, i));\n
-        current_promise.then(next, fail, notify);\n
-        i += step;\n
-        return;\n
+    UriTemplate.prototype.toString = function () {\n
+        return this.templateText;\n
+    };\n
+\n
+    UriTemplate.prototype.expand = function (variables) {\n
+        // this.expressions.map(function (expression) {return expression.expand(variables);}).join(\'\');\n
+        var\n
+            index,\n
+            result = \'\';\n
+        for (index = 0; index < this.expressions.length; index += 1) {\n
+            result += this.expressions[index].expand(variables);\n
+        }\n
+        return result;\n
+    };\n
+\n
+    UriTemplate.prototype.extract = function (text) {\n
+      var expression_index,\n
+          extracted_index = 1,\n
+          expression,\n
+          varspec,\n
+          matched = true,\n
+          variables = {},\n
+          result;\n
+\n
+      if ((this.regexp !== undefined) && (this.regexp.test(text))) {\n
+        result = this.regexp.exec(text);\n
+        for (expression_index = 0; expression_index < this.expressions.length; expression_index += 1) {\n
+          expression = this.expressions[expression_index];\n
+          if (expression.literal === undefined) {\n
+            if ((expression.operator !== undefined) && (expression.operator.symbol.length === 0) && (expression.varspecs.length === 1)) {\n
+              varspec = expression.varspecs[0];\n
+              if ((varspec.exploded === false) && (varspec.maxLength === null)) {\n
+                if (result[extracted_index].indexOf(\',\') === -1) {\n
+                  variables[varspec.varname] = decodeURIComponent(result[extracted_index]);\n
+                  extracted_index += 1;\n
+                } else {\n
+                  matched = false;\n
+                }\n
+              } else {\n
+                matched = false;\n
+              }\n
+            } else {\n
+              matched = false;\n
+            }\n
+          }\n
+        }\n
+        if (matched) {\n
+          return variables;\n
+        }\n
       }\n
-      done();\n
-    }\n
-    next();\n
-  }, function () {\n
-    cancelled = true;\n
-    if (typeof current_promise.cancel === "function") {\n
-      current_promise.cancel();\n
-    }\n
-  });\n
-}\n
-exports.util.range = range;\n
+      return false;\n
+    };\n
 \n
-/*jslint indent: 2, maxlen: 80, nomen: true, sloppy: true */\n
-/*global secureMethods, exports, console */\n
+    UriTemplate.parse = parse;\n
+    UriTemplate.UriTemplateError = UriTemplateError;\n
+    return UriTemplate;\n
+}());\n
 \n
-/**\n
- * Inspired by nodejs EventEmitter class\n
- * http://nodejs.org/api/events.html\n
- *\n
- * When an EventEmitter instance experiences an error, the typical action is\n
- * to emit an \'error\' event. Error events are treated as a special case in\n
- * node. If there is no listener for it, then the default action throws the\n
- * exception again.\n
- *\n
- * All EventEmitters emit the event \'newListener\' when new listeners are added\n
- * and \'removeListener\' when a listener is removed.\n
- *\n
- * @class EventEmitter\n
- * @constructor\n
- */\n
-function EventEmitter() {\n
-  this._events = {};\n
-  this._maxListeners = 10;\n
+    exportCallback(UriTemplate);\n
+\n
+}(function (UriTemplate) {\n
+        "use strict";\n
+        // export UriTemplate, when module is present, or pass it to window or global\n
+        if (typeof module !== "undefined") {\n
+            module.exports = UriTemplate;\n
+        }\n
+        else if (typeof define === "function") {\n
+            define([],function() {\n
+                return UriTemplate;\n
+            });\n
+        }\n
+        else if (typeof window !== "undefined") {\n
+            window.UriTemplate = UriTemplate;\n
+        }\n
+        else {\n
+            global.UriTemplate = UriTemplate;\n
+        }\n
+    }\n
+));\n
+;// Copyright (c) 2013 Pieroxy <pieroxy@pieroxy.net>\n
+// This work is free. You can redistribute it and/or modify it\n
+// under the terms of the WTFPL, Version 2\n
+// For more information see LICENSE.txt or http://www.wtfpl.net/\n
+//\n
+// For more information, the home page:\n
+// http://pieroxy.net/blog/pages/lz-string/testing.html\n
+//\n
+// LZ-based compression algorithm, version 1.4.4\n
+var LZString = (function() {\n
+\n
+// private property\n
+var f = String.fromCharCode;\n
+var keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";\n
+var keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$";\n
+var baseReverseDic = {};\n
+\n
+function getBaseValue(alphabet, character) {\n
+  if (!baseReverseDic[alphabet]) {\n
+    baseReverseDic[alphabet] = {};\n
+    for (var i=0 ; i<alphabet.length ; i++) {\n
+      baseReverseDic[alphabet][alphabet.charAt(i)] = i;\n
+    }\n
+  }\n
+  return baseReverseDic[alphabet][character];\n
 }\n
 \n
-/**\n
- * Adds a listener to the end of the listeners array for the specified\n
- * event.\n
- *\n
- * @method addListener\n
- * @param  {String} event The event name\n
- * @param  {Function} listener The listener callback\n
- * @return {EventEmitter} This emitter\n
- */\n
-EventEmitter.prototype.addListener = function (event, listener) {\n
-  var listener_list;\n
-  if (typeof listener !== "function") {\n
-    return this;\n
-  }\n
-  this.emit("newListener", event, listener);\n
-  listener_list = this._events[event];\n
-  if (listener_list === undefined) {\n
-    this._events[event] = listener;\n
-    listener_list = listener;\n
-  } else if (typeof listener_list === "function") {\n
-    this._events[event] = [listener_list, listener];\n
-    listener_list = this._events[event];\n
-  } else {\n
-    listener_list[listener_list.length] = listener;\n
-  }\n
-  if (this._maxListeners > 0 &&\n
-      typeof listener_list !== "function" &&\n
-      listener_list.length > this._maxListeners &&\n
-      listener_list.warned !== true) {\n
-    console.warn("warning: possible EventEmitter memory leak detected. " +\n
-                 listener_list.length + " listeners added. " +\n
-                 "Use emitter.setMaxListeners() to increase limit.");\n
-    listener_list.warned = true;\n
-  }\n
-  return this;\n
-};\n
+var LZString = {\n
+  compressToBase64 : function (input) {\n
+    if (input == null) return "";\n
+    var res = LZString._compress(input, 6, function(a){return keyStrBase64.charAt(a);});\n
+    switch (res.length % 4) { // To produce valid Base64\n
+    default: // When could this happen ?\n
+    case 0 : return res;\n
+    case 1 : return res+"===";\n
+    case 2 : return res+"==";\n
+    case 3 : return res+"=";\n
+    }\n
+  },\n
+\n
+  decompressFromBase64 : function (input) {\n
+    if (input == null) return "";\n
+    if (input == "") return null;\n
+    return LZString._decompress(input.length, 32, function(index) { return getBaseValue(keyStrBase64, input.charAt(index)); });\n
+  },\n
+\n
+  compressToUTF16 : function (input) {\n
+    if (input == null) return "";\n
+    return LZString._compress(input, 15, function(a){return f(a+32);}) + " ";\n
+  },\n
+\n
+  decompressFromUTF16: function (compressed) {\n
+    if (compressed == null) return "";\n
+    if (compressed == "") return null;\n
+    return LZString._decompress(compressed.length, 16384, function(index) { return compressed.charCodeAt(index) - 32; });\n
+  },\n
+\n
+  //compress into uint8array (UCS-2 big endian format)\n
+  compressToUint8Array: function (uncompressed) {\n
+    var compressed = LZString.compress(uncompressed);\n
+    var buf=new Uint8Array(compressed.length*2); // 2 bytes per character\n
+\n
+    for (var i=0, TotalLen=compressed.length; i<TotalLen; i++) {\n
+      var current_value = compressed.charCodeAt(i);\n
+      buf[i*2] = current_value >>> 8;\n
+      buf[i*2+1] = current_value % 256;\n
+    }\n
+    return buf;\n
+  },\n
+\n
+  //decompress from uint8array (UCS-2 big endian format)\n
+  decompressFromUint8Array:function (compressed) {\n
+    if (compressed===null || compressed===undefined){\n
+        return LZString.decompress(compressed);\n
+    } else {\n
+        var buf=new Array(compressed.length/2); // 2 bytes per character\n
+        for (var i=0, TotalLen=buf.length; i<TotalLen; i++) {\n
+          buf[i]=compressed[i*2]*256+compressed[i*2+1];\n
+        }\n
 \n
-/**\n
- * #crossLink "EventEmitter/addListener:method"\n
- *\n
- * @method on\n
- */\n
-EventEmitter.prototype.on = EventEmitter.prototype.addListener;\n
+        var result = [];\n
+        buf.forEach(function (c) {\n
+          result.push(f(c));\n
+        });\n
+        return LZString.decompress(result.join(\'\'));\n
+\n
+    }\n
+\n
+  },\n
+\n
+\n
+  //compress into a string that is already URI encoded\n
+  compressToEncodedURIComponent: function (input) {\n
+    if (input == null) return "";\n
+    return LZString._compress(input, 6, function(a){return keyStrUriSafe.charAt(a);});\n
+  },\n
+\n
+  //decompress from an output of compressToEncodedURIComponent\n
+  decompressFromEncodedURIComponent:function (input) {\n
+    if (input == null) return "";\n
+    if (input == "") return null;\n
+    input = input.replace(/ /g, "+");\n
+    return LZString._decompress(input.length, 32, function(index) { return getBaseValue(keyStrUriSafe, input.charAt(index)); });\n
+  },\n
+\n
+  compress: function (uncompressed) {\n
+    return LZString._compress(uncompressed, 16, function(a){return f(a);});\n
+  },\n
+  _compress: function (uncompressed, bitsPerChar, getCharFromInt) {\n
+    if (uncompressed == null) return "";\n
+    var i, value,\n
+        context_dictionary= {},\n
+        context_dictionaryToCreate= {},\n
+        context_c="",\n
+        context_wc="",\n
+        context_w="",\n
+        context_enlargeIn= 2, // Compensate for the first entry which should not count\n
+        context_dictSize= 3,\n
+        context_numBits= 2,\n
+        context_data=[],\n
+        context_data_val=0,\n
+        context_data_position=0,\n
+        ii;\n
+\n
+    for (ii = 0; ii < uncompressed.length; ii += 1) {\n
+      context_c = uncompressed.charAt(ii);\n
+      if (!Object.prototype.hasOwnProperty.call(context_dictionary,context_c)) {\n
+        context_dictionary[context_c] = context_dictSize++;\n
+        context_dictionaryToCreate[context_c] = true;\n
+      }\n
 \n
-/**\n
- * Adds a one time listener for the event. This listener is invoked only the\n
- * next time the event is fired, after which it is removed.\n
- *\n
- * @method once\n
- * @param  {String} event The event name\n
- * @param  {Function} listener The listener callback\n
- * @return {EventEmitter} This emitter\n
- */\n
-EventEmitter.prototype.once = function (event, listener) {\n
-  var that = this, wrapper = function () {\n
-    that.removeListener(event, wrapper);\n
-    listener.apply(that, arguments);\n
-  };\n
-  wrapper.original = listener;\n
-  return that.on(event, wrapper);\n
-};\n
+      context_wc = context_w + context_c;\n
+      if (Object.prototype.hasOwnProperty.call(context_dictionary,context_wc)) {\n
+        context_w = context_wc;\n
+      } else {\n
+        if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) {\n
+          if (context_w.charCodeAt(0)<256) {\n
+            for (i=0 ; i<context_numBits ; i++) {\n
+              context_data_val = (context_data_val << 1);\n
+              if (context_data_position == bitsPerChar-1) {\n
+                context_data_position = 0;\n
+                context_data.push(getCharFromInt(context_data_val));\n
+                context_data_val = 0;\n
+              } else {\n
+                context_data_position++;\n
+              }\n
+            }\n
+            value = context_w.charCodeAt(0);\n
+            for (i=0 ; i<8 ; i++) {\n
+              context_data_val = (context_data_val << 1) | (value&1);\n
+              if (context_data_position == bitsPerChar-1) {\n
+                context_data_position = 0;\n
+                context_data.push(getCharFromInt(context_data_val));\n
+                context_data_val = 0;\n
+              } else {\n
+                context_data_position++;\n
+              }\n
+              value = value >> 1;\n
+            }\n
+          } else {\n
+            value = 1;\n
+            for (i=0 ; i<context_numBits ; i++) {\n
+              context_data_val = (context_data_val << 1) | value;\n
+              if (context_data_position ==bitsPerChar-1) {\n
+                context_data_position = 0;\n
+                context_data.push(getCharFromInt(context_data_val));\n
+                context_data_val = 0;\n
+              } else {\n
+                context_data_position++;\n
+              }\n
+              value = 0;\n
+            }\n
+            value = context_w.charCodeAt(0);\n
+            for (i=0 ; i<16 ; i++) {\n
+              context_data_val = (context_data_val << 1) | (value&1);\n
+              if (context_data_position == bitsPerChar-1) {\n
+                context_data_position = 0;\n
+                context_data.push(getCharFromInt(context_data_val));\n
+                context_data_val = 0;\n
+              } else {\n
+                context_data_position++;\n
+              }\n
+              value = value >> 1;\n
+            }\n
+          }\n
+          context_enlargeIn--;\n
+          if (context_enlargeIn == 0) {\n
+            context_enlargeIn = Math.pow(2, context_numBits);\n
+            context_numBits++;\n
+          }\n
+          delete context_dictionaryToCreate[context_w];\n
+        } else {\n
+          value = context_dictionary[context_w];\n
+          for (i=0 ; i<context_numBits ; i++) {\n
+            context_data_val = (context_data_val << 1) | (value&1);\n
+            if (context_data_position == bitsPerChar-1) {\n
+              context_data_position = 0;\n
+              context_data.push(getCharFromInt(context_data_val));\n
+              context_data_val = 0;\n
+            } else {\n
+              context_data_position++;\n
+            }\n
+            value = value >> 1;\n
+          }\n
 \n
-/**\n
- * Remove a listener from the listener array for the specified event.\n
- * Caution: changes array indices in the listener array behind the listener\n
- *\n
- * @method removeListener\n
- * @param  {String} event The event name\n
- * @param  {Function} listener The listener callback\n
- * @return {EventEmitter} This emitter\n
- */\n
-EventEmitter.prototype.removeListener = function (event, listener) {\n
-  var listener_list = this._events[event], i;\n
-  if (listener_list) {\n
-    if (typeof listener_list === "function") {\n
-      if (listener_list === listener || listener_list.original === listener) {\n
-        delete this._events[event];\n
+\n
+        }\n
+        context_enlargeIn--;\n
+        if (context_enlargeIn == 0) {\n
+          context_enlargeIn = Math.pow(2, context_numBits);\n
+          context_numBits++;\n
+        }\n
+        // Add wc to the dictionary.\n
+        context_dictionary[context_wc] = context_dictSize++;\n
+        context_w = String(context_c);\n
       }\n
-      return this;\n
     }\n
-    for (i = 0; i < listener_list.length; i += 1) {\n
-      if (listener_list[i] === listener ||\n
-          listener_list[i].original === listener) {\n
-        listener_list.splice(i, 1);\n
-        this.emit("removeListener", event, listener);\n
-        break;\n
+\n
+    // Output the code for w.\n
+    if (context_w !== "") {\n
+      if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) {\n
+        if (context_w.charCodeAt(0)<256) {\n
+          for (i=0 ; i<context_numBits ; i++) {\n
+            context_data_val = (context_data_val << 1);\n
+            if (context_data_position == bitsPerChar-1) {\n
+              context_data_position = 0;\n
+              context_data.push(getCharFromInt(context_data_val));\n
+              context_data_val = 0;\n
+            } else {\n
+              context_data_position++;\n
+            }\n
+          }\n
+          value = context_w.charCodeAt(0);\n
+          for (i=0 ; i<8 ; i++) {\n
+            context_data_val = (context_data_val << 1) | (value&1);\n
+            if (context_data_position == bitsPerChar-1) {\n
+              context_data_position = 0;\n
+              context_data.push(getCharFromInt(context_data_val));\n
+              context_data_val = 0;\n
+            } else {\n
+              context_data_position++;\n
+            }\n
+            value = value >> 1;\n
+          }\n
+        } else {\n
+          value = 1;\n
+          for (i=0 ; i<context_numBits ; i++) {\n
+            context_data_val = (context_data_val << 1) | value;\n
+            if (context_data_position == bitsPerChar-1) {\n
+              context_data_position = 0;\n
+              context_data.push(getCharFromInt(context_data_val));\n
+              context_data_val = 0;\n
+            } else {\n
+              context_data_position++;\n
+            }\n
+            value = 0;\n
+          }\n
+          value = context_w.charCodeAt(0);\n
+          for (i=0 ; i<16 ; i++) {\n
+            context_data_val = (context_data_val << 1) | (value&1);\n
+            if (context_data_position == bitsPerChar-1) {\n
+              context_data_position = 0;\n
+              context_data.push(getCharFromInt(context_data_val));\n
+              context_data_val = 0;\n
+            } else {\n
+              context_data_position++;\n
+            }\n
+            value = value >> 1;\n
+          }\n
+        }\n
+        context_enlargeIn--;\n
+        if (context_enlargeIn == 0) {\n
+          context_enlargeIn = Math.pow(2, context_numBits);\n
+          context_numBits++;\n
+        }\n
+        delete context_dictionaryToCreate[context_w];\n
+      } else {\n
+        value = context_dictionary[context_w];\n
+        for (i=0 ; i<context_numBits ; i++) {\n
+          context_data_val = (context_data_val << 1) | (value&1);\n
+          if (context_data_position == bitsPerChar-1) {\n
+            context_data_position = 0;\n
+            context_data.push(getCharFromInt(context_data_val));\n
+            context_data_val = 0;\n
+          } else {\n
+            context_data_position++;\n
+          }\n
+          value = value >> 1;\n
+        }\n
+\n
+\n
+      }\n
+      context_enlargeIn--;\n
+      if (context_enlargeIn == 0) {\n
+        context_enlargeIn = Math.pow(2, context_numBits);\n
+        context_numBits++;\n
       }\n
     }\n
-    if (listener_list.length === 1) {\n
-      this._events[event] = listener_list[0];\n
-    }\n
-    if (listener_list.length === 0) {\n
-      this._events[event] = undefined;\n
-    }\n
-  }\n
-  return this;\n
-};\n
 \n
-/**\n
- * Removes all listeners, or those of the specified event.\n
- *\n
- * @method removeAllListeners\n
- * @param  {String} event The event name (optional)\n
- * @return {EventEmitter} This emitter\n
- */\n
-EventEmitter.prototype.removeAllListeners = function (event) {\n
-  var key;\n
-  if (event === undefined) {\n
-    for (key in this._events) {\n
-      if (this._events.hasOwnProperty(key)) {\n
-        delete this._events[key];\n
+    // Mark the end of the stream\n
+    value = 2;\n
+    for (i=0 ; i<context_numBits ; i++) {\n
+      context_data_val = (context_data_val << 1) | (value&1);\n
+      if (context_data_position == bitsPerChar-1) {\n
+        context_data_position = 0;\n
+        context_data.push(getCharFromInt(context_data_val));\n
+        context_data_val = 0;\n
+      } else {\n
+        context_data_position++;\n
       }\n
+      value = value >> 1;\n
     }\n
-    return this;\n
-  }\n
-  delete this._events[event];\n
-  return this;\n
-};\n
-\n
-/**\n
- * By default EventEmitters will print a warning if more than 10 listeners\n
- * are added for a particular event. This is a useful default which helps\n
- * finding memory leaks. Obviously not all Emitters should be limited to 10.\n
- * This function allows that to be increased. Set to zero for unlimited.\n
- *\n
- * @method setMaxListeners\n
- * @param  {Number} max_listeners The maximum of listeners\n
- */\n
-EventEmitter.prototype.setMaxListeners = function (max_listeners) {\n
-  this._maxListeners = max_listeners;\n
-};\n
 \n
-/**\n
- * Execute each of the listeners in order with the supplied arguments.\n
- *\n
- * @method emit\n
- * @param  {String} event The event name\n
- * @param  {Any} [args]* The listener argument to give\n
- * @return {Boolean} true if event had listeners, false otherwise.\n
- */\n
-EventEmitter.prototype.emit = function (event) {\n
-  var i, argument_list, listener_list;\n
-  listener_list = this._events[event];\n
-  if (typeof listener_list === \'function\') {\n
-    listener_list = [listener_list];\n
-  } else if (Array.isArray(listener_list)) {\n
-    listener_list = listener_list.slice();\n
-  } else {\n
-    return false;\n
-  }\n
-  argument_list = Array.prototype.slice.call(arguments, 1);\n
-  for (i = 0; i < listener_list.length; i += 1) {\n
-    try {\n
-      listener_list[i].apply(this, argument_list);\n
-    } catch (e) {\n
-      if (this.listeners("error").length > 0) {\n
-        this.emit("error", e);\n
+    // Flush the last char\n
+    while (true) {\n
+      context_data_val = (context_data_val << 1);\n
+      if (context_data_position == bitsPerChar-1) {\n
+        context_data.push(getCharFromInt(context_data_val));\n
         break;\n
       }\n
-      throw e;\n
-    }\n
-  }\n
-  return true;\n
-};\n
+      else context_data_position++;\n
+    }\n
+    return context_data.join(\'\');\n
+  },\n
+\n
+  decompress: function (compressed) {\n
+    if (compressed == null) return "";\n
+    if (compressed == "") return null;\n
+    return LZString._decompress(compressed.length, 32768, function(index) { return compressed.charCodeAt(index); });\n
+  },\n
+\n
+  _decompress: function (length, resetValue, getNextValue) {\n
+    var dictionary = [],\n
+        next,\n
+        enlargeIn = 4,\n
+        dictSize = 4,\n
+        numBits = 3,\n
+        entry = "",\n
+        result = [],\n
+        i,\n
+        w,\n
+        bits, resb, maxpower, power,\n
+        c,\n
+        data = {val:getNextValue(0), position:resetValue, index:1};\n
+\n
+    for (i = 0; i < 3; i += 1) {\n
+      dictionary[i] = i;\n
+    }\n
+\n
+    bits = 0;\n
+    maxpower = Math.pow(2,2);\n
+    power=1;\n
+    while (power!=maxpower) {\n
+      resb = data.val & data.position;\n
+      data.position >>= 1;\n
+      if (data.position == 0) {\n
+        data.position = resetValue;\n
+        data.val = getNextValue(data.index++);\n
+      }\n
+      bits |= (resb>0 ? 1 : 0) * power;\n
+      power <<= 1;\n
+    }\n
+\n
+    switch (next = bits) {\n
+      case 0:\n
+          bits = 0;\n
+          maxpower = Math.pow(2,8);\n
+          power=1;\n
+          while (power!=maxpower) {\n
+            resb = data.val & data.position;\n
+            data.position >>= 1;\n
+            if (data.position == 0) {\n
+              data.position = resetValue;\n
+              data.val = getNextValue(data.index++);\n
+            }\n
+            bits |= (resb>0 ? 1 : 0) * power;\n
+            power <<= 1;\n
+          }\n
+        c = f(bits);\n
+        break;\n
+      case 1:\n
+          bits = 0;\n
+          maxpower = Math.pow(2,16);\n
+          power=1;\n
+          while (power!=maxpower) {\n
+            resb = data.val & data.position;\n
+            data.position >>= 1;\n
+            if (data.position == 0) {\n
+              data.position = resetValue;\n
+              data.val = getNextValue(data.index++);\n
+            }\n
+            bits |= (resb>0 ? 1 : 0) * power;\n
+            power <<= 1;\n
+          }\n
+        c = f(bits);\n
+        break;\n
+      case 2:\n
+        return "";\n
+    }\n
+    dictionary[3] = c;\n
+    w = c;\n
+    result.push(c);\n
+    while (true) {\n
+      if (data.index > length) {\n
+        return "";\n
+      }\n
 \n
-/**\n
- * Returns an array of listeners for the specified event.\n
- *\n
- * @method listeners\n
- * @param  {String} event The event name\n
- * @return {Array} The array of listeners\n
- */\n
-EventEmitter.prototype.listeners = function (event) {\n
-  return (typeof this._events[event] === \'function\' ?\n
-          [this._events[event]] : (this._events[event] || []).slice());\n
-};\n
+      bits = 0;\n
+      maxpower = Math.pow(2,numBits);\n
+      power=1;\n
+      while (power!=maxpower) {\n
+        resb = data.val & data.position;\n
+        data.position >>= 1;\n
+        if (data.position == 0) {\n
+          data.position = resetValue;\n
+          data.val = getNextValue(data.index++);\n
+        }\n
+        bits |= (resb>0 ? 1 : 0) * power;\n
+        power <<= 1;\n
+      }\n
 \n
-/**\n
- * Static method; Return the number of listeners for a given event.\n
- *\n
- * @method listenerCount\n
- * @static\n
- * @param  {EventEmitter} emitter The event emitter\n
- * @param  {String} event The event name\n
- * @return {Number} The number of listener\n
- */\n
-EventEmitter.listenerCount = function (emitter, event) {\n
-  return emitter.listeners(event).length;\n
-};\n
+      switch (c = bits) {\n
+        case 0:\n
+          bits = 0;\n
+          maxpower = Math.pow(2,8);\n
+          power=1;\n
+          while (power!=maxpower) {\n
+            resb = data.val & data.position;\n
+            data.position >>= 1;\n
+            if (data.position == 0) {\n
+              data.position = resetValue;\n
+              data.val = getNextValue(data.index++);\n
+            }\n
+            bits |= (resb>0 ? 1 : 0) * power;\n
+            power <<= 1;\n
+          }\n
 \n
-exports.EventEmitter = EventEmitter;\n
+          dictionary[dictSize++] = f(bits);\n
+          c = dictSize-1;\n
+          enlargeIn--;\n
+          break;\n
+        case 1:\n
+          bits = 0;\n
+          maxpower = Math.pow(2,16);\n
+          power=1;\n
+          while (power!=maxpower) {\n
+            resb = data.val & data.position;\n
+            data.position >>= 1;\n
+            if (data.position == 0) {\n
+              data.position = resetValue;\n
+              data.val = getNextValue(data.index++);\n
+            }\n
+            bits |= (resb>0 ? 1 : 0) * power;\n
+            power <<= 1;\n
+          }\n
+          dictionary[dictSize++] = f(bits);\n
+          c = dictSize-1;\n
+          enlargeIn--;\n
+          break;\n
+        case 2:\n
+          return result.join(\'\');\n
+      }\n
 \n
-/*jslint indent: 2, maxlen: 80, nomen: true, sloppy: true */\n
-/*global EventEmitter, deepClone, inherits, exports */\n
-/*global enableRestAPI, enableRestParamChecker, enableJobMaker, enableJobRetry,\n
-  enableJobReference, enableJobChecker, enableJobQueue, enableJobRecovery,\n
-  enableJobTimeout, enableJobExecuter */\n
+      if (enlargeIn == 0) {\n
+        enlargeIn = Math.pow(2, numBits);\n
+        numBits++;\n
+      }\n
 \n
-function JIO(storage_spec, options) {\n
-  JIO.super_.call(this);\n
-  var shared = new EventEmitter();\n
+      if (dictionary[c]) {\n
+        entry = dictionary[c];\n
+      } else {\n
+        if (c === dictSize) {\n
+          entry = w + w.charAt(0);\n
+        } else {\n
+          return null;\n
+        }\n
+      }\n
+      result.push(entry);\n
 \n
-  shared.storage_spec = deepClone(storage_spec);\n
+      // Add w+entry[0] to the dictionary.\n
+      dictionary[dictSize++] = w + entry.charAt(0);\n
+      enlargeIn--;\n
 \n
-  if (options === undefined) {\n
-    options = {};\n
-  } else if (typeof options !== \'object\' || Array.isArray(options)) {\n
-    throw new TypeError("JIO(): Optional argument 2 is not of type \'object\'");\n
-  }\n
+      w = entry;\n
 \n
-  enableRestAPI(this, shared, options);\n
-  enableRestParamChecker(this, shared, options);\n
-  enableJobMaker(this, shared, options);\n
-  enableJobReference(this, shared, options);\n
-  enableJobRetry(this, shared, options);\n
-  enableJobTimeout(this, shared, options);\n
-  enableJobChecker(this, shared, options);\n
-  enableJobQueue(this, shared, options);\n
-  enableJobRecovery(this, shared, options);\n
-  enableJobExecuter(this, shared, options);\n
-\n
-  shared.emit(\'load\');\n
-}\n
-inherits(JIO, EventEmitter);\n
+      if (enlargeIn == 0) {\n
+        enlargeIn = Math.pow(2, numBits);\n
+        numBits++;\n
+      }\n
 \n
-JIO.createInstance = function (storage_spec, options) {\n
-  return new JIO(storage_spec, options);\n
+    }\n
+  }\n
 };\n
+  return LZString;\n
+})();\n
 \n
-exports.JIO = JIO;\n
+if (typeof define === \'function\' && define.amd) {\n
+  define(function () { return LZString; });\n
+} else if( typeof module !== \'undefined\' && module != null ) {\n
+  module.exports = LZString\n
+}\n
+;//! moment.js\n
+//! version : 2.5.0\n
+//! authors : Tim Wood, Iskren Chernev, Moment.js contributors\n
+//! license : MIT\n
+//! momentjs.com\n
+\n
+(function (undefined) {\n
+\n
+    /************************************\n
+        Constants\n
+    ************************************/\n
+\n
+    var moment,\n
+        VERSION = "2.5.0",\n
+        global = this,\n
+        round = Math.round,\n
+        i,\n
+\n
+        YEAR = 0,\n
+        MONTH = 1,\n
+        DATE = 2,\n
+        HOUR = 3,\n
+        MINUTE = 4,\n
+        SECOND = 5,\n
+        MILLISECOND = 6,\n
+\n
+        // internal storage for language config files\n
+        languages = {},\n
+\n
+        // check for nodeJS\n
+        hasModule = (typeof module !== \'undefined\' && module.exports && typeof require !== \'undefined\'),\n
+\n
+        // ASP.NET json date format regex\n
+        aspNetJsonRegex = /^\\/?Date\\((\\-?\\d+)/i,\n
+        aspNetTimeSpanJsonRegex = /(\\-)?(?:(\\d*)\\.)?(\\d+)\\:(\\d+)(?:\\:(\\d+)\\.?(\\d{3})?)?/,\n
+\n
+        // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html\n
+        // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere\n
+        isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,\n
+\n
+        // format tokens\n
+        formattingTokens = /(\\[[^\\[]*\\])|(\\\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g,\n
+        localFormattingTokens = /(\\[[^\\[]*\\])|(\\\\)?(LT|LL?L?L?|l{1,4})/g,\n
+\n
+        // parsing token regexes\n
+        parseTokenOneOrTwoDigits = /\\d\\d?/, // 0 - 99\n
+        parseTokenOneToThreeDigits = /\\d{1,3}/, // 0 - 999\n
+        parseTokenOneToFourDigits = /\\d{1,4}/, // 0 - 9999\n
+        parseTokenOneToSixDigits = /[+\\-]?\\d{1,6}/, // -999,999 - 999,999\n
+        parseTokenDigits = /\\d+/, // nonzero number of digits\n
+        parseTokenWord = /[0-9]*[\'a-z\\u00A0-\\u05FF\\u0700-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]+|[\\u0600-\\u06FF\\/]+(\\s*?[\\u0600-\\u06FF]+){1,2}/i, // any word (or two) characters or numbers including two/three word month in arabic.\n
+        parseTokenTimezone = /Z|[\\+\\-]\\d\\d:?\\d\\d/gi, // +00:00 -00:00 +0000 -0000 or Z\n
+        parseTokenT = /T/i, // T (ISO separator)\n
+        parseTokenTimestampMs = /[\\+\\-]?\\d+(\\.\\d{1,3})?/, // 123456789 123456789.123\n
+\n
+        //strict parsing regexes\n
+        parseTokenOneDigit = /\\d/, // 0 - 9\n
+        parseTokenTwoDigits = /\\d\\d/, // 00 - 99\n
+        parseTokenThreeDigits = /\\d{3}/, // 000 - 999\n
+        parseTokenFourDigits = /\\d{4}/, // 0000 - 9999\n
+        parseTokenSixDigits = /[+\\-]?\\d{6}/, // -999,999 - 999,999\n
+\n
+        // iso 8601 regex\n
+        // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)\n
+        isoRegex = /^\\s*\\d{4}-(?:(\\d\\d-\\d\\d)|(W\\d\\d$)|(W\\d\\d-\\d)|(\\d\\d\\d))((T| )(\\d\\d(:\\d\\d(:\\d\\d(\\.\\d+)?)?)?)?([\\+\\-]\\d\\d(?::?\\d\\d)?|\\s*Z)?)?$/,\n
+\n
+        isoFormat = \'YYYY-MM-DDTHH:mm:ssZ\',\n
+\n
+        isoDates = [\n
+            \'YYYY-MM-DD\',\n
+            \'GGGG-[W]WW\',\n
+            \'GGGG-[W]WW-E\',\n
+            \'YYYY-DDD\'\n
+        ],\n
+\n
+        // iso time formats and regexes\n
+        isoTimes = [\n
+            [\'HH:mm:ss.SSSS\', /(T| )\\d\\d:\\d\\d:\\d\\d\\.\\d{1,3}/],\n
+            [\'HH:mm:ss\', /(T| )\\d\\d:\\d\\d:\\d\\d/],\n
+            [\'HH:mm\', /(T| )\\d\\d:\\d\\d/],\n
+            [\'HH\', /(T| )\\d\\d/]\n
+        ],\n
+\n
+        // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"]\n
+        parseTimezoneChunker = /([\\+\\-]|\\d\\d)/gi,\n
+\n
+        // getter and setter names\n
+        proxyGettersAndSetters = \'Date|Hours|Minutes|Seconds|Milliseconds\'.split(\'|\'),\n
+        unitMillisecondFactors = {\n
+            \'Milliseconds\' : 1,\n
+            \'Seconds\' : 1e3,\n
+            \'Minutes\' : 6e4,\n
+            \'Hours\' : 36e5,\n
+            \'Days\' : 864e5,\n
+            \'Months\' : 2592e6,\n
+            \'Years\' : 31536e6\n
+        },\n
+\n
+        unitAliases = {\n
+            ms : \'millisecond\',\n
+            s : \'second\',\n
+            m : \'minute\',\n
+            h : \'hour\',\n
+            d : \'day\',\n
+            D : \'date\',\n
+            w : \'week\',\n
+            W : \'isoWeek\',\n
+            M : \'month\',\n
+            y : \'year\',\n
+            DDD : \'dayOfYear\',\n
+            e : \'weekday\',\n
+            E : \'isoWeekday\',\n
+            gg: \'weekYear\',\n
+            GG: \'isoWeekYear\'\n
+        },\n
+\n
+        camelFunctions = {\n
+            dayofyear : \'dayOfYear\',\n
+            isoweekday : \'isoWeekday\',\n
+            isoweek : \'isoWeek\',\n
+            weekyear : \'weekYear\',\n
+            isoweekyear : \'isoWeekYear\'\n
+        },\n
+\n
+        // format function strings\n
+        formatFunctions = {},\n
+\n
+        // tokens to ordinalize and pad\n
+        ordinalizeTokens = \'DDD w W M D d\'.split(\' \'),\n
+        paddedTokens = \'M D H h m s w W\'.split(\' \'),\n
+\n
+        formatTokenFunctions = {\n
+            M    : function () {\n
+                return this.month() + 1;\n
+            },\n
+            MMM  : function (format) {\n
+                return this.lang().monthsShort(this, format);\n
+            },\n
+            MMMM : function (format) {\n
+                return this.lang().months(this, format);\n
+            },\n
+            D    : function () {\n
+                return this.date();\n
+            },\n
+            DDD  : function () {\n
+                return this.dayOfYear();\n
+            },\n
+            d    : function () {\n
+                return this.day();\n
+            },\n
+            dd   : function (format) {\n
+                return this.lang().weekdaysMin(this, format);\n
+            },\n
+            ddd  : function (format) {\n
+                return this.lang().weekdaysShort(this, format);\n
+            },\n
+            dddd : function (format) {\n
+                return this.lang().weekdays(this, format);\n
+            },\n
+            w    : function () {\n
+                return this.week();\n
+            },\n
+            W    : function () {\n
+                return this.isoWeek();\n
+            },\n
+            YY   : function () {\n
+                return leftZeroFill(this.year() % 100, 2);\n
+            },\n
+            YYYY : function () {\n
+                return leftZeroFill(this.year(), 4);\n
+            },\n
+            YYYYY : function () {\n
+                return leftZeroFill(this.year(), 5);\n
+            },\n
+            YYYYYY : function () {\n
+                var y = this.year(), sign = y >= 0 ? \'+\' : \'-\';\n
+                return sign + leftZeroFill(Math.abs(y), 6);\n
+            },\n
+            gg   : function () {\n
+                return leftZeroFill(this.weekYear() % 100, 2);\n
+            },\n
+            gggg : function () {\n
+                return this.weekYear();\n
+            },\n
+            ggggg : function () {\n
+                return leftZeroFill(this.weekYear(), 5);\n
+            },\n
+            GG   : function () {\n
+                return leftZeroFill(this.isoWeekYear() % 100, 2);\n
+            },\n
+            GGGG : function () {\n
+                return this.isoWeekYear();\n
+            },\n
+            GGGGG : function () {\n
+                return leftZeroFill(this.isoWeekYear(), 5);\n
+            },\n
+            e : function () {\n
+                return this.weekday();\n
+            },\n
+            E : function () {\n
+                return this.isoWeekday();\n
+            },\n
+            a    : function () {\n
+                return this.lang().meridiem(this.hours(), this.minutes(), true);\n
+            },\n
+            A    : function () {\n
+                return this.lang().meridiem(this.hours(), this.minutes(), false);\n
+            },\n
+            H    : function () {\n
+                return this.hours();\n
+            },\n
+            h    : function () {\n
+                return this.hours() % 12 || 12;\n
+            },\n
+            m    : function () {\n
+                return this.minutes();\n
+            },\n
+            s    : function () {\n
+                return this.seconds();\n
+            },\n
+            S    : function () {\n
+                return toInt(this.milliseconds() / 100);\n
+            },\n
+            SS   : function () {\n
+                return leftZeroFill(toInt(this.milliseconds() / 10), 2);\n
+            },\n
+            SSS  : function () {\n
+                return leftZeroFill(this.milliseconds(), 3);\n
+            },\n
+            SSSS : function () {\n
+                return leftZeroFill(this.milliseconds(), 3);\n
+            },\n
+            Z    : function () {\n
+                var a = -this.zone(),\n
+                    b = "+";\n
+                if (a < 0) {\n
+                    a = -a;\n
+                    b = "-";\n
+                }\n
+                return b + leftZeroFill(toInt(a / 60), 2) + ":" + leftZeroFill(toInt(a) % 60, 2);\n
+            },\n
+            ZZ   : function () {\n
+                var a = -this.zone(),\n
+                    b = "+";\n
+                if (a < 0) {\n
+                    a = -a;\n
+                    b = "-";\n
+                }\n
+                return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2);\n
+            },\n
+            z : function () {\n
+                return this.zoneAbbr();\n
+            },\n
+            zz : function () {\n
+                return this.zoneName();\n
+            },\n
+            X    : function () {\n
+                return this.unix();\n
+            },\n
+            Q : function () {\n
+                return this.quarter();\n
+            }\n
+        },\n
 \n
-exports.createJIO = JIO.createInstance;\n
+        lists = [\'months\', \'monthsShort\', \'weekdays\', \'weekdaysShort\', \'weekdaysMin\'];\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */\n
-/*global deepClone, dictFilter, uniqueJSONStringify */\n
+    function padToken(func, count) {\n
+        return function (a) {\n
+            return leftZeroFill(func.call(this, a), count);\n
+        };\n
+    }\n
+    function ordinalizeToken(func, period) {\n
+        return function (a) {\n
+            return this.lang().ordinal(func.call(this, a), period);\n
+        };\n
+    }\n
 \n
-/**\n
- * Tool to manipulate a list of object containing at least one property: \'id\'.\n
- * Id must be a number > 0.\n
- *\n
- * @class JobQueue\n
- * @constructor\n
- * @param  {Workspace} workspace The workspace where to store\n
- * @param  {String} namespace The namespace to use in the workspace\n
- * @param  {Array} job_keys An array of job keys to store\n
- * @param  {Array} [array] An array of object\n
- */\n
-function JobQueue(workspace, namespace, job_keys, array) {\n
-  this._workspace = workspace;\n
-  this._namespace = namespace;\n
-  this._job_keys = job_keys;\n
-  if (Array.isArray(array)) {\n
-    this._array = array;\n
-  } else {\n
-    this._array = [];\n
-  }\n
-}\n
+    while (ordinalizeTokens.length) {\n
+        i = ordinalizeTokens.pop();\n
+        formatTokenFunctions[i + \'o\'] = ordinalizeToken(formatTokenFunctions[i], i);\n
+    }\n
+    while (paddedTokens.length) {\n
+        i = paddedTokens.pop();\n
+        formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2);\n
+    }\n
+    formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3);\n
 \n
-/**\n
- * Store the job queue into the workspace.\n
- *\n
- * @method save\n
- */\n
-JobQueue.prototype.save = function () {\n
-  var i, job_queue = deepClone(this._array);\n
-  for (i = 0; i < job_queue.length; i += 1) {\n
-    dictFilter(job_queue[i], this._job_keys);\n
-  }\n
-  if (this._array.length === 0) {\n
-    this._workspace.removeItem(this._namespace);\n
-  } else {\n
-    this._workspace.setItem(\n
-      this._namespace,\n
-      uniqueJSONStringify(job_queue)\n
-    );\n
-  }\n
-  return this;\n
-};\n
 \n
-/**\n
- * Loads the job queue from the workspace.\n
- *\n
- * @method load\n
- */\n
-JobQueue.prototype.load = function () {\n
-  var job_list;\n
-  try {\n
-    job_list = JSON.parse(this._workspace.getItem(this._namespace));\n
-  } catch (ignore) {}\n
-  if (!Array.isArray(job_list)) {\n
-    job_list = [];\n
-  }\n
-  this.clear();\n
-  new JobQueue(job_list).repair();\n
-  this.update(job_list);\n
-  return this;\n
-};\n
+    /************************************\n
+        Constructors\n
+    ************************************/\n
 \n
-/**\n
- * Returns the array version of the job queue\n
- *\n
- * @method asArray\n
- * @return {Array} The job queue as array\n
- */\n
-JobQueue.prototype.asArray = function () {\n
-  return this._array;\n
-};\n
+    function Language() {\n
 \n
-/**\n
- * Removes elements which are not objects containing at least \'id\' property.\n
- *\n
- * @method repair\n
- */\n
-JobQueue.prototype.repair = function () {\n
-  var i, job;\n
-  for (i = 0; i < this._array.length; i += 1) {\n
-    job = this._array[i];\n
-    if (typeof job !== \'object\' || Array.isArray(job) ||\n
-        typeof job.id !== \'number\' || job.id <= 0) {\n
-      this._array.splice(i, 1);\n
-      i -= 1;\n
     }\n
-  }\n
-};\n
 \n
-/**\n
- * Post an object and generate an id\n
- *\n
- * @method post\n
- * @param  {Object} job The job object\n
- * @return {Number} The generated id\n
- */\n
-JobQueue.prototype.post = function (job) {\n
-  var i, next = 1;\n
-  // get next id\n
-  for (i = 0; i < this._array.length; i += 1) {\n
-    if (this._array[i].id >= next) {\n
-      next = this._array[i].id + 1;\n
+    // Moment prototype object\n
+    function Moment(config) {\n
+        checkOverflow(config);\n
+        extend(this, config);\n
     }\n
-  }\n
-  job.id = next;\n
-  this._array[this._array.length] = deepClone(job);\n
-  return this;\n
-};\n
 \n
-/**\n
- * Put an object to the list. If an object contains the same id, it is replaced\n
- * by the new one.\n
- *\n
- * @method put\n
- * @param  {Object} job The job object with an id\n
- */\n
-JobQueue.prototype.put = function (job) {\n
-  var i;\n
-  if (typeof job.id !== \'number\' || job.id <= 0) {\n
-    throw new TypeError("JobQueue().put(): Job id should be a positive number");\n
-  }\n
-  for (i = 0; i < this._array.length; i += 1) {\n
-    if (this._array[i].id === job.id) {\n
-      break;\n
-    }\n
-  }\n
-  this._array[i] = deepClone(job);\n
-  return this;\n
-};\n
+    // Duration Constructor\n
+    function Duration(duration) {\n
+        var normalizedInput = normalizeObjectUnits(duration),\n
+            years = normalizedInput.year || 0,\n
+            months = normalizedInput.month || 0,\n
+            weeks = normalizedInput.week || 0,\n
+            days = normalizedInput.day || 0,\n
+            hours = normalizedInput.hour || 0,\n
+            minutes = normalizedInput.minute || 0,\n
+            seconds = normalizedInput.second || 0,\n
+            milliseconds = normalizedInput.millisecond || 0;\n
 \n
-/**\n
- * Puts some object into the list. Update object with the same id, and add\n
- * unreferenced one.\n
- *\n
- * @method update\n
- * @param  {Array} job_list A list of new jobs\n
- */\n
-JobQueue.prototype.update = function (job_list) {\n
-  var i, j = 0, j_max, index = {}, next = 1, job, post_list = [];\n
-  j_max = this._array.length;\n
-  for (i = 0; i < job_list.length; i += 1) {\n
-    if (typeof job_list[i].id !== \'number\' || job_list[i].id <= 0) {\n
-      // this job has no id, it has to be post\n
-      post_list[post_list.length] = job_list[i];\n
-    } else {\n
-      job = deepClone(job_list[i]);\n
-      if (index[job.id] !== undefined) {\n
-        // this job is on the list, update\n
-        this._array[index[job.id]] = job;\n
-      } else if (j === j_max) {\n
-        // this job is not on the list, update\n
-        this._array[this._array.length] = job;\n
-      } else {\n
-        // don\'t if the job is there or not\n
-        // searching same job in the original list\n
-        while (j < j_max) {\n
-          // references visited job\n
-          index[this._array[j].id] = j;\n
-          if (this._array[j].id >= next) {\n
-            next = this._array[j].id + 1;\n
-          }\n
-          if (this._array[j].id === job.id) {\n
-            // found on the list, just update\n
-            this._array[j] = job;\n
-            break;\n
-          }\n
-          j += 1;\n
-        }\n
-        if (j === j_max) {\n
-          // not found on the list, add to the end\n
-          this._array[this._array.length] = job;\n
-        } else {\n
-          // found on the list, already updated\n
-          j += 1;\n
-        }\n
-      }\n
-      if (job.id >= next) {\n
-        next = job.id + 1;\n
-      }\n
-    }\n
-  }\n
-  for (i = 0; i < post_list.length; i += 1) {\n
-    // adding job without id\n
-    post_list[i].id = next;\n
-    next += 1;\n
-    this._array[this._array.length] = deepClone(post_list[i]);\n
-  }\n
-  return this;\n
-};\n
+        // representation for dateAddRemove\n
+        this._milliseconds = +milliseconds +\n
+            seconds * 1e3 + // 1000\n
+            minutes * 6e4 + // 1000 * 60\n
+            hours * 36e5; // 1000 * 60 * 60\n
+        // Because of dateAddRemove treats 24 hours as different from a\n
+        // day when working around DST, we need to store them separately\n
+        this._days = +days +\n
+            weeks * 7;\n
+        // It is impossible translate months into days without knowing\n
+        // which months you are are talking about, so we have to store\n
+        // it separately.\n
+        this._months = +months +\n
+            years * 12;\n
 \n
-/**\n
- * Get an object from an id. Returns undefined if not found\n
- *\n
- * @method get\n
- * @param  {Number} id The job id\n
- * @return {Object} The job or undefined\n
- */\n
-JobQueue.prototype.get = function (id) {\n
-  var i;\n
-  for (i = 0; i < this._array.length; i += 1) {\n
-    if (this._array[i].id === id) {\n
-      return deepClone(this._array[i]);\n
-    }\n
-  }\n
-};\n
+        this._data = {};\n
 \n
-/**\n
- * Removes an object from an id\n
- *\n
- * @method remove\n
- * @param  {Number} id The job id\n
- */\n
-JobQueue.prototype.remove = function (id) {\n
-  var i;\n
-  for (i = 0; i < this._array.length; i += 1) {\n
-    if (this._array[i].id === id) {\n
-      this._array.splice(i, 1);\n
-      return true;\n
+        this._bubble();\n
     }\n
-  }\n
-  return false;\n
-};\n
-\n
-/**\n
- * Clears the list.\n
- *\n
- * @method clear\n
- */\n
-JobQueue.prototype.clear = function () {\n
-  this._array.length = 0;\n
-  return this;\n
-};\n
-\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true */\n
-/*global localStorage */\n
+    /************************************\n
+        Helpers\n
+    ************************************/\n
 \n
-// keywords: js, javascript, store on local storage as array\n
 \n
-function LocalStorageArray(namespace) {\n
-  var index, next;\n
+    function extend(a, b) {\n
+        for (var i in b) {\n
+            if (b.hasOwnProperty(i)) {\n
+                a[i] = b[i];\n
+            }\n
+        }\n
 \n
-  function nextId() {\n
-    var i = next;\n
-    next += 1;\n
-    return i;\n
-  }\n
+        if (b.hasOwnProperty("toString")) {\n
+            a.toString = b.toString;\n
+        }\n
 \n
-  this.length = function () {\n
-    return index.length;\n
-  };\n
+        if (b.hasOwnProperty("valueOf")) {\n
+            a.valueOf = b.valueOf;\n
+        }\n
 \n
-  this.truncate = function (length) {\n
-    var i;\n
-    if (length === index.length) {\n
-      return this;\n
-    }\n
-    if (length > index.length) {\n
-      index.length = length;\n
-      localStorage[namespace + \'.index\'] = JSON.stringify(index);\n
-      return this;\n
-    }\n
-    while (length < index.length) {\n
-      i = index.pop();\n
-      if (i !== undefined && i !== null) {\n
-        delete localStorage[namespace + \'.\' + i];\n
-      }\n
+        return a;\n
     }\n
-    localStorage[namespace + \'.index\'] = JSON.stringify(index);\n
-    return this;\n
-  };\n
-\n
-  this.get = function (i) {\n
-    return JSON.parse(localStorage[namespace + \'.\' + index[i]] || \'null\');\n
-  };\n
 \n
-  this.set = function (i, value) {\n
-    if (index[i] === undefined || index[i] === null) {\n
-      index[i] = nextId();\n
-      localStorage[namespace + \'.\' + index[i]] = JSON.stringify(value);\n
-      localStorage[namespace + \'.index\'] = JSON.stringify(index);\n
-    } else {\n
-      localStorage[namespace + \'.\' + index[i]] = JSON.stringify(value);\n
+    function absRound(number) {\n
+        if (number < 0) {\n
+            return Math.ceil(number);\n
+        } else {\n
+            return Math.floor(number);\n
+        }\n
     }\n
-    return this;\n
-  };\n
 \n
-  this.append = function (value) {\n
-    index[index.length] = nextId();\n
-    localStorage[namespace + \'.\' + index[index.length - 1]] =\n
-      JSON.stringify(value);\n
-    localStorage[namespace + \'.index\'] = JSON.stringify(index);\n
-    return this;\n
-  };\n
+    // left zero fill a number\n
+    // see http://jsperf.com/left-zero-filling for performance comparison\n
+    function leftZeroFill(number, targetLength, forceSign) {\n
+        var output = Math.abs(number) + \'\',\n
+            sign = number >= 0;\n
 \n
-  this.pop = function (i) {\n
-    var value, key;\n
-    if (i === undefined || i === null) {\n
-      key = namespace + \'.\' + index[index.length - 1];\n
-      index.pop();\n
-    } else {\n
-      if (i < 0 || i >= index.length) {\n
-        return null;\n
-      }\n
-      key = namespace + \'.\' + i;\n
-      index.splice(i, 1);\n
+        while (output.length < targetLength) {\n
+            output = \'0\' + output;\n
+        }\n
+        return (sign ? (forceSign ? \'+\' : \'\') : \'-\') + output;\n
     }\n
 \n
-    value = localStorage[key];\n
+    // helper function for _.addTime and _.subtractTime\n
+    function addOrSubtractDurationFromMoment(mom, duration, isAdding, ignoreUpdateOffset) {\n
+        var milliseconds = duration._milliseconds,\n
+            days = duration._days,\n
+            months = duration._months,\n
+            minutes,\n
+            hours;\n
 \n
-    if (index.length === 0) {\n
-      delete localStorage[namespace + \'.index\'];\n
-    } else {\n
-      localStorage[namespace + \'.index\'] = JSON.stringify(index);\n
+        if (milliseconds) {\n
+            mom._d.setTime(+mom._d + milliseconds * isAdding);\n
+        }\n
+        // store the minutes and hours so we can restore them\n
+        if (days || months) {\n
+            minutes = mom.minute();\n
+            hours = mom.hour();\n
+        }\n
+        if (days) {\n
+            mom.date(mom.date() + days * isAdding);\n
+        }\n
+        if (months) {\n
+            mom.month(mom.month() + months * isAdding);\n
+        }\n
+        if (milliseconds && !ignoreUpdateOffset) {\n
+            moment.updateOffset(mom);\n
+        }\n
+        // restore the minutes and hours after possibly changing dst\n
+        if (days || months) {\n
+            mom.minute(minutes);\n
+            mom.hour(hours);\n
+        }\n
     }\n
-    delete localStorage[key];\n
-\n
-    return JSON.parse(value || \'null\');\n
-  };\n
 \n
-  this.clear = function () {\n
-    var i;\n
-    for (i = 0; i < index.length; i += 1) {\n
-      delete localStorage[namespace + \'.\' + index[i]];\n
+    // check if is an array\n
+    function isArray(input) {\n
+        return Object.prototype.toString.call(input) === \'[object Array]\';\n
     }\n
-    index = [];\n
-    delete localStorage[namespace + \'.index\'];\n
-    return this;\n
-  };\n
 \n
-  this.reload = function () {\n
-    var i;\n
-    index = JSON.parse(localStorage[namespace + \'.index\'] || \'[]\');\n
-    next = 0;\n
-    for (i = 0; i < index.length; i += 1) {\n
-      if (next < index[i]) {\n
-        next = index[i];\n
-      }\n
+    function isDate(input) {\n
+        return  Object.prototype.toString.call(input) === \'[object Date]\' ||\n
+                input instanceof Date;\n
     }\n
-    return this;\n
-  };\n
 \n
-  this.toArray = function () {\n
-    var i, list = [];\n
-    for (i = 0; i < index.length; i += 1) {\n
-      list[list.length] = this.get(i);\n
+    // compare two arrays, return the number of differences\n
+    function compareArrays(array1, array2, dontConvert) {\n
+        var len = Math.min(array1.length, array2.length),\n
+            lengthDiff = Math.abs(array1.length - array2.length),\n
+            diffs = 0,\n
+            i;\n
+        for (i = 0; i < len; i++) {\n
+            if ((dontConvert && array1[i] !== array2[i]) ||\n
+                (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {\n
+                diffs++;\n
+            }\n
+        }\n
+        return diffs + lengthDiff;\n
     }\n
-    return list;\n
-  };\n
 \n
-  this.update = function (list) {\n
-    if (!Array.isArray(list)) {\n
-      throw new TypeError("LocalStorageArray().saveArray(): " +\n
-                          "Argument 1 is not of type \'array\'");\n
+    function normalizeUnits(units) {\n
+        if (units) {\n
+            var lowered = units.toLowerCase().replace(/(.)s$/, \'$1\');\n
+            units = unitAliases[units] || camelFunctions[lowered] || lowered;\n
+        }\n
+        return units;\n
     }\n
-    var i, location;\n
-    // update previous values\n
-    for (i = 0; i < list.length; i += 1) {\n
-      location = index[i];\n
-      if (location === undefined || location === null) {\n
-        location = nextId();\n
-        index[i] = location;\n
-      }\n
-      localStorage[namespace + \'.\' + location] =\n
-        JSON.stringify(list[i]);\n
-    }\n
-    // remove last ones\n
-    while (list.length < index.length) {\n
-      location = index.pop();\n
-      if (location !== undefined && location !== null) {\n
-        delete localStorage[namespace + \'.\' + location];\n
-      }\n
+\n
+    function normalizeObjectUnits(inputObject) {\n
+        var normalizedInput = {},\n
+            normalizedProp,\n
+            prop;\n
+\n
+        for (prop in inputObject) {\n
+            if (inputObject.hasOwnProperty(prop)) {\n
+                normalizedProp = normalizeUnits(prop);\n
+                if (normalizedProp) {\n
+                    normalizedInput[normalizedProp] = inputObject[prop];\n
+                }\n
+            }\n
+        }\n
+\n
+        return normalizedInput;\n
     }\n
-    // store index\n
-    localStorage[namespace + \'.index\'] = JSON.stringify(index);\n
-    return this;\n
-  };\n
 \n
-  this.reload();\n
-}\n
+    function makeList(field) {\n
+        var count, setter;\n
 \n
-LocalStorageArray.saveArray = function (namespace, list) {\n
-  if (!Array.isArray(list)) {\n
-    throw new TypeError("LocalStorageArray.saveArray(): " +\n
-                        "Argument 2 is not of type \'array\'");\n
-  }\n
-  var local_storage_array = new LocalStorageArray(namespace).clear(), i;\n
-  for (i = 0; i < list.length; i += 1) {\n
-    local_storage_array.append(list[i]);\n
-  }\n
-};\n
+        if (field.indexOf(\'week\') === 0) {\n
+            count = 7;\n
+            setter = \'day\';\n
+        }\n
+        else if (field.indexOf(\'month\') === 0) {\n
+            count = 12;\n
+            setter = \'month\';\n
+        }\n
+        else {\n
+            return;\n
+        }\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */\n
-/*global exports, deepClone, jsonDeepClone */\n
+        moment[field] = function (format, index) {\n
+            var i, getter,\n
+                method = moment.fn._lang[field],\n
+                results = [];\n
 \n
-/**\n
- * A class to manipulate metadata\n
- *\n
- * @class Metadata\n
- * @constructor\n
- */\n
-function Metadata(metadata) {\n
-  if (arguments.length > 0) {\n
-    if (metadata === null || typeof metadata !== \'object\' ||\n
-        Array.isArray(metadata)) {\n
-      throw new TypeError("Metadata(): Optional argument 1 is not an object");\n
-    }\n
-    this._dict = metadata;\n
-  } else {\n
-    this._dict = {};\n
-  }\n
-}\n
+            if (typeof format === \'number\') {\n
+                index = format;\n
+                format = undefined;\n
+            }\n
 \n
-Metadata.prototype.format = function () {\n
-  return this.update(this._dict);\n
-};\n
+            getter = function (i) {\n
+                var m = moment().utc().set(setter, i);\n
+                return method.call(moment.fn._lang, m, format || \'\');\n
+            };\n
+\n
+            if (index != null) {\n
+                return getter(index);\n
+            }\n
+            else {\n
+                for (i = 0; i < count; i++) {\n
+                    results.push(getter(i));\n
+                }\n
+                return results;\n
+            }\n
+        };\n
+    }\n
 \n
-Metadata.prototype.check = function () {\n
-  var k;\n
-  for (k in this._dict) {\n
-    if (this._dict.hasOwnProperty(k)) {\n
-      if (k[0] !== \'_\') {\n
-        if (!Metadata.checkValue(this._dict[k])) {\n
-          return false;\n
+    function toInt(argumentForCoercion) {\n
+        var coercedNumber = +argumentForCoercion,\n
+            value = 0;\n
+\n
+        if (coercedNumber !== 0 && isFinite(coercedNumber)) {\n
+            if (coercedNumber >= 0) {\n
+                value = Math.floor(coercedNumber);\n
+            } else {\n
+                value = Math.ceil(coercedNumber);\n
+            }\n
         }\n
-      }\n
+\n
+        return value;\n
     }\n
-  }\n
-  return true;\n
-};\n
 \n
-Metadata.prototype.update = function (metadata) {\n
-  var k;\n
-  for (k in metadata) {\n
-    if (metadata.hasOwnProperty(k)) {\n
-      if (k[0] === \'_\') {\n
-        this._dict[k] = jsonDeepClone(metadata[k]);\n
-      } else {\n
-        this._dict[k] = Metadata.normalizeValue(metadata[k]);\n
-      }\n
-      if (this._dict[k] === undefined) {\n
-        delete this._dict[k];\n
-      }\n
+    function daysInMonth(year, month) {\n
+        return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();\n
     }\n
-  }\n
-  return this;\n
-};\n
 \n
-Metadata.prototype.get = function (key) {\n
-  return this._dict[key];\n
-};\n
+    function daysInYear(year) {\n
+        return isLeapYear(year) ? 366 : 365;\n
+    }\n
 \n
-Metadata.prototype.add = function (key, value) {\n
-  var i;\n
-  if (key[0] === \'_\') {\n
-    return this;\n
-  }\n
-  if (this._dict[key] === undefined) {\n
-    this._dict[key] = Metadata.normalizeValue(value);\n
-    if (this._dict[key] === undefined) {\n
-      delete this._dict[key];\n
+    function isLeapYear(year) {\n
+        return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;\n
     }\n
-    return this;\n
-  }\n
-  if (!Array.isArray(this._dict[key])) {\n
-    this._dict[key] = [this._dict[key]];\n
-  }\n
-  value = Metadata.normalizeValue(value);\n
-  if (value === undefined) {\n
-    return this;\n
-  }\n
-  if (!Array.isArray(value)) {\n
-    value = [value];\n
-  }\n
-  for (i = 0; i < value.length; i += 1) {\n
-    this._dict[key][this._dict[key].length] = value[i];\n
-  }\n
-  return this;\n
-};\n
 \n
-Metadata.prototype.set = function (key, value) {\n
-  if (key[0] === \'_\') {\n
-    this._dict[key] = JSON.parse(JSON.stringify(value));\n
-  } else {\n
-    this._dict[key] = Metadata.normalizeValue(value);\n
-  }\n
-  if (this._dict[key] === undefined) {\n
-    delete this._dict[key];\n
-  }\n
-  return this;\n
-};\n
+    function checkOverflow(m) {\n
+        var overflow;\n
+        if (m._a && m._pf.overflow === -2) {\n
+            overflow =\n
+                m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH :\n
+                m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE :\n
+                m._a[HOUR] < 0 || m._a[HOUR] > 23 ? HOUR :\n
+                m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE :\n
+                m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND :\n
+                m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND :\n
+                -1;\n
 \n
-Metadata.prototype.remove = function (key) {\n
-  delete this._dict[key];\n
-  return this;\n
-};\n
+            if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {\n
+                overflow = DATE;\n
+            }\n
 \n
+            m._pf.overflow = overflow;\n
+        }\n
+    }\n
 \n
-Metadata.prototype.forEach = function (key, fun) {\n
-  var k, i, value, that = this;\n
-  if (typeof key === \'function\') {\n
-    fun = key;\n
-    key = undefined;\n
-  }\n
-  function forEach(key, fun) {\n
-    value = that._dict[key];\n
-    if (!Array.isArray(that._dict[key])) {\n
-      value = [value];\n
+    function initializeParsingFlags(config) {\n
+        config._pf = {\n
+            empty : false,\n
+            unusedTokens : [],\n
+            unusedInput : [],\n
+            overflow : -2,\n
+            charsLeftOver : 0,\n
+            nullInput : false,\n
+            invalidMonth : null,\n
+            invalidFormat : false,\n
+            userInvalidated : false,\n
+            iso: false\n
+        };\n
+    }\n
+\n
+    function isValid(m) {\n
+        if (m._isValid == null) {\n
+            m._isValid = !isNaN(m._d.getTime()) &&\n
+                m._pf.overflow < 0 &&\n
+                !m._pf.empty &&\n
+                !m._pf.invalidMonth &&\n
+                !m._pf.nullInput &&\n
+                !m._pf.invalidFormat &&\n
+                !m._pf.userInvalidated;\n
+\n
+            if (m._strict) {\n
+                m._isValid = m._isValid &&\n
+                    m._pf.charsLeftOver === 0 &&\n
+                    m._pf.unusedTokens.length === 0;\n
+            }\n
+        }\n
+        return m._isValid;\n
     }\n
-    for (i = 0; i < value.length; i += 1) {\n
-      if (typeof value[i] === \'object\') {\n
-        fun.call(that, key, deepClone(value[i]), i);\n
-      } else {\n
-        fun.call(that, key, {\'content\': value[i]}, i);\n
-      }\n
+\n
+    function normalizeLanguage(key) {\n
+        return key ? key.toLowerCase().replace(\'_\', \'-\') : key;\n
     }\n
-  }\n
-  if (key === undefined) {\n
-    for (k in this._dict) {\n
-      if (this._dict.hasOwnProperty(k)) {\n
-        forEach(k, fun);\n
-      }\n
+\n
+    // Return a moment from input, that is local/utc/zone equivalent to model.\n
+    function makeAs(input, model) {\n
+        return model._isUTC ? moment(input).zone(model._offset || 0) :\n
+            moment(input).local();\n
     }\n
-  } else {\n
-    forEach(key, fun);\n
-  }\n
-  return this;\n
-};\n
 \n
-Metadata.prototype.toFullDict = function () {\n
-  var dict = {};\n
-  this.forEach(function (key, value, index) {\n
-    dict[key] = dict[key] || [];\n
-    dict[key][index] = value;\n
-  });\n
-  return dict;\n
-};\n
+    /************************************\n
+        Languages\n
+    ************************************/\n
 \n
-Metadata.asJsonableValue = function (value) {\n
-  switch (typeof value) {\n
-  case \'string\':\n
-  case \'boolean\':\n
-    return value;\n
-  case \'number\':\n
-    if (isFinite(value)) {\n
-      return value;\n
-    }\n
-    return null;\n
-  case \'object\':\n
-    if (value === null) {\n
-      return null;\n
-    }\n
-    if (value instanceof Date) {\n
-      // XXX this block is to enable phantomjs and browsers compatibility with\n
-      // Date.prototype.toJSON when it is a invalid date. In phantomjs, it\n
-      // returns `"Invalid Date"` but in browsers it returns `null`. Here, the\n
-      // result will always be `null`.\n
-      if (isNaN(value.getTime())) {\n
-        return null;\n
-      }\n
-    }\n
-    if (typeof value.toJSON === \'function\') {\n
-      return Metadata.asJsonableValue(value.toJSON());\n
-    }\n
-    return value; // dict, array\n
-  // case \'undefined\':\n
-  default:\n
-    return null;\n
-  }\n
-};\n
 \n
-Metadata.isDict = function (o) {\n
-  return typeof o === \'object\' &&\n
-    Object.getPrototypeOf(o || []) === Object.prototype;\n
-};\n
+    extend(Language.prototype, {\n
 \n
-Metadata.isContent = function (c) {\n
-  return typeof c === \'string\' ||\n
-    (typeof c === \'number\' && isFinite(c)) ||\n
-    typeof c === \'boolean\';\n
-};\n
+        set : function (config) {\n
+            var prop, i;\n
+            for (i in config) {\n
+                prop = config[i];\n
+                if (typeof prop === \'function\') {\n
+                    this[i] = prop;\n
+                } else {\n
+                    this[\'_\' + i] = prop;\n
+                }\n
+            }\n
+        },\n
 \n
-Metadata.contentValue = function (value) {\n
-  if (Array.isArray(value)) {\n
-    return Metadata.contentValue(value[0]);\n
-  }\n
-  if (Metadata.isDict(value)) {\n
-    return value.content;\n
-  }\n
-  return value;\n
-};\n
+        _months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),\n
+        months : function (m) {\n
+            return this._months[m.month()];\n
+        },\n
 \n
-Metadata.normalizeArray = function (value) {\n
-  var i;\n
-  value = value.slice();\n
-  i = 0;\n
-  while (i < value.length) {\n
-    value[i] = Metadata.asJsonableValue(value[i]);\n
-    if (Metadata.isDict(value[i])) {\n
-      value[i] = Metadata.normalizeObject(value[i]);\n
-      if (value[i] === undefined) {\n
-        value.splice(i, 1);\n
-      } else {\n
-        i += 1;\n
-      }\n
-    } else if (Metadata.isContent(value[i])) {\n
-      i += 1;\n
-    } else {\n
-      value.splice(i, 1);\n
-    }\n
-  }\n
-  if (value.length === 0) {\n
-    return;\n
-  }\n
-  if (value.length === 1) {\n
-    return value[0];\n
-  }\n
-  return value;\n
-};\n
+        _monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),\n
+        monthsShort : function (m) {\n
+            return this._monthsShort[m.month()];\n
+        },\n
 \n
-Metadata.normalizeObject = function (value) {\n
-  var i, count = 0, ok = false, new_value = {};\n
-  for (i in value) {\n
-    if (value.hasOwnProperty(i)) {\n
-      value[i] = Metadata.asJsonableValue(value[i]);\n
-      if (Metadata.isContent(value[i])) {\n
-        new_value[i] = value[i];\n
-        if (new_value[i] === undefined) {\n
-          delete new_value[i];\n
+        monthsParse : function (monthName) {\n
+            var i, mom, regex;\n
+\n
+            if (!this._monthsParse) {\n
+                this._monthsParse = [];\n
+            }\n
+\n
+            for (i = 0; i < 12; i++) {\n
+                // make the regex if we don\'t have it already\n
+                if (!this._monthsParse[i]) {\n
+                    mom = moment.utc([2000, i]);\n
+                    regex = \'^\' + this.months(mom, \'\') + \'|^\' + this.monthsShort(mom, \'\');\n
+                    this._monthsParse[i] = new RegExp(regex.replace(\'.\', \'\'), \'i\');\n
+                }\n
+                // test the regex\n
+                if (this._monthsParse[i].test(monthName)) {\n
+                    return i;\n
+                }\n
+            }\n
+        },\n
+\n
+        _weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),\n
+        weekdays : function (m) {\n
+            return this._weekdays[m.day()];\n
+        },\n
+\n
+        _weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),\n
+        weekdaysShort : function (m) {\n
+            return this._weekdaysShort[m.day()];\n
+        },\n
+\n
+        _weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"),\n
+        weekdaysMin : function (m) {\n
+            return this._weekdaysMin[m.day()];\n
+        },\n
+\n
+        weekdaysParse : function (weekdayName) {\n
+            var i, mom, regex;\n
+\n
+            if (!this._weekdaysParse) {\n
+                this._weekdaysParse = [];\n
+            }\n
+\n
+            for (i = 0; i < 7; i++) {\n
+                // make the regex if we don\'t have it already\n
+                if (!this._weekdaysParse[i]) {\n
+                    mom = moment([2000, 1]).day(i);\n
+                    regex = \'^\' + this.weekdays(mom, \'\') + \'|^\' + this.weekdaysShort(mom, \'\') + \'|^\' + this.weekdaysMin(mom, \'\');\n
+                    this._weekdaysParse[i] = new RegExp(regex.replace(\'.\', \'\'), \'i\');\n
+                }\n
+                // test the regex\n
+                if (this._weekdaysParse[i].test(weekdayName)) {\n
+                    return i;\n
+                }\n
+            }\n
+        },\n
+\n
+        _longDateFormat : {\n
+            LT : "h:mm A",\n
+            L : "MM/DD/YYYY",\n
+            LL : "MMMM D YYYY",\n
+            LLL : "MMMM D YYYY LT",\n
+            LLLL : "dddd, MMMM D YYYY LT"\n
+        },\n
+        longDateFormat : function (key) {\n
+            var output = this._longDateFormat[key];\n
+            if (!output && this._longDateFormat[key.toUpperCase()]) {\n
+                output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) {\n
+                    return val.slice(1);\n
+                });\n
+                this._longDateFormat[key] = output;\n
+            }\n
+            return output;\n
+        },\n
+\n
+        isPM : function (input) {\n
+            // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays\n
+            // Using charAt should be more compatible.\n
+            return ((input + \'\').toLowerCase().charAt(0) === \'p\');\n
+        },\n
+\n
+        _meridiemParse : /[ap]\\.?m?\\.?/i,\n
+        meridiem : function (hours, minutes, isLower) {\n
+            if (hours > 11) {\n
+                return isLower ? \'pm\' : \'PM\';\n
+            } else {\n
+                return isLower ? \'am\' : \'AM\';\n
+            }\n
+        },\n
+\n
+        _calendar : {\n
+            sameDay : \'[Today at] LT\',\n
+            nextDay : \'[Tomorrow at] LT\',\n
+            nextWeek : \'dddd [at] LT\',\n
+            lastDay : \'[Yesterday at] LT\',\n
+            lastWeek : \'[Last] dddd [at] LT\',\n
+            sameElse : \'L\'\n
+        },\n
+        calendar : function (key, mom) {\n
+            var output = this._calendar[key];\n
+            return typeof output === \'function\' ? output.apply(mom) : output;\n
+        },\n
+\n
+        _relativeTime : {\n
+            future : "in %s",\n
+            past : "%s ago",\n
+            s : "a few seconds",\n
+            m : "a minute",\n
+            mm : "%d minutes",\n
+            h : "an hour",\n
+            hh : "%d hours",\n
+            d : "a day",\n
+            dd : "%d days",\n
+            M : "a month",\n
+            MM : "%d months",\n
+            y : "a year",\n
+            yy : "%d years"\n
+        },\n
+        relativeTime : function (number, withoutSuffix, string, isFuture) {\n
+            var output = this._relativeTime[string];\n
+            return (typeof output === \'function\') ?\n
+                output(number, withoutSuffix, string, isFuture) :\n
+                output.replace(/%d/i, number);\n
+        },\n
+        pastFuture : function (diff, output) {\n
+            var format = this._relativeTime[diff > 0 ? \'future\' : \'past\'];\n
+            return typeof format === \'function\' ? format(output) : format.replace(/%s/i, output);\n
+        },\n
+\n
+        ordinal : function (number) {\n
+            return this._ordinal.replace("%d", number);\n
+        },\n
+        _ordinal : "%d",\n
+\n
+        preparse : function (string) {\n
+            return string;\n
+        },\n
+\n
+        postformat : function (string) {\n
+            return string;\n
+        },\n
+\n
+        week : function (mom) {\n
+            return weekOfYear(mom, this._week.dow, this._week.doy).week;\n
+        },\n
+\n
+        _week : {\n
+            dow : 0, // Sunday is the first day of the week.\n
+            doy : 6  // The week that contains Jan 1st is the first week of the year.\n
+        },\n
+\n
+        _invalidDate: \'Invalid date\',\n
+        invalidDate: function () {\n
+            return this._invalidDate;\n
+        }\n
+    });\n
+\n
+    // Loads a language definition into the `languages` cache.  The function\n
+    // takes a key and optionally values.  If not in the browser and no values\n
+    // are provided, it will load the language file module.  As a convenience,\n
+    // this function also returns the language values.\n
+    function loadLang(key, values) {\n
+        values.abbr = key;\n
+        if (!languages[key]) {\n
+            languages[key] = new Language();\n
         }\n
-        count += 1;\n
-        if (i === \'content\') {\n
-          ok = true;\n
+        languages[key].set(values);\n
+        return languages[key];\n
+    }\n
+\n
+    // Remove a language from the `languages` cache. Mostly useful in tests.\n
+    function unloadLang(key) {\n
+        delete languages[key];\n
+    }\n
+\n
+    // Determines which language definition to use and returns it.\n
+    //\n
+    // With no parameters, it will return the global language.  If you\n
+    // pass in a language key, such as \'en\', it will return the\n
+    // definition for \'en\', so long as \'en\' has already been loaded using\n
+    // moment.lang.\n
+    function getLangDefinition(key) {\n
+        var i = 0, j, lang, next, split,\n
+            get = function (k) {\n
+                if (!languages[k] && hasModule) {\n
+                    try {\n
+                        require(\'./lang/\' + k);\n
+                    } catch (e) { }\n
+                }\n
+                return languages[k];\n
+            };\n
+\n
+        if (!key) {\n
+            return moment.fn._lang;\n
         }\n
-      }\n
-    }\n
-  }\n
-  if (ok === false) {\n
-    return;\n
-  }\n
-  if (count === 1) {\n
-    return new_value.content;\n
-  }\n
-  return new_value;\n
-};\n
 \n
-Metadata.normalizeValue = function (value) {\n
-  value = Metadata.asJsonableValue(value);\n
-  if (Metadata.isContent(value)) {\n
-    return value;\n
-  }\n
-  if (Array.isArray(value)) {\n
-    return Metadata.normalizeArray(value);\n
-  }\n
-  if (Metadata.isDict(value)) {\n
-    return Metadata.normalizeObject(value);\n
-  }\n
-};\n
+        if (!isArray(key)) {\n
+            //short-circuit everything else\n
+            lang = get(key);\n
+            if (lang) {\n
+                return lang;\n
+            }\n
+            key = [key];\n
+        }\n
 \n
-Metadata.checkArray = function (value) {\n
-  var i;\n
-  for (i = 0; i < value.length; i += 1) {\n
-    if (Metadata.isDict(value[i])) {\n
-      if (!Metadata.checkObject(value[i])) {\n
-        return false;\n
-      }\n
-    } else if (!Metadata.isContent(value[i])) {\n
-      return false;\n
+        //pick the language from the array\n
+        //try [\'en-au\', \'en-gb\'] as \'en-au\', \'en-gb\', \'en\', as in move through the list trying each\n
+        //substring from most specific to least, but move to the next array item if it\'s a more specific variant than the current root\n
+        while (i < key.length) {\n
+            split = normalizeLanguage(key[i]).split(\'-\');\n
+            j = split.length;\n
+            next = normalizeLanguage(key[i + 1]);\n
+            next = next ? next.split(\'-\') : null;\n
+            while (j > 0) {\n
+                lang = get(split.slice(0, j).join(\'-\'));\n
+                if (lang) {\n
+                    return lang;\n
+                }\n
+                if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {\n
+                    //the next array item is better than a shallower substring of this one\n
+                    break;\n
+                }\n
+                j--;\n
+            }\n
+            i++;\n
+        }\n
+        return moment.fn._lang;\n
     }\n
-  }\n
-  return true;\n
-};\n
 \n
-Metadata.checkObject = function (value) {\n
-  var i, ok = false;\n
-  for (i in value) {\n
-    if (value.hasOwnProperty(i)) {\n
-      if (Metadata.isContent(value[i])) {\n
-        if (i === \'content\') {\n
-          ok = true;\n
+    /************************************\n
+        Formatting\n
+    ************************************/\n
+\n
+\n
+    function removeFormattingTokens(input) {\n
+        if (input.match(/\\[[\\s\\S]/)) {\n
+            return input.replace(/^\\[|\\]$/g, "");\n
         }\n
-      } else {\n
-        return false;\n
-      }\n
+        return input.replace(/\\\\/g, "");\n
     }\n
-  }\n
-  if (ok === false) {\n
-    return false;\n
-  }\n
-  return true;\n
-};\n
-\n
-Metadata.checkValue = function (value) {\n
-  if (Metadata.isContent(value)) {\n
-    return true;\n
-  }\n
-  if (Array.isArray(value)) {\n
-    return Metadata.checkArray(value);\n
-  }\n
-  if (Metadata.isDict(value)) {\n
-    return Metadata.checkObject(value);\n
-  }\n
-  return false;\n
-};\n
 \n
-exports.Metadata = Metadata;\n
+    function makeFormatFunction(format) {\n
+        var array = format.match(formattingTokens), i, length;\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */\n
-/*global */\n
+        for (i = 0, length = array.length; i < length; i++) {\n
+            if (formatTokenFunctions[array[i]]) {\n
+                array[i] = formatTokenFunctions[array[i]];\n
+            } else {\n
+                array[i] = removeFormattingTokens(array[i]);\n
+            }\n
+        }\n
 \n
-/**\n
- * An array that contain object (or array) references.\n
- *\n
- * @class ReferenceArray\n
- * @constructor\n
- * @param  {array} [array] The array where to work on\n
- */\n
-function ReferenceArray(array) {\n
-  if (Array.isArray(array)) {\n
-    this._array = array;\n
-  } else {\n
-    this._array = [];\n
-  }\n
-}\n
-\n
-/**\n
- * Returns the array version of the job queue\n
- *\n
- * @method asArray\n
- * @return {Array} The job queue as array\n
- */\n
-ReferenceArray.prototype.asArray = function () {\n
-  return this._array;\n
-};\n
-\n
-/**\n
- * Returns the index of the object\n
- *\n
- * @method indexOf\n
- * @param  {Object} object The object to search\n
- */\n
-ReferenceArray.prototype.indexOf = function (object) {\n
-  var i;\n
-  for (i = 0; i < this._array.length; i += 1) {\n
-    if (this._array[i] === object) {\n
-      return i;\n
-    }\n
-  }\n
-  return -1;\n
-};\n
-\n
-/**\n
- * Put an object to the list. If an object already exists, do nothing.\n
- *\n
- * @method put\n
- * @param  {Object} object The object to add\n
- */\n
-ReferenceArray.prototype.put = function (object) {\n
-  var i;\n
-  for (i = 0; i < this._array.length; i += 1) {\n
-    if (this._array[i] === object) {\n
-      return false;\n
-    }\n
-  }\n
-  this._array[i] = object;\n
-  return true;\n
-};\n
-\n
-/**\n
- * Removes an object from the list\n
- *\n
- * @method remove\n
- * @param  {Object} object The object to remove\n
- */\n
-ReferenceArray.prototype.remove = function (object) {\n
-  var i;\n
-  for (i = 0; i < this._array.length; i += 1) {\n
-    if (this._array[i] === object) {\n
-      this._array.splice(i, 1);\n
-      return true;\n
+        return function (mom) {\n
+            var output = "";\n
+            for (i = 0; i < length; i++) {\n
+                output += array[i] instanceof Function ? array[i].call(mom, format) : array[i];\n
+            }\n
+            return output;\n
+        };\n
     }\n
-  }\n
-  return false;\n
-};\n
-\n
-/**\n
- * Clears the list.\n
- *\n
- * @method clear\n
- */\n
-ReferenceArray.prototype.clear = function () {\n
-  this._array.length = 0;\n
-  return this;\n
-};\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true */\n
-/*global exports, defaults */\n
+    // format date using native date object\n
+    function formatMoment(m, format) {\n
 \n
-function Storage() { // (storage_spec, util)\n
-  return undefined; // this is a constructor\n
-}\n
-// end Storage\n
+        if (!m.isValid()) {\n
+            return m.lang().invalidDate();\n
+        }\n
 \n
-function createStorage(storage_spec, util) {\n
-  if (typeof storage_spec.type !== \'string\') {\n
-    throw new TypeError("Invalid storage description");\n
-  }\n
-  if (!defaults.storage_types[storage_spec.type]) {\n
-    throw new TypeError("Unknown storage \'" + storage_spec.type + "\'");\n
-  }\n
-  return new defaults.storage_types[storage_spec.type](storage_spec, util);\n
-}\n
+        format = expandFormat(format, m.lang());\n
 \n
-function addStorage(type, Constructor) {\n
-  // var proto = {};\n
-  if (typeof type !== \'string\') {\n
-    throw new TypeError("jIO.addStorage(): Argument 1 is not of type \'string\'");\n
-  }\n
-  if (typeof Constructor !== \'function\') {\n
-    throw new TypeError("jIO.addStorage(): " +\n
-                        "Argument 2 is not of type \'function\'");\n
-  }\n
-  if (defaults.storage_types[type]) {\n
-    throw new TypeError("jIO.addStorage(): Storage type already exists");\n
-  }\n
-  // dictUpdate(proto, Constructor.prototype);\n
-  // inherits(Constructor, Storage);\n
-  // dictUpdate(Constructor.prototype, proto);\n
-  defaults.storage_types[type] = Constructor;\n
-}\n
-exports.addStorage = addStorage;\n
+        if (!formatFunctions[format]) {\n
+            formatFunctions[format] = makeFormatFunction(format);\n
+        }\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */\n
-/*global */\n
+        return formatFunctions[format](m);\n
+    }\n
 \n
-/**\n
- * A class that acts like localStorage on a simple object.\n
- *\n
- * Like localStorage, the object will contain only strings.\n
- *\n
- * @class Workspace\n
- * @constructor\n
- */\n
-function Workspace(object) {\n
-  this._object = object;\n
-}\n
+    function expandFormat(format, lang) {\n
+        var i = 5;\n
 \n
-// // Too dangerous, never use it\n
-// /**\n
-//  * Empty the entire space.\n
-//  *\n
-//  * @method clear\n
-//  */\n
-// Workspace.prototype.clear = function () {\n
-//   var k;\n
-//   for (k in this._object) {\n
-//     if (this._object.hasOwnProperty(k)) {\n
-//       delete this._object;\n
-//     }\n
-//   }\n
-//   return undefined;\n
-// };\n
+        function replaceLongDateFormatTokens(input) {\n
+            return lang.longDateFormat(input) || input;\n
+        }\n
 \n
-/**\n
- * Get an item from the space. If the value does not exists, it returns\n
- * null. Else, it returns the string value.\n
- *\n
- * @method getItem\n
- * @param  {String} key The location where to get the item\n
- * @return {String} The item\n
- */\n
-Workspace.prototype.getItem = function (key) {\n
-  return this._object[key] === undefined ? null : this._object[key];\n
-};\n
+        localFormattingTokens.lastIndex = 0;\n
+        while (i >= 0 && localFormattingTokens.test(format)) {\n
+            format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);\n
+            localFormattingTokens.lastIndex = 0;\n
+            i -= 1;\n
+        }\n
 \n
-/**\n
- * Set an item into the space. The value to store is converted to string before.\n
- *\n
- * @method setItem\n
- * @param  {String} key The location where to set the item\n
- * @param  {Any} value The value to store\n
- */\n
-Workspace.prototype.setItem = function (key, value) {\n
-  if (value === undefined) {\n
-    this._object[key] = \'undefined\';\n
-  } else if (value === null) {\n
-    this._object[key] = \'null\';\n
-  } else {\n
-    this._object[key] = value.toString();\n
-  }\n
-  return undefined;\n
-};\n
+        return format;\n
+    }\n
+\n
+\n
+    /************************************\n
+        Parsing\n
+    ************************************/\n
+\n
+\n
+    // get the regex to find the next token\n
+    function getParseRegexForToken(token, config) {\n
+        var a, strict = config._strict;\n
+        switch (token) {\n
+        case \'DDDD\':\n
+            return parseTokenThreeDigits;\n
+        case \'YYYY\':\n
+        case \'GGGG\':\n
+        case \'gggg\':\n
+            return strict ? parseTokenFourDigits : parseTokenOneToFourDigits;\n
+        case \'YYYYYY\':\n
+        case \'YYYYY\':\n
+        case \'GGGGG\':\n
+        case \'ggggg\':\n
+            return strict ? parseTokenSixDigits : parseTokenOneToSixDigits;\n
+        case \'S\':\n
+            if (strict) { return parseTokenOneDigit; }\n
+            /* falls through */\n
+        case \'SS\':\n
+            if (strict) { return parseTokenTwoDigits; }\n
+            /* falls through */\n
+        case \'SSS\':\n
+        case \'DDD\':\n
+            return strict ? parseTokenThreeDigits : parseTokenOneToThreeDigits;\n
+        case \'MMM\':\n
+        case \'MMMM\':\n
+        case \'dd\':\n
+        case \'ddd\':\n
+        case \'dddd\':\n
+            return parseTokenWord;\n
+        case \'a\':\n
+        case \'A\':\n
+            return getLangDefinition(config._l)._meridiemParse;\n
+        case \'X\':\n
+            return parseTokenTimestampMs;\n
+        case \'Z\':\n
+        case \'ZZ\':\n
+            return parseTokenTimezone;\n
+        case \'T\':\n
+            return parseTokenT;\n
+        case \'SSSS\':\n
+            return parseTokenDigits;\n
+        case \'MM\':\n
+        case \'DD\':\n
+        case \'YY\':\n
+        case \'GG\':\n
+        case \'gg\':\n
+        case \'HH\':\n
+        case \'hh\':\n
+        case \'mm\':\n
+        case \'ss\':\n
+        case \'ww\':\n
+        case \'WW\':\n
+            return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits;\n
+        case \'M\':\n
+        case \'D\':\n
+        case \'d\':\n
+        case \'H\':\n
+        case \'h\':\n
+        case \'m\':\n
+        case \'s\':\n
+        case \'w\':\n
+        case \'W\':\n
+        case \'e\':\n
+        case \'E\':\n
+            return strict ? parseTokenOneDigit : parseTokenOneOrTwoDigits;\n
+        default :\n
+            a = new RegExp(regexpEscape(unescapeFormat(token.replace(\'\\\\\', \'\')), "i"));\n
+            return a;\n
+        }\n
+    }\n
 \n
-/**\n
- * Removes an item from the space.\n
- *\n
- * @method removeItem\n
- * @param  {String} key The location where to remove the item\n
- */\n
-Workspace.prototype.removeItem = function (key) {\n
-  delete this._object[key];\n
-  return undefined;\n
-};\n
+    function timezoneMinutesFromString(string) {\n
+        string = string || "";\n
+        var possibleTzMatches = (string.match(parseTokenTimezone) || []),\n
+            tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [],\n
+            parts = (tzChunk + \'\').match(parseTimezoneChunker) || [\'-\', 0, 0],\n
+            minutes = +(parts[1] * 60) + toInt(parts[2]);\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true */\n
-/*global exports, defaults */\n
+        return parts[0] === \'+\' ? -minutes : minutes;\n
+    }\n
 \n
-// adds\n
-// - jIO.addJobRuleCondition(name, function)\n
+    // function to convert string input to date\n
+    function addTimeToArrayFromToken(token, input, config) {\n
+        var a, datePartArray = config._a;\n
 \n
-function addJobRuleCondition(name, method) {\n
-  if (typeof name !== \'string\') {\n
-    throw new TypeError("jIO.addJobRuleAction(): " +\n
-                        "Argument 1 is not of type \'string\'");\n
-  }\n
-  if (typeof method !== \'function\') {\n
-    throw new TypeError("jIO.addJobRuleAction(): " +\n
-                        "Argument 2 is not of type \'function\'");\n
-  }\n
-  if (defaults.job_rule_conditions[name]) {\n
-    throw new TypeError("jIO.addJobRuleAction(): Action already exists");\n
-  }\n
-  defaults.job_rule_conditions[name] = method;\n
-}\n
-exports.addJobRuleCondition = addJobRuleCondition;\n
-\n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, regexp: true */\n
-/*global constants, dictUpdate, deepClone, DOMException */\n
-\n
-function restCommandRejecter(param, args) {\n
-  // reject(status, reason, message, {"custom": "value"});\n
-  // reject(status, reason, {..});\n
-  // reject(status, {..});\n
-  var arg, current_priority, priority = [\n
-    // 0 - custom parameter values\n
-    {},\n
-    // 1 - default values\n
-    {\n
-      "status": constants.http_status.unknown,\n
-      "statusText": constants.http_status_text.unknown,\n
-      "message": "Command failed",\n
-      "reason": "unknown"\n
-    },\n
-    // 2 - status, reason, message properties\n
-    {},\n
-    // 3 - status, reason, message parameters\n
-    {},\n
-    // 4 - never change\n
-    {"result": "error", "method": param.method}\n
-  ];\n
-  args = Array.prototype.slice.call(args);\n
-  arg = args.shift();\n
-\n
-  // priority 4 - never change\n
-  current_priority = priority[4];\n
-  if (param.kwargs._id) {\n
-    current_priority.id = param.kwargs._id;\n
-  }\n
-  if (/Attachment$/.test(param.method)) {\n
-    current_priority.attachment = param.kwargs._attachment;\n
-  }\n
+        switch (token) {\n
+        // MONTH\n
+        case \'M\' : // fall through to MM\n
+        case \'MM\' :\n
+            if (input != null) {\n
+                datePartArray[MONTH] = toInt(input) - 1;\n
+            }\n
+            break;\n
+        case \'MMM\' : // fall through to MMMM\n
+        case \'MMMM\' :\n
+            a = getLangDefinition(config._l).monthsParse(input);\n
+            // if we didn\'t find a month name, mark the date as invalid.\n
+            if (a != null) {\n
+                datePartArray[MONTH] = a;\n
+            } else {\n
+                config._pf.invalidMonth = input;\n
+            }\n
+            break;\n
+        // DAY OF MONTH\n
+        case \'D\' : // fall through to DD\n
+        case \'DD\' :\n
+            if (input != null) {\n
+                datePartArray[DATE] = toInt(input);\n
+            }\n
+            break;\n
+        // DAY OF YEAR\n
+        case \'DDD\' : // fall through to DDDD\n
+        case \'DDDD\' :\n
+            if (input != null) {\n
+                config._dayOfYear = toInt(input);\n
+            }\n
 \n
-  // priority 3 - status, reason, message parameters\n
-  current_priority = priority[3];\n
-  // parsing first parameter if is not an object\n
-  if (typeof arg !== \'object\' || arg === null || Array.isArray(arg)) {\n
-    // first parameter is mandatory\n
-    current_priority.status = arg;\n
-    arg = args.shift();\n
-  }\n
-  // parsing second parameter if is not an object\n
-  if (typeof arg !== \'object\' || arg === null || Array.isArray(arg)) {\n
-    if (arg !== undefined) {\n
-      current_priority.reason = arg;\n
-    }\n
-    arg = args.shift();\n
-  }\n
-  // parsing third parameter if is not an object\n
-  if (typeof arg !== \'object\' || arg === null || Array.isArray(arg)) {\n
-    if (arg !== undefined) {\n
-      current_priority.message = arg;\n
+            break;\n
+        // YEAR\n
+        case \'YY\' :\n
+            datePartArray[YEAR] = toInt(input) + (toInt(input) > 68 ? 1900 : 2000);\n
+            break;\n
+        case \'YYYY\' :\n
+        case \'YYYYY\' :\n
+        case \'YYYYYY\' :\n
+            datePartArray[YEAR] = toInt(input);\n
+            break;\n
+        // AM / PM\n
+        case \'a\' : // fall through to A\n
+        case \'A\' :\n
+            config._isPm = getLangDefinition(config._l).isPM(input);\n
+            break;\n
+        // 24 HOUR\n
+        case \'H\' : // fall through to hh\n
+        case \'HH\' : // fall through to hh\n
+        case \'h\' : // fall through to hh\n
+        case \'hh\' :\n
+            datePartArray[HOUR] = toInt(input);\n
+            break;\n
+        // MINUTE\n
+        case \'m\' : // fall through to mm\n
+        case \'mm\' :\n
+            datePartArray[MINUTE] = toInt(input);\n
+            break;\n
+        // SECOND\n
+        case \'s\' : // fall through to ss\n
+        case \'ss\' :\n
+            datePartArray[SECOND] = toInt(input);\n
+            break;\n
+        // MILLISECOND\n
+        case \'S\' :\n
+        case \'SS\' :\n
+        case \'SSS\' :\n
+        case \'SSSS\' :\n
+            datePartArray[MILLISECOND] = toInt((\'0.\' + input) * 1000);\n
+            break;\n
+        // UNIX TIMESTAMP WITH MS\n
+        case \'X\':\n
+            config._d = new Date(parseFloat(input) * 1000);\n
+            break;\n
+        // TIMEZONE\n
+        case \'Z\' : // fall through to ZZ\n
+        case \'ZZ\' :\n
+            config._useUTC = true;\n
+            config._tzm = timezoneMinutesFromString(input);\n
+            break;\n
+        case \'w\':\n
+        case \'ww\':\n
+        case \'W\':\n
+        case \'WW\':\n
+        case \'d\':\n
+        case \'dd\':\n
+        case \'ddd\':\n
+        case \'dddd\':\n
+        case \'e\':\n
+        case \'E\':\n
+            token = token.substr(0, 1);\n
+            /* falls through */\n
+        case \'gg\':\n
+        case \'gggg\':\n
+        case \'GG\':\n
+        case \'GGGG\':\n
+        case \'GGGGG\':\n
+            token = token.substr(0, 2);\n
+            if (input) {\n
+                config._w = config._w || {};\n
+                config._w[token] = input;\n
+            }\n
+            break;\n
+        }\n
     }\n
-    arg = args.shift();\n
-  }\n
 \n
-  // parsing fourth parameter if is an object\n
-  if (typeof arg === \'object\' && arg !== null && !Array.isArray(arg)) {\n
-    // priority 0 - custom values\n
-    dictUpdate(priority[0], arg);\n
-    // priority 2 - status, reason, message properties\n
-    current_priority = priority[2];\n
-    if (arg.hasOwnProperty(\'reason\')) {\n
-      current_priority.reason = arg.reason;\n
-    }\n
-    if (arg.hasOwnProperty(\'message\')) {\n
-      current_priority.message = arg.message;\n
-    }\n
-    if ((arg.statusText || arg.status >= 0)) {\n
-      current_priority.status = arg.statusText || arg.status;\n
-    }\n
-    if (arg instanceof Error || arg instanceof DOMException) {\n
-      if (arg.code !== undefined && arg.code !== null) {\n
-        current_priority.code = arg.code;\n
-      }\n
-      if (arg.lineNumber !== undefined && arg.lineNumber !== null) {\n
-        current_priority.lineNumber = arg.lineNumber;\n
-      }\n
-      if (arg.columnNumber !== undefined && arg.columnNumber !== null) {\n
-        current_priority.columnNumber = arg.columnNumber;\n
-      }\n
-      if (arg.filename !== undefined && arg.filename !== null) {\n
-        current_priority.filename = arg.filename;\n
-      }\n
-      if (arg.message !== undefined && arg.message !== null) {\n
-        current_priority.reason = arg.message;\n
-      }\n
-      current_priority.error = arg.name;\n
-    }\n
-  }\n
+    // convert an array to a date.\n
+    // the array should mirror the parameters below\n
+    // note: all values past the year are optional and will default to the lowest possible value.\n
+    // [year, month, day , hour, minute, second, millisecond]\n
+    function dateFromConfig(config) {\n
+        var i, date, input = [], currentDate,\n
+            yearToUse, fixYear, w, temp, lang, weekday, week;\n
 \n
-  // merge priority dicts\n
-  for (current_priority = priority.length - 1;\n
-       current_priority > 0;\n
-       current_priority -= 1) {\n
-    dictUpdate(priority[current_priority - 1], priority[current_priority]);\n
-  }\n
-  priority = priority[0];\n
-\n
-  // check status\n
-  priority.statusText = constants.http_status_text[priority.status];\n
-  if (priority.statusText === undefined) {\n
-    return restCommandRejecter(param, [\n
-      // can create infernal loop if \'internal_storage_error\' is not defined in\n
-      // the constants\n
-      \'internal_storage_error\',\n
-      \'invalid response\',\n
-      \'Unknown status "\' + priority.status + \'"\'\n
-    ]);\n
-  }\n
-  priority.status = constants.http_status[priority.statusText];\n
+        if (config._d) {\n
+            return;\n
+        }\n
 \n
-  // set default priority error if not already set\n
-  if (priority.error === undefined) {\n
-    priority.error = priority.statusText.toLowerCase().replace(/ /g, \'_\').\n
-      replace(/[^_a-z]/g, \'\');\n
-  }\n
-  param.storage_response = priority;\n
-  return param.solver.reject(deepClone(priority));\n
-}\n
+        currentDate = currentDateArray(config);\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */\n
-/*global constants, methodType, dictUpdate, Blob, deepClone,\n
-  restCommandRejecter */\n
-\n
-function restCommandResolver(param, args) {\n
-  // resolve(\'ok\', {"custom": "value"});\n
-  // resolve(200, {...});\n
-  // resolve({...});\n
-  var arg, current_priority, priority = [\n
-    // 0 - custom parameter values\n
-    {},\n
-    // 1 - default values\n
-    {},\n
-    // 2 - status property\n
-    {},\n
-    // 3 - status parameter\n
-    {},\n
-    // 4 - never change\n
-    {"result": "success", "method": param.method}\n
-  ];\n
-  args = Array.prototype.slice.call(args);\n
-  arg = args.shift();\n
-\n
-  // priority 4 - never change\n
-  current_priority = priority[4];\n
-  if (param.kwargs._id) {\n
-    current_priority.id = param.kwargs._id;\n
-  }\n
-  if (/Attachment$/.test(param.method)) {\n
-    current_priority.attachment = param.kwargs._attachment;\n
-  }\n
+        //compute day of the year from weeks and weekdays\n
+        if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {\n
+            fixYear = function (val) {\n
+                var int_val = parseInt(val, 10);\n
+                return val ?\n
+                  (val.length < 3 ? (int_val > 68 ? 1900 + int_val : 2000 + int_val) : int_val) :\n
+                  (config._a[YEAR] == null ? moment().weekYear() : config._a[YEAR]);\n
+            };\n
 \n
-  // priority 1 - default values\n
-  current_priority = priority[1];\n
-  if (param.method === \'post\') {\n
-    current_priority.status = constants.http_status.created;\n
-    current_priority.statusText = constants.http_status_text.created;\n
-  } else if (methodType(param.method) === "writer" ||\n
-             param.method === "check") {\n
-    current_priority.status = constants.http_status.no_content;\n
-    current_priority.statusText = constants.http_status_text.no_content;\n
-  } else {\n
-    current_priority.status = constants.http_status.ok;\n
-    current_priority.statusText = constants.http_status_text.ok;\n
-  }\n
+            w = config._w;\n
+            if (w.GG != null || w.W != null || w.E != null) {\n
+                temp = dayOfYearFromWeeks(fixYear(w.GG), w.W || 1, w.E, 4, 1);\n
+            }\n
+            else {\n
+                lang = getLangDefinition(config._l);\n
+                weekday = w.d != null ?  parseWeekday(w.d, lang) :\n
+                  (w.e != null ?  parseInt(w.e, 10) + lang._week.dow : 0);\n
 \n
-  // priority 3 - status parameter\n
-  current_priority = priority[3];\n
-  // parsing first parameter if is not an object\n
-  if (typeof arg !== \'object\' || arg === null || Array.isArray(arg)) {\n
-    if (arg !== undefined) {\n
-      current_priority.status = arg;\n
-    }\n
-    arg = args.shift();\n
-  }\n
+                week = parseInt(w.w, 10) || 1;\n
 \n
-  // parsing second parameter if is an object\n
-  if (typeof arg === \'object\' && arg !== null && !Array.isArray(arg)) {\n
-    // priority 0 - custom values\n
-    dictUpdate(current_priority, arg);\n
-    // priority 2 - status property\n
-    if (arg.hasOwnProperty("status") || arg.hasOwnProperty("statusText")) {\n
-      priority[2].status = arg.statusText || arg.status;\n
-    }\n
-  }\n
+                //if we\'re parsing \'d\', then the low day numbers may be next week\n
+                if (w.d != null && weekday < lang._week.dow) {\n
+                    week++;\n
+                }\n
 \n
-  // merge priority dicts\n
-  for (current_priority = priority.length - 1;\n
-       current_priority > 0;\n
-       current_priority -= 1) {\n
-    dictUpdate(priority[current_priority - 1], priority[current_priority]);\n
-  }\n
-  priority = priority[0];\n
-\n
-  // check document id if post method\n
-  if (param.method === \'post\' &&\n
-      (typeof priority.id !== \'string\' || !priority.id)) {\n
-    return restCommandRejecter(param, [\n
-      \'internal_storage_error\',\n
-      \'invalid response\',\n
-      \'New document id have to be specified\'\n
-    ]);\n
-  }\n
+                temp = dayOfYearFromWeeks(fixYear(w.gg), week, weekday, lang._week.doy, lang._week.dow);\n
+            }\n
 \n
-  // check status\n
-  priority.statusText = constants.http_status_text[priority.status];\n
-  if (priority.statusText === undefined) {\n
-    return restCommandRejecter(param, [\n
-      \'internal_storage_error\',\n
-      \'invalid response\',\n
-      \'Unknown status "\' + priority.status + \'"\'\n
-    ]);\n
-  }\n
-  priority.status = constants.http_status[priority.statusText];\n
+            config._a[YEAR] = temp.year;\n
+            config._dayOfYear = temp.dayOfYear;\n
+        }\n
 \n
-  // check data for get Attachment\n
-  if (param.method === \'getAttachment\') {\n
-    if (typeof priority.data === \'string\') {\n
-      priority.data = new Blob([priority.data], {\n
-        "type": priority.content_type || priority.mimetype || ""\n
-      });\n
-      delete priority.content_type;\n
-      delete priority.mimetype;\n
-    }\n
-    if (!(priority.data instanceof Blob)) {\n
-      return restCommandRejecter(param, [\n
-        \'internal_storage_error\',\n
-        \'invalid response\',\n
-        \'getAttachment method needs a Blob as returned "data".\'\n
-      ]);\n
-    }\n
-    // check data for readers (except check method)\n
-  } else if (methodType(param.method) === \'reader\' &&\n
-             param.method !== \'check\' &&\n
-             (typeof priority.data !== \'object\' ||\n
-              priority.data === null ||\n
-              Object.getPrototypeOf(priority.data) !== Object.prototype)) {\n
-    return restCommandRejecter(param, [\n
-      \'internal_storage_error\',\n
-      \'invalid response\',\n
-      param.method + \' method needs a dict as returned "data".\'\n
-    ]);\n
-  }\n
+        //if the day of the year is set, figure out what it is\n
+        if (config._dayOfYear) {\n
+            yearToUse = config._a[YEAR] == null ? currentDate[YEAR] : config._a[YEAR];\n
 \n
-  param.storage_response = priority;\n
-  return param.solver.resolve(deepClone(priority));\n
-}\n
+            if (config._dayOfYear > daysInYear(yearToUse)) {\n
+                config._pf._overflowDayOfYear = true;\n
+            }\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */\n
-/*global arrayInsert, deepClone, defaults */\n
+            date = makeUTCDate(yearToUse, 0, config._dayOfYear);\n
+            config._a[MONTH] = date.getUTCMonth();\n
+            config._a[DATE] = date.getUTCDate();\n
+        }\n
 \n
-// creates\n
-// - some defaults job rule actions\n
+        // Default to current date.\n
+        // * if no year, month, day of month are given, default to today\n
+        // * if day of month is given, default month and year\n
+        // * if month is given, default only year\n
+        // * if year is given, don\'t default anything\n
+        for (i = 0; i < 3 && config._a[i] == null; ++i) {\n
+            config._a[i] = input[i] = currentDate[i];\n
+        }\n
 \n
-function enableJobChecker(jio, shared, options) {\n
+        // Zero out whatever was not defaulted, including time\n
+        for (; i < 7; i++) {\n
+            config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];\n
+        }\n
 \n
-  // dependencies\n
-  // - shared.jobs Object Array\n
-  // - param.promise Object\n
+        // add the offsets to the time to be parsed so that we can have a clean array for checking isValid\n
+        input[HOUR] += toInt((config._tzm || 0) / 60);\n
+        input[MINUTE] += toInt((config._tzm || 0) % 60);\n
 \n
-  // creates\n
-  // - shared.job_rules Array\n
+        config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input);\n
+    }\n
 \n
-  // uses \'job:new\' event\n
-  // emits \'job:modified\', \'job:start\', \'job:resolved\',\n
-  // \'job:end\', \'job:reject\' events\n
+    function dateFromObject(config) {\n
+        var normalizedInput;\n
 \n
-  shared.job_rule_action_names = [undefined, "ok", "wait", "update", "deny"];\n
+        if (config._d) {\n
+            return;\n
+        }\n
 \n
-  shared.job_rule_actions = {\n
-    wait: function (original_job, new_job) {\n
-      original_job.promise.always(function () {\n
-        new_job.state = \'ready\';\n
-        new_job.modified = new Date();\n
-        shared.emit(\'job:modified\', new_job);\n
-        shared.emit(\'job:start\', new_job);\n
-      });\n
-      new_job.state = \'waiting\';\n
-      new_job.modified = new Date();\n
-      shared.emit(\'job:modified\', new_job);\n
-    },\n
-    update: function (original_job, new_job) {\n
-      if (!new_job.solver) {\n
-        // promise associated to the job\n
-        new_job.state = \'done\';\n
-        shared.emit(\'job:resolved\', new_job, []); // XXX why resolve?\n
-        shared.emit(\'job:end\', new_job);\n
-      } else {\n
-        if (!original_job.solver) {\n
-          original_job.solver = new_job.solver;\n
+        normalizedInput = normalizeObjectUnits(config._i);\n
+        config._a = [\n
+            normalizedInput.year,\n
+            normalizedInput.month,\n
+            normalizedInput.day,\n
+            normalizedInput.hour,\n
+            normalizedInput.minute,\n
+            normalizedInput.second,\n
+            normalizedInput.millisecond\n
+        ];\n
+\n
+        dateFromConfig(config);\n
+    }\n
+\n
+    function currentDateArray(config) {\n
+        var now = new Date();\n
+        if (config._useUTC) {\n
+            return [\n
+                now.getUTCFullYear(),\n
+                now.getUTCMonth(),\n
+                now.getUTCDate()\n
+            ];\n
         } else {\n
-          original_job.promise.then(function () {\n
-            new_job.command.resolve(deepClone(original_job.storage_response));\n
-          }, function () {\n
-            new_job.command.reject(deepClone(original_job.storage_response));\n
-          }, new_job.command.notify);\n
+            return [now.getFullYear(), now.getMonth(), now.getDate()];\n
         }\n
-      }\n
-      new_job.state = \'running\';\n
-      new_job.modified = new Date();\n
-      shared.emit(\'job:modified\', new_job);\n
-    },\n
-    deny: function (original_job, new_job) {\n
-      new_job.state = "running";\n
-      shared.emit(\'job:reject\', new_job, [\n
-        \'precondition_failed\',\n
-        \'command denied\',\n
-        \'Command rejected by the job checker.\'\n
-      ]);\n
-    }\n
-  };\n
-\n
-  function addJobRule(job_rule) {\n
-    var i, old_position, before_position, after_position;\n
-    // job_rule = {\n
-    //   code_name: string\n
-    //   conditions: [string, ...]\n
-    //   action: \'wait\',\n
-    //   after: code_name\n
-    //   before: code_name\n
-    // }\n
-    if (typeof job_rule !== \'object\' || job_rule === null) {\n
-      // wrong job rule\n
-      return;\n
-    }\n
-    if (typeof job_rule.code_name !== \'string\') {\n
-      // wrong code name\n
-      return;\n
-    }\n
-    if (!Array.isArray(job_rule.conditions)) {\n
-      // wrong conditions\n
-      return;\n
-    }\n
-    if (job_rule.single !== undefined && typeof job_rule.single !== \'boolean\') {\n
-      // wrong single property\n
-      return;\n
-    }\n
-    if (shared.job_rule_action_names.indexOf(job_rule.action) === -1) {\n
-      // wrong action\n
-      return;\n
-    }\n
-    if (job_rule.action !== \'deny\' && job_rule.single === true) {\n
-      // only \'deny\' action doesn\'t require original_job parameter\n
-      return;\n
     }\n
 \n
-    if (typeof job_rule.after !== \'string\') {\n
-      job_rule.after = \'\';\n
-    }\n
-    if (typeof job_rule.before !== \'string\') {\n
-      job_rule.before = \'\';\n
-    }\n
+    // date from string and format string\n
+    function makeDateFromStringAndFormat(config) {\n
 \n
-    for (i = 0; i < shared.job_rules.length; i += 1) {\n
-      if (shared.job_rules[i].code_name === job_rule.after) {\n
-        after_position = i + 1;\n
-      }\n
-      if (shared.job_rules[i].code_name === job_rule.before) {\n
-        before_position = i;\n
-      }\n
-      if (shared.job_rules[i].code_name === job_rule.code_name) {\n
-        old_position = i;\n
-      }\n
-    }\n
+        config._a = [];\n
+        config._pf.empty = true;\n
 \n
-    job_rule = {\n
-      "code_name": job_rule.code_name,\n
-      "conditions": job_rule.conditions,\n
-      "single": job_rule.single || false,\n
-      "action": job_rule.action || "ok"\n
-    };\n
+        // This array is used to make a Date, either with `new Date` or `Date.UTC`\n
+        var lang = getLangDefinition(config._l),\n
+            string = \'\' + config._i,\n
+            i, parsedInput, tokens, token, skipped,\n
+            stringLength = string.length,\n
+            totalParsedInputLength = 0;\n
 \n
-    if (before_position === undefined) {\n
-      before_position = shared.job_rules.length;\n
-    }\n
-    if (after_position > before_position) {\n
-      before_position = undefined;\n
-    }\n
-    if (job_rule.action !== "ok" && before_position !== undefined) {\n
-      arrayInsert(shared.job_rules, before_position, job_rule);\n
-    }\n
-    if (old_position !== undefined) {\n
-      if (old_position >= before_position) {\n
-        old_position += 1;\n
-      }\n
-      shared.job_rules.splice(old_position, 1);\n
-    }\n
-  }\n
+        tokens = expandFormat(config._f, lang).match(formattingTokens) || [];\n
 \n
-  function jobsRespectConditions(original_job, new_job, conditions) {\n
-    var j;\n
-    // browsing conditions\n
-    for (j = 0; j < conditions.length; j += 1) {\n
-      if (defaults.job_rule_conditions[conditions[j]]) {\n
-        if (\n
-          !defaults.job_rule_conditions[conditions[j]](original_job, new_job)\n
-        ) {\n
-          return false;\n
+        for (i = 0; i < tokens.length; i++) {\n
+            token = tokens[i];\n
+            parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];\n
+            if (parsedInput) {\n
+                skipped = string.substr(0, string.indexOf(parsedInput));\n
+                if (skipped.length > 0) {\n
+                    config._pf.unusedInput.push(skipped);\n
+                }\n
+                string = string.slice(string.indexOf(parsedInput) + parsedInput.length);\n
+                totalParsedInputLength += parsedInput.length;\n
+            }\n
+            // don\'t parse if it\'s not a known token\n
+            if (formatTokenFunctions[token]) {\n
+                if (parsedInput) {\n
+                    config._pf.empty = false;\n
+                }\n
+                else {\n
+                    config._pf.unusedTokens.push(token);\n
+                }\n
+                addTimeToArrayFromToken(token, parsedInput, config);\n
+            }\n
+            else if (config._strict && !parsedInput) {\n
+                config._pf.unusedTokens.push(token);\n
+            }\n
         }\n
-      }\n
-    }\n
-    return true;\n
-  }\n
 \n
-  function checkJob(job) {\n
-    var i, j;\n
-    if (job.state === \'ready\') {\n
-      // browsing rules\n
-      for (i = 0; i < shared.job_rules.length; i += 1) {\n
-        if (shared.job_rules[i].single) {\n
-          // no browse\n
-          if (\n
-            jobsRespectConditions(\n
-              job,\n
-              undefined,\n
-              shared.job_rules[i].conditions\n
-            )\n
-          ) {\n
-            shared.job_rule_actions[shared.job_rules[i].action](\n
-              undefined,\n
-              job\n
-            );\n
-            return;\n
-          }\n
-        } else {\n
-          // browsing jobs\n
-          for (j = shared.jobs.length - 1; j >= 0; j -= 1) {\n
-            if (shared.jobs[j] !== job) {\n
-              if (\n
-                jobsRespectConditions(\n
-                  shared.jobs[j],\n
-                  job,\n
-                  shared.job_rules[i].conditions\n
-                )\n
-              ) {\n
-                shared.job_rule_actions[shared.job_rules[i].action](\n
-                  shared.jobs[j],\n
-                  job\n
-                );\n
-                return;\n
-              }\n
-            }\n
-          }\n
+        // add remaining unparsed input length to the string\n
+        config._pf.charsLeftOver = stringLength - totalParsedInputLength;\n
+        if (string.length > 0) {\n
+            config._pf.unusedInput.push(string);\n
         }\n
-      }\n
-    }\n
-  }\n
 \n
-  var index;\n
-\n
-  if (options.job_management !== false) {\n
-\n
-    shared.job_rules = [{\n
-      "code_name": "readers update",\n
-      "conditions": [\n
-        "sameStorageDescription",\n
-        "areReaders",\n
-        "sameMethod",\n
-        "sameParameters",\n
-        "sameOptions"\n
-      ],\n
-      "action": "update"\n
-    }, {\n
-      "code_name": "metadata writers update",\n
-      "conditions": [\n
-        "sameStorageDescription",\n
-        "areWriters",\n
-        "useMetadataOnly",\n
-        "sameMethod",\n
-        "haveDocumentIds",\n
-        "sameParameters"\n
-      ],\n
-      "action": "update"\n
-    }, {\n
-      "code_name": "writers wait",\n
-      "conditions": [\n
-        "sameStorageDescription",\n
-        "areWriters",\n
-        "haveDocumentIds",\n
-        "sameDocumentId"\n
-      ],\n
-      "action": "wait"\n
-    }];\n
+        // handle am pm\n
+        if (config._isPm && config._a[HOUR] < 12) {\n
+            config._a[HOUR] += 12;\n
+        }\n
+        // if is 12 am, change hours to 0\n
+        if (config._isPm === false && config._a[HOUR] === 12) {\n
+            config._a[HOUR] = 0;\n
+        }\n
 \n
-    if (options.clear_job_rules === true) {\n
-      shared.job_rules.length = 0;\n
+        dateFromConfig(config);\n
+        checkOverflow(config);\n
     }\n
 \n
-    if (Array.isArray(options.job_rules)) {\n
-      for (index = 0; index < options.job_rules.length; index += 1) {\n
-        addJobRule(deepClone(options.job_rules[index]));\n
-      }\n
+    function unescapeFormat(s) {\n
+        return s.replace(/\\\\(\\[)|\\\\(\\])|\\[([^\\]\\[]*)\\]|\\\\(.)/g, function (matched, p1, p2, p3, p4) {\n
+            return p1 || p2 || p3 || p4;\n
+        });\n
     }\n
 \n
-    shared.on(\'job:new\', checkJob);\n
+    // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript\n
+    function regexpEscape(s) {\n
+        return s.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, \'\\\\$&\');\n
+    }\n
 \n
-  }\n
+    // date from string and array of format strings\n
+    function makeDateFromStringAndArray(config) {\n
+        var tempConfig,\n
+            bestMoment,\n
 \n
-  jio.jobRules = function () {\n
-    return deepClone(shared.job_rules);\n
-  };\n
+            scoreToBeat,\n
+            i,\n
+            currentScore;\n
 \n
-}\n
+        if (config._f.length === 0) {\n
+            config._pf.invalidFormat = true;\n
+            config._d = new Date(NaN);\n
+            return;\n
+        }\n
+\n
+        for (i = 0; i < config._f.length; i++) {\n
+            currentScore = 0;\n
+            tempConfig = extend({}, config);\n
+            initializeParsingFlags(tempConfig);\n
+            tempConfig._f = config._f[i];\n
+            makeDateFromStringAndFormat(tempConfig);\n
+\n
+            if (!isValid(tempConfig)) {\n
+                continue;\n
+            }\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */\n
-/*global setTimeout, Job, createStorage, deepClone, restCommandResolver,\n
-  restCommandRejecter */\n
+            // if there is any input that was not parsed add a penalty for that format\n
+            currentScore += tempConfig._pf.charsLeftOver;\n
 \n
-function enableJobExecuter(jio, shared) { // , options) {\n
+            //or tokens\n
+            currentScore += tempConfig._pf.unusedTokens.length * 10;\n
 \n
-  // uses \'job:new\' events\n
-  // uses actions \'job:resolve\', \'job:reject\' and \'job:notify\'\n
+            tempConfig._pf.score = currentScore;\n
 \n
-  // emits \'job:modified\', \'job:started\', \'job:resolved\',\n
-  // \'job:rejected\', \'job:notified\' and \'job:end\' events\n
-  // emits action \'job:start\'\n
+            if (scoreToBeat == null || currentScore < scoreToBeat) {\n
+                scoreToBeat = currentScore;\n
+                bestMoment = tempConfig;\n
+            }\n
+        }\n
 \n
-  function startJobIfReady(job) {\n
-    if (job.state === \'ready\') {\n
-      shared.emit(\'job:start\', job);\n
+        extend(config, bestMoment || tempConfig);\n
     }\n
-  }\n
 \n
-  function executeJobIfReady(param) {\n
-    var storage;\n
-    if (param.state === \'ready\') {\n
-      param.tried += 1;\n
-      param.started = new Date();\n
-      param.state = \'running\';\n
-      param.modified = new Date();\n
-      shared.emit(\'job:modified\', param);\n
-      shared.emit(\'job:started\', param);\n
-      try {\n
-        storage = createStorage(deepClone(param.storage_spec));\n
-      } catch (e) {\n
-        return param.command.reject(\n
-          \'internal_storage_error\',\n
-          \'invalid description\',\n
-          \'Check if the storage description respects the \' +\n
-            \'constraints provided by the storage designer. (\' +\n
-            e.name + ": " + e.message + \')\'\n
-        );\n
-      }\n
-      if (typeof storage[param.method] !== \'function\') {\n
-        return param.command.reject(\n
-          \'not_implemented\',\n
-          \'method missing\',\n
-          \'Storage "\' + param.storage_spec.type + \'", "\' +\n
-            param.method + \'" method is missing.\'\n
-        );\n
-      }\n
-      setTimeout(function () {\n
-        storage[param.method](\n
-          deepClone(param.command),\n
-          deepClone(param.kwargs),\n
-          deepClone(param.options)\n
-        );\n
-      });\n
+    // date from iso format\n
+    function makeDateFromString(config) {\n
+        var i,\n
+            string = config._i,\n
+            match = isoRegex.exec(string);\n
+\n
+        if (match) {\n
+            config._pf.iso = true;\n
+            for (i = 4; i > 0; i--) {\n
+                if (match[i]) {\n
+                    // match[5] should be "T" or undefined\n
+                    config._f = isoDates[i - 1] + (match[6] || " ");\n
+                    break;\n
+                }\n
+            }\n
+            for (i = 0; i < 4; i++) {\n
+                if (isoTimes[i][1].exec(string)) {\n
+                    config._f += isoTimes[i][0];\n
+                    break;\n
+                }\n
+            }\n
+            if (string.match(parseTokenTimezone)) {\n
+                config._f += "Z";\n
+            }\n
+            makeDateFromStringAndFormat(config);\n
+        }\n
+        else {\n
+            config._d = new Date(string);\n
+        }\n
     }\n
-  }\n
 \n
-  function endAndResolveIfRunning(job, args) {\n
-    if (job.state === \'running\') {\n
-      job.state = \'done\';\n
-      job.modified = new Date();\n
-      shared.emit(\'job:modified\', job);\n
-      if (job.solver) {\n
-        restCommandResolver(job, args);\n
-      }\n
-      shared.emit(\'job:resolved\', job, args);\n
-      shared.emit(\'job:end\', job);\n
+    function makeDateFromInput(config) {\n
+        var input = config._i,\n
+            matched = aspNetJsonRegex.exec(input);\n
+\n
+        if (input === undefined) {\n
+            config._d = new Date();\n
+        } else if (matched) {\n
+            config._d = new Date(+matched[1]);\n
+        } else if (typeof input === \'string\') {\n
+            makeDateFromString(config);\n
+        } else if (isArray(input)) {\n
+            config._a = input.slice(0);\n
+            dateFromConfig(config);\n
+        } else if (isDate(input)) {\n
+            config._d = new Date(+input);\n
+        } else if (typeof(input) === \'object\') {\n
+            dateFromObject(config);\n
+        } else {\n
+            config._d = new Date(input);\n
+        }\n
     }\n
-  }\n
 \n
-  function endAndRejectIfRunning(job, args) {\n
-    if (job.state === \'running\') {\n
-      job.state = \'fail\';\n
-      job.modified = new Date();\n
-      shared.emit(\'job:modified\', job);\n
-      if (job.solver) {\n
-        restCommandRejecter(job, args);\n
-      }\n
-      shared.emit(\'job:rejected\', job, args);\n
-      shared.emit(\'job:end\', job);\n
+    function makeDate(y, m, d, h, M, s, ms) {\n
+        //can\'t just apply() to create a date:\n
+        //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply\n
+        var date = new Date(y, m, d, h, M, s, ms);\n
+\n
+        //the date constructor doesn\'t accept years < 1970\n
+        if (y < 1970) {\n
+            date.setFullYear(y);\n
+        }\n
+        return date;\n
     }\n
-  }\n
 \n
-  function notifyJobIfRunning(job, args) {\n
-    if (job.state === \'running\' && job.solver) {\n
-      job.solver.notify(args[0]);\n
-      shared.emit(\'job:notified\', job, args);\n
+    function makeUTCDate(y) {\n
+        var date = new Date(Date.UTC.apply(null, arguments));\n
+        if (y < 1970) {\n
+            date.setUTCFullYear(y);\n
+        }\n
+        return date;\n
     }\n
-  }\n
 \n
-  // listeners\n
+    function parseWeekday(input, language) {\n
+        if (typeof input === \'string\') {\n
+            if (!isNaN(input)) {\n
+                input = parseInt(input, 10);\n
+            }\n
+            else {\n
+                input = language.weekdaysParse(input);\n
+                if (typeof input !== \'number\') {\n
+                    return null;\n
+                }\n
+            }\n
+        }\n
+        return input;\n
+    }\n
+\n
+    /************************************\n
+        Relative Time\n
+    ************************************/\n
+\n
+\n
+    // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize\n
+    function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) {\n
+        return lang.relativeTime(number || 1, !!withoutSuffix, string, isFuture);\n
+    }\n
+\n
+    function relativeTime(milliseconds, withoutSuffix, lang) {\n
+        var seconds = round(Math.abs(milliseconds) / 1000),\n
+            minutes = round(seconds / 60),\n
+            hours = round(minutes / 60),\n
+            days = round(hours / 24),\n
+            years = round(days / 365),\n
+            args = seconds < 45 && [\'s\', seconds] ||\n
+                minutes === 1 && [\'m\'] ||\n
+                minutes < 45 && [\'mm\', minutes] ||\n
+                hours === 1 && [\'h\'] ||\n
+                hours < 22 && [\'hh\', hours] ||\n
+                days === 1 && [\'d\'] ||\n
+                days <= 25 && [\'dd\', days] ||\n
+                days <= 45 && [\'M\'] ||\n
+                days < 345 && [\'MM\', round(days / 30)] ||\n
+                years === 1 && [\'y\'] || [\'yy\', years];\n
+        args[2] = withoutSuffix;\n
+        args[3] = milliseconds > 0;\n
+        args[4] = lang;\n
+        return substituteTimeAgo.apply({}, args);\n
+    }\n
+\n
+\n
+    /************************************\n
+        Week of Year\n
+    ************************************/\n
+\n
+\n
+    // firstDayOfWeek       0 = sun, 6 = sat\n
+    //                      the day of the week that starts the week\n
+    //                      (usually sunday or monday)\n
+    // firstDayOfWeekOfYear 0 = sun, 6 = sat\n
+    //                      the first week is the week that contains the first\n
+    //                      of this day of the week\n
+    //                      (eg. ISO weeks use thursday (4))\n
+    function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) {\n
+        var end = firstDayOfWeekOfYear - firstDayOfWeek,\n
+            daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(),\n
+            adjustedMoment;\n
+\n
+\n
+        if (daysToDayOfWeek > end) {\n
+            daysToDayOfWeek -= 7;\n
+        }\n
 \n
-  shared.on(\'job:new\', startJobIfReady);\n
-  shared.on(\'job:start\', executeJobIfReady);\n
+        if (daysToDayOfWeek < end - 7) {\n
+            daysToDayOfWeek += 7;\n
+        }\n
 \n
-  shared.on(\'job:resolve\', endAndResolveIfRunning);\n
-  shared.on(\'job:reject\', endAndRejectIfRunning);\n
-  shared.on(\'job:notify\', notifyJobIfRunning);\n
-}\n
+        adjustedMoment = moment(mom).add(\'d\', daysToDayOfWeek);\n
+        return {\n
+            week: Math.ceil(adjustedMoment.dayOfYear() / 7),\n
+            year: adjustedMoment.year()\n
+        };\n
+    }\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */\n
-/*global arrayExtend */\n
-\n
-function enableJobMaker(jio, shared, options) {\n
-\n
-  // dependencies\n
-  // - param.method\n
-  // - param.storage_spec\n
-  // - param.kwargs\n
-  // - param.options\n
-\n
-  // uses (Job)\n
-  // - param.created date\n
-  // - param.modified date\n
-  // - param.tried number >= 0\n
-  // - param.state string \'ready\'\n
-  // - param.method string\n
-  // - param.storage_spec object\n
-  // - param.kwargs object\n
-  // - param.options object\n
-  // - param.command object\n
-\n
-  // list of job events:\n
-  // - Job existence -> new, end\n
-  // - Job execution -> started, stopped\n
-  // - Job resolution -> resolved, rejected, notified, cancelled\n
-  // - Job modification -> modified\n
-\n
-  // emits actions \'job:resolve\', \'job:reject\' and \'job:notify\'\n
-\n
-  // uses `rest method` events\n
-  // emits \'job:new\' event\n
-\n
-  shared.job_keys = arrayExtend(shared.job_keys || [], [\n
-    "created",\n
-    "modified",\n
-    "tried",\n
-    "state",\n
-    "method",\n
-    "storage_spec",\n
-    "kwargs",\n
-    "options"\n
-  ]);\n
-\n
-  function addCommandToJob(job) {\n
-    job.command = {};\n
-    job.command.resolve = function () {\n
-      shared.emit(\'job:resolve\', job, arguments);\n
-    };\n
-    job.command.success = job.command.resolve;\n
-    job.command.reject = function () {\n
-      shared.emit(\'job:reject\', job, arguments);\n
-    };\n
-    job.command.error = job.command.reject;\n
-    job.command.notify = function () {\n
-      shared.emit(\'job:notify\', job, arguments);\n
-    };\n
-    job.command.storage = function () {\n
-      return shared.createRestApi.apply(null, arguments);\n
-    };\n
-    job.command.setCanceller = function (canceller) {\n
-      job.cancellers["command:canceller"] = canceller;\n
-    };\n
-    job.cancellers = job.cancellers || {};\n
-    job.cancellers["job:canceller"] = function () {\n
-      shared.emit("job:reject", job, ["cancelled"]);\n
-    };\n
-  }\n
+    //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday\n
+    function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) {\n
+        // The only solid way to create an iso date from year is to use\n
+        // a string format (Date.UTC handles only years > 1900). Don\'t ask why\n
+        // it doesn\'t need Z at the end.\n
+        var d = new Date(leftZeroFill(year, 6, true) + \'-01-01\').getUTCDay(),\n
+            daysToAdd, dayOfYear;\n
 \n
-  function createJobFromRest(param) {\n
-    if (param.solver) {\n
-      // rest parameters are good\n
-      shared.emit(\'job:new\', param);\n
-    }\n
-  }\n
+        weekday = weekday != null ? weekday : firstDayOfWeek;\n
+        daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0);\n
+        dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1;\n
 \n
-  function initJob(job) {\n
-    job.state = \'ready\';\n
-    if (typeof job.tried !== \'number\' || !isFinite(job.tried)) {\n
-      job.tried = 0;\n
-    }\n
-    if (!job.created) {\n
-      job.created = new Date();\n
+        return {\n
+            year: dayOfYear > 0 ? year : year - 1,\n
+            dayOfYear: dayOfYear > 0 ?  dayOfYear : daysInYear(year - 1) + dayOfYear\n
+        };\n
     }\n
-    addCommandToJob(job);\n
-    job.modified = new Date();\n
-  }\n
-\n
-  // listeners\n
-\n
-  shared.rest_method_names.forEach(function (method) {\n
-    shared.on(method, createJobFromRest);\n
-  });\n
-\n
-  shared.on(\'job:new\', initJob);\n
-\n
-}\n
-\n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */\n
-/*global arrayExtend, localStorage, Workspace, uniqueJSONStringify, JobQueue,\n
-  constants, setTimeout, clearTimeout */\n
 \n
-function enableJobQueue(jio, shared, options) {\n
+    /************************************\n
+        Top Level Functions\n
+    ************************************/\n
 \n
-  // dependencies\n
-  // - shared.storage_spec Object\n
+    function makeMoment(config) {\n
+        var input = config._i,\n
+            format = config._f;\n
 \n
-  // uses\n
-  // - options.workspace Workspace\n
-  // - shared.job_keys String Array\n
-\n
-  // creates\n
-  // - shared.storage_spec_str String\n
-  // - shared.workspace Workspace\n
-  // - shared.job_queue JobQueue\n
+        if (typeof config._pf === \'undefined\') {\n
+            initializeParsingFlags(config);\n
+        }\n
 \n
-  // uses \'job:new\', \'job:started\', \'job:stopped\', \'job:modified\',\n
-  // \'job:notified\', \'job:end\' events\n
+        if (input === null) {\n
+            return moment.invalid({nullInput: true});\n
+        }\n
 \n
-  // emits \'job:end\' event\n
+        if (typeof input === \'string\') {\n
+            config._i = input = getLangDefinition().preparse(input);\n
+        }\n
 \n
-  function postJobIfReady(param) {\n
-    if (!param.stored && param.state === \'ready\') {\n
-      clearTimeout(param.queue_ident);\n
-      delete param.queue_ident;\n
-      shared.job_queue.load();\n
-      shared.job_queue.post(param);\n
-      shared.job_queue.save();\n
-      param.stored = true;\n
-    }\n
-  }\n
+        if (moment.isMoment(input)) {\n
+            config = extend({}, input);\n
 \n
-  function deferredPutJob(param) {\n
-    if (param.queue_ident === undefined) {\n
-      param.queue_ident = setTimeout(function () {\n
-        delete param.queue_ident;\n
-        if (param.stored) {\n
-          shared.job_queue.load();\n
-          shared.job_queue.put(param);\n
-          shared.job_queue.save();\n
+            config._d = new Date(+input._d);\n
+        } else if (format) {\n
+            if (isArray(format)) {\n
+                makeDateFromStringAndArray(config);\n
+            } else {\n
+                makeDateFromStringAndFormat(config);\n
+            }\n
+        } else {\n
+            makeDateFromInput(config);\n
         }\n
-      });\n
-    }\n
-  }\n
-\n
-  function removeJob(param) {\n
-    clearTimeout(param.queue_ident);\n
-    delete param.queue_ident;\n
-    if (param.stored) {\n
-      shared.job_queue.load();\n
-      shared.job_queue.remove(param.id);\n
-      shared.job_queue.save();\n
-      delete param.stored;\n
-      delete param.id;\n
-    }\n
-  }\n
 \n
-  function initJob(param) {\n
-    if (!param.command.end) {\n
-      param.command.end = function () {\n
-        shared.emit(\'job:end\', param);\n
-      };\n
+        return new Moment(config);\n
     }\n
-  }\n
 \n
-  shared.on(\'job:new\', initJob);\n
-\n
-  if (options.job_management !== false) {\n
+    moment = function (input, format, lang, strict) {\n
+        if (typeof(lang) === "boolean") {\n
+            strict = lang;\n
+            lang = undefined;\n
+        }\n
+        return makeMoment({\n
+            _i : input,\n
+            _f : format,\n
+            _l : lang,\n
+            _strict : strict,\n
+            _isUTC : false\n
+        });\n
+    };\n
 \n
-    shared.job_keys = arrayExtend(shared.job_keys || [], ["id"]);\n
+    // creating with utc\n
+    moment.utc = function (input, format, lang, strict) {\n
+        var m;\n
 \n
-    if (typeof options.workspace !== \'object\') {\n
-      shared.workspace = localStorage;\n
-    } else {\n
-      shared.workspace = new Workspace(options.workspace);\n
-    }\n
+        if (typeof(lang) === "boolean") {\n
+            strict = lang;\n
+            lang = undefined;\n
+        }\n
+        m = makeMoment({\n
+            _useUTC : true,\n
+            _isUTC : true,\n
+            _l : lang,\n
+            _i : input,\n
+            _f : format,\n
+            _strict : strict\n
+        }).utc();\n
+\n
+        return m;\n
+    };\n
 \n
-    if (!shared.storage_spec_str) {\n
-      shared.storage_spec_str = uniqueJSONStringify(shared.storage_spec);\n
-    }\n
+    // creating with unix timestamp (in seconds)\n
+    moment.unix = function (input) {\n
+        return moment(input * 1000);\n
+    };\n
 \n
-    shared.job_queue = new JobQueue(\n
-      shared.workspace,\n
-      \'jio/jobs/\' + shared.storage_spec_str,\n
-      shared.job_keys\n
-    );\n
+    // duration\n
+    moment.duration = function (input, key) {\n
+        var duration = input,\n
+            // matching against regexp is expensive, do it on demand\n
+            match = null,\n
+            sign,\n
+            ret,\n
+            parseIso;\n
+\n
+        if (moment.isDuration(input)) {\n
+            duration = {\n
+                ms: input._milliseconds,\n
+                d: input._days,\n
+                M: input._months\n
+            };\n
+        } else if (typeof input === \'number\') {\n
+            duration = {};\n
+            if (key) {\n
+                duration[key] = input;\n
+            } else {\n
+                duration.milliseconds = input;\n
+            }\n
+        } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) {\n
+            sign = (match[1] === "-") ? -1 : 1;\n
+            duration = {\n
+                y: 0,\n
+                d: toInt(match[DATE]) * sign,\n
+                h: toInt(match[HOUR]) * sign,\n
+                m: toInt(match[MINUTE]) * sign,\n
+                s: toInt(match[SECOND]) * sign,\n
+                ms: toInt(match[MILLISECOND]) * sign\n
+            };\n
+        } else if (!!(match = isoDurationRegex.exec(input))) {\n
+            sign = (match[1] === "-") ? -1 : 1;\n
+            parseIso = function (inp) {\n
+                // We\'d normally use ~~inp for this, but unfortunately it also\n
+                // converts floats to ints.\n
+                // inp may be undefined, so careful calling replace on it.\n
+                var res = inp && parseFloat(inp.replace(\',\', \'.\'));\n
+                // apply sign while we\'re at it\n
+                return (isNaN(res) ? 0 : res) * sign;\n
+            };\n
+            duration = {\n
+                y: parseIso(match[2]),\n
+                M: parseIso(match[3]),\n
+                d: parseIso(match[4]),\n
+                h: parseIso(match[5]),\n
+                m: parseIso(match[6]),\n
+                s: parseIso(match[7]),\n
+                w: parseIso(match[8])\n
+            };\n
+        }\n
 \n
-    // Listeners\n
+        ret = new Duration(duration);\n
 \n
-    shared.on(\'job:new\', postJobIfReady);\n
+        if (moment.isDuration(input) && input.hasOwnProperty(\'_lang\')) {\n
+            ret._lang = input._lang;\n
+        }\n
 \n
-    shared.on(\'job:started\', deferredPutJob);\n
-    shared.on(\'job:stopped\', deferredPutJob);\n
-    shared.on(\'job:modified\', deferredPutJob);\n
-    shared.on(\'job:notified\', deferredPutJob);\n
+        return ret;\n
+    };\n
 \n
-    shared.on(\'job:end\', removeJob);\n
+    // version number\n
+    moment.version = VERSION;\n
 \n
-  }\n
+    // default format\n
+    moment.defaultFormat = isoFormat;\n
 \n
-}\n
+    // This function will be called whenever a moment is mutated.\n
+    // It is intended to keep the offset in sync with the timezone.\n
+    moment.updateOffset = function () {};\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */\n
-/*global setTimeout, methodType */\n
+    // This function will load languages and then set the global language.  If\n
+    // no arguments are passed in, it will simply return the current global\n
+    // language key.\n
+    moment.lang = function (key, values) {\n
+        var r;\n
+        if (!key) {\n
+            return moment.fn._lang._abbr;\n
+        }\n
+        if (values) {\n
+            loadLang(normalizeLanguage(key), values);\n
+        } else if (values === null) {\n
+            unloadLang(key);\n
+            key = \'en\';\n
+        } else if (!languages[key]) {\n
+            getLangDefinition(key);\n
+        }\n
+        r = moment.duration.fn._lang = moment.fn._lang = getLangDefinition(key);\n
+        return r._abbr;\n
+    };\n
 \n
-function enableJobRecovery(jio, shared, options) {\n
+    // returns language data\n
+    moment.langData = function (key) {\n
+        if (key && key._lang && key._lang._abbr) {\n
+            key = key._lang._abbr;\n
+        }\n
+        return getLangDefinition(key);\n
+    };\n
 \n
-  // dependencies\n
-  // - JobQueue enabled and before this\n
+    // compare moment object\n
+    moment.isMoment = function (obj) {\n
+        return obj instanceof Moment;\n
+    };\n
 \n
-  // uses\n
-  // - shared.job_queue JobQueue\n
+    // for typechecking Duration objects\n
+    moment.isDuration = function (obj) {\n
+        return obj instanceof Duration;\n
+    };\n
 \n
-  // emits \'job:new\' event\n
+    for (i = lists.length - 1; i >= 0; --i) {\n
+        makeList(lists[i]);\n
+    }\n
 \n
-  function numberOrDefault(number, default_value) {\n
-    return (typeof number === \'number\' &&\n
-            isFinite(number) ? number : default_value);\n
-  }\n
+    moment.normalizeUnits = function (units) {\n
+        return normalizeUnits(units);\n
+    };\n
 \n
-  function recoverJob(param) {\n
-    shared.job_queue.load();\n
-    shared.job_queue.remove(param.id);\n
-    delete param.id;\n
-    if (methodType(param.method) === \'writer\' &&\n
-        (param.state === \'ready\' ||\n
-         param.state === \'running\' ||\n
-         param.state === \'waiting\')) {\n
-      shared.job_queue.save();\n
-      shared.emit(\'job:new\', param);\n
-    }\n
-  }\n
+    moment.invalid = function (flags) {\n
+        var m = moment.utc(NaN);\n
+        if (flags != null) {\n
+            extend(m._pf, flags);\n
+        }\n
+        else {\n
+            m._pf.userInvalidated = true;\n
+        }\n
 \n
-  function jobWaiter(id, modified) {\n
-    return function () {\n
-      var job;\n
-      shared.job_queue.load();\n
-      job = shared.job_queue.get(id);\n
-      if (job && job.modified === modified) {\n
-        // job not modified, no one takes care of it\n
-        recoverJob(job);\n
-      }\n
+        return m;\n
     };\n
-  }\n
 \n
-  var i, job_array, delay, deadline, recovery_delay;\n
+    moment.parseZone = function (input) {\n
+        return moment(input).parseZone();\n
+    };\n
 \n
-  // 1 m 30 s  ===  default firefox request timeout\n
-  recovery_delay = numberOrDefault(options.recovery_delay, 90000);\n
-  if (recovery_delay < 0) {\n
-    recovery_delay = 90000;\n
-  }\n
+    /************************************\n
+        Moment Prototype\n
+    ************************************/\n
 \n
-  if (options.job_management !== false && options.job_recovery !== false) {\n
-\n
-    shared.job_queue.load();\n
-    job_array = shared.job_queue.asArray();\n
-\n
-    for (i = 0; i < job_array.length; i += 1) {\n
-      delay = numberOrDefault(job_array[i].timeout + recovery_delay,\n
-                              recovery_delay);\n
-      deadline = new Date(job_array[i].modified).getTime() + delay;\n
-      if (!isFinite(delay)) {\n
-        // \'modified\' date is broken\n
-        recoverJob(job_array[i]);\n
-      } else if (deadline <= Date.now()) {\n
-        // deadline reached\n
-        recoverJob(job_array[i]);\n
-      } else {\n
-        // deadline not reached yet\n
-        // wait until deadline is reached then check job again\n
-        setTimeout(jobWaiter(job_array[i].id, job_array[i].modified),\n
-                   deadline - Date.now());\n
-      }\n
-    }\n
 \n
-  }\n
-}\n
+    extend(moment.fn = Moment.prototype, {\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, unparam: true */\n
-/*global ReferenceArray */\n
+        clone : function () {\n
+            return moment(this);\n
+        },\n
 \n
-function enableJobReference(jio, shared, options) {\n
+        valueOf : function () {\n
+            return +this._d + ((this._offset || 0) * 60000);\n
+        },\n
 \n
-  // creates\n
-  // - shared.jobs Object Array\n
+        unix : function () {\n
+            return Math.floor(+this / 1000);\n
+        },\n
 \n
-  // uses \'job:new\' and \'job:end\' events\n
+        toString : function () {\n
+            return this.clone().lang(\'en\').format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ");\n
+        },\n
 \n
-  shared.jobs = [];\n
+        toDate : function () {\n
+            return this._offset ? new Date(+this) : this._d;\n
+        },\n
 \n
-  var job_references = new ReferenceArray(shared.jobs);\n
+        toISOString : function () {\n
+            var m = moment(this).utc();\n
+            if (0 < m.year() && m.year() <= 9999) {\n
+                return formatMoment(m, \'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]\');\n
+            } else {\n
+                return formatMoment(m, \'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]\');\n
+            }\n
+        },\n
+\n
+        toArray : function () {\n
+            var m = this;\n
+            return [\n
+                m.year(),\n
+                m.month(),\n
+                m.date(),\n
+                m.hours(),\n
+                m.minutes(),\n
+                m.seconds(),\n
+                m.milliseconds()\n
+            ];\n
+        },\n
+\n
+        isValid : function () {\n
+            return isValid(this);\n
+        },\n
+\n
+        isDSTShifted : function () {\n
+\n
+            if (this._a) {\n
+                return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0;\n
+            }\n
 \n
-  shared.on(\'job:new\', function (param) {\n
-    job_references.put(param);\n
-  });\n
+            return false;\n
+        },\n
+\n
+        parsingFlags : function () {\n
+            return extend({}, this._pf);\n
+        },\n
+\n
+        invalidAt: function () {\n
+            return this._pf.overflow;\n
+        },\n
+\n
+        utc : function () {\n
+            return this.zone(0);\n
+        },\n
+\n
+        local : function () {\n
+            this.zone(0);\n
+            this._isUTC = false;\n
+            return this;\n
+        },\n
+\n
+        format : function (inputString) {\n
+            var output = formatMoment(this, inputString || moment.defaultFormat);\n
+            return this.lang().postformat(output);\n
+        },\n
+\n
+        add : function (input, val) {\n
+            var dur;\n
+            // switch args to support add(\'s\', 1) and add(1, \'s\')\n
+            if (typeof input === \'string\') {\n
+                dur = moment.duration(+val, input);\n
+            } else {\n
+                dur = moment.duration(input, val);\n
+            }\n
+            addOrSubtractDurationFromMoment(this, dur, 1);\n
+            return this;\n
+        },\n
+\n
+        subtract : function (input, val) {\n
+            var dur;\n
+            // switch args to support subtract(\'s\', 1) and subtract(1, \'s\')\n
+            if (typeof input === \'string\') {\n
+                dur = moment.duration(+val, input);\n
+            } else {\n
+                dur = moment.duration(input, val);\n
+            }\n
+            addOrSubtractDurationFromMoment(this, dur, -1);\n
+            return this;\n
+        },\n
+\n
+        diff : function (input, units, asFloat) {\n
+            var that = makeAs(input, this),\n
+                zoneDiff = (this.zone() - that.zone()) * 6e4,\n
+                diff, output;\n
+\n
+            units = normalizeUnits(units);\n
+\n
+            if (units === \'year\' || units === \'month\') {\n
+                // average number of days in the months in the given dates\n
+                diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2\n
+                // difference in months\n
+                output = ((this.year() - that.year()) * 12) + (this.month() - that.month());\n
+                // adjust by taking difference in days, average number of days\n
+                // and dst in the given months.\n
+                output += ((this - moment(this).startOf(\'month\')) -\n
+                        (that - moment(that).startOf(\'month\'))) / diff;\n
+                // same as above but with zones, to negate all dst\n
+                output -= ((this.zone() - moment(this).startOf(\'month\').zone()) -\n
+                        (that.zone() - moment(that).startOf(\'month\').zone())) * 6e4 / diff;\n
+                if (units === \'year\') {\n
+                    output = output / 12;\n
+                }\n
+            } else {\n
+                diff = (this - that);\n
+                output = units === \'second\' ? diff / 1e3 : // 1000\n
+                    units === \'minute\' ? diff / 6e4 : // 1000 * 60\n
+                    units === \'hour\' ? diff / 36e5 : // 1000 * 60 * 60\n
+                    units === \'day\' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst\n
+                    units === \'week\' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst\n
+                    diff;\n
+            }\n
+            return asFloat ? output : absRound(output);\n
+        },\n
+\n
+        from : function (time, withoutSuffix) {\n
+            return moment.duration(this.diff(time)).lang(this.lang()._abbr).humanize(!withoutSuffix);\n
+        },\n
+\n
+        fromNow : function (withoutSuffix) {\n
+            return this.from(moment(), withoutSuffix);\n
+        },\n
+\n
+        calendar : function () {\n
+            // We want to compare the start of today, vs this.\n
+            // Getting start-of-today depends on whether we\'re zone\'d or not.\n
+            var sod = makeAs(moment(), this).startOf(\'day\'),\n
+                diff = this.diff(sod, \'days\', true),\n
+                format = diff < -6 ? \'sameElse\' :\n
+                    diff < -1 ? \'lastWeek\' :\n
+                    diff < 0 ? \'lastDay\' :\n
+                    diff < 1 ? \'sameDay\' :\n
+                    diff < 2 ? \'nextDay\' :\n
+                    diff < 7 ? \'nextWeek\' : \'sameElse\';\n
+            return this.format(this.lang().calendar(format, this));\n
+        },\n
+\n
+        isLeapYear : function () {\n
+            return isLeapYear(this.year());\n
+        },\n
+\n
+        isDST : function () {\n
+            return (this.zone() < this.clone().month(0).zone() ||\n
+                this.zone() < this.clone().month(5).zone());\n
+        },\n
+\n
+        day : function (input) {\n
+            var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();\n
+            if (input != null) {\n
+                input = parseWeekday(input, this.lang());\n
+                return this.add({ d : input - day });\n
+            } else {\n
+                return day;\n
+            }\n
+        },\n
+\n
+        month : function (input) {\n
+            var utc = this._isUTC ? \'UTC\' : \'\',\n
+                dayOfMonth;\n
+\n
+            if (input != null) {\n
+                if (typeof input === \'string\') {\n
+                    input = this.lang().monthsParse(input);\n
+                    if (typeof input !== \'number\') {\n
+                        return this;\n
+                    }\n
+                }\n
+\n
+                dayOfMonth = this.date();\n
+                this.date(1);\n
+                this._d[\'set\' + utc + \'Month\'](input);\n
+                this.date(Math.min(dayOfMonth, this.daysInMonth()));\n
+\n
+                moment.updateOffset(this);\n
+                return this;\n
+            } else {\n
+                return this._d[\'get\' + utc + \'Month\']();\n
+            }\n
+        },\n
+\n
+        startOf: function (units) {\n
+            units = normalizeUnits(units);\n
+            // the following switch intentionally omits break keywords\n
+            // to utilize falling through the cases.\n
+            switch (units) {\n
+            case \'year\':\n
+                this.month(0);\n
+                /* falls through */\n
+            case \'month\':\n
+                this.date(1);\n
+                /* falls through */\n
+            case \'week\':\n
+            case \'isoWeek\':\n
+            case \'day\':\n
+                this.hours(0);\n
+                /* falls through */\n
+            case \'hour\':\n
+                this.minutes(0);\n
+                /* falls through */\n
+            case \'minute\':\n
+                this.seconds(0);\n
+                /* falls through */\n
+            case \'second\':\n
+                this.milliseconds(0);\n
+                /* falls through */\n
+            }\n
 \n
-  shared.on(\'job:end\', function (param) {\n
-    job_references.remove(param);\n
-  });\n
-}\n
+            // weeks are a special case\n
+            if (units === \'week\') {\n
+                this.weekday(0);\n
+            } else if (units === \'isoWeek\') {\n
+                this.isoWeekday(1);\n
+            }\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */\n
-/*global arrayExtend, setTimeout, methodType, constants */\n
-\n
-function enableJobRetry(jio, shared, options) {\n
-\n
-  // dependencies\n
-  // - param.method\n
-  // - param.storage_spec\n
-  // - param.kwargs\n
-  // - param.options\n
-  // - param.command\n
-\n
-  // uses\n
-  // - options.default_writers_max_retry number >= 0 or null\n
-  // - options.default_readers_max_retry number >= 0 or null\n
-  // - options.default_max_retry number >= 0 or null\n
-  // - options.writers_max_retry number >= 0 or null\n
-  // - options.readers_max_retry number >= 0 or null\n
-  // - options.max_retry number >= 0 or null\n
-  // - param.modified date\n
-  // - param.tried number >= 0\n
-  // - param.max_retry >= 0 or undefined\n
-  // - param.state string \'ready\' \'waiting\'\n
-  // - param.method string\n
-  // - param.storage_spec object\n
-  // - param.kwargs object\n
-  // - param.options object\n
-  // - param.command object\n
-\n
-  // uses \'job:new\' and \'job:retry\' events\n
-  // emits action \'job:start\' event\n
-  // emits \'job:retry\', \'job:reject\', \'job:modified\' and \'job:stopped\' events\n
-\n
-  shared.job_keys = arrayExtend(shared.job_keys || [], ["max_retry"]);\n
-\n
-  var writers_max_retry, readers_max_retry, max_retry;\n
-\n
-  function defaultMaxRetry(param) {\n
-    if (methodType(param.method) === \'writers\') {\n
-      if (max_retry === undefined) {\n
-        return writers_max_retry;\n
-      }\n
-      return max_retry;\n
-    }\n
-    if (max_retry === undefined) {\n
-      return readers_max_retry;\n
-    }\n
-    return max_retry;\n
-  }\n
+            return this;\n
+        },\n
+\n
+        endOf: function (units) {\n
+            units = normalizeUnits(units);\n
+            return this.startOf(units).add((units === \'isoWeek\' ? \'week\' : units), 1).subtract(\'ms\', 1);\n
+        },\n
+\n
+        isAfter: function (input, units) {\n
+            units = typeof units !== \'undefined\' ? units : \'millisecond\';\n
+            return +this.clone().startOf(units) > +moment(input).startOf(units);\n
+        },\n
+\n
+        isBefore: function (input, units) {\n
+            units = typeof units !== \'undefined\' ? units : \'millisecond\';\n
+            return +this.clone().startOf(units) < +moment(input).startOf(units);\n
+        },\n
+\n
+        isSame: function (input, units) {\n
+            units = units || \'ms\';\n
+            return +this.clone().startOf(units) === +makeAs(input, this).startOf(units);\n
+        },\n
+\n
+        min: function (other) {\n
+            other = moment.apply(null, arguments);\n
+            return other < this ? this : other;\n
+        },\n
+\n
+        max: function (other) {\n
+            other = moment.apply(null, arguments);\n
+            return other > this ? this : other;\n
+        },\n
+\n
+        zone : function (input) {\n
+            var offset = this._offset || 0;\n
+            if (input != null) {\n
+                if (typeof input === "string") {\n
+                    input = timezoneMinutesFromString(input);\n
+                }\n
+                if (Math.abs(input) < 16) {\n
+                    input = input * 60;\n
+                }\n
+                this._offset = input;\n
+                this._isUTC = true;\n
+                if (offset !== input) {\n
+                    addOrSubtractDurationFromMoment(this, moment.duration(offset - input, \'m\'), 1, true);\n
+                }\n
+            } else {\n
+                return this._isUTC ? offset : this._d.getTimezoneOffset();\n
+            }\n
+            return this;\n
+        },\n
+\n
+        zoneAbbr : function () {\n
+            return this._isUTC ? "UTC" : "";\n
+        },\n
+\n
+        zoneName : function () {\n
+            return this._isUTC ? "Coordinated Universal Time" : "";\n
+        },\n
+\n
+        parseZone : function () {\n
+            if (this._tzm) {\n
+                this.zone(this._tzm);\n
+            } else if (typeof this._i === \'string\') {\n
+                this.zone(this._i);\n
+            }\n
+            return this;\n
+        },\n
 \n
-  function positiveNumberOrDefault(number, default_value) {\n
-    return (typeof number === \'number\' &&\n
-            number >= 0 ?\n
-            number : default_value);\n
-  }\n
+        hasAlignedHourOffset : function (input) {\n
+            if (!input) {\n
+                input = 0;\n
+            }\n
+            else {\n
+                input = moment(input).zone();\n
+            }\n
 \n
-  function positiveNumberNullOrDefault(number, default_value) {\n
-    return ((typeof number === \'number\' &&\n
-            number >= 0) || number === null ?\n
-            number : default_value);\n
-  }\n
+            return (this.zone() - input) % 60 === 0;\n
+        },\n
+\n
+        daysInMonth : function () {\n
+            return daysInMonth(this.year(), this.month());\n
+        },\n
+\n
+        dayOfYear : function (input) {\n
+            var dayOfYear = round((moment(this).startOf(\'day\') - moment(this).startOf(\'year\')) / 864e5) + 1;\n
+            return input == null ? dayOfYear : this.add("d", (input - dayOfYear));\n
+        },\n
+\n
+        quarter : function () {\n
+            return Math.ceil((this.month() + 1.0) / 3.0);\n
+        },\n
+\n
+        weekYear : function (input) {\n
+            var year = weekOfYear(this, this.lang()._week.dow, this.lang()._week.doy).year;\n
+            return input == null ? year : this.add("y", (input - year));\n
+        },\n
+\n
+        isoWeekYear : function (input) {\n
+            var year = weekOfYear(this, 1, 4).year;\n
+            return input == null ? year : this.add("y", (input - year));\n
+        },\n
+\n
+        week : function (input) {\n
+            var week = this.lang().week(this);\n
+            return input == null ? week : this.add("d", (input - week) * 7);\n
+        },\n
+\n
+        isoWeek : function (input) {\n
+            var week = weekOfYear(this, 1, 4).week;\n
+            return input == null ? week : this.add("d", (input - week) * 7);\n
+        },\n
+\n
+        weekday : function (input) {\n
+            var weekday = (this.day() + 7 - this.lang()._week.dow) % 7;\n
+            return input == null ? weekday : this.add("d", input - weekday);\n
+        },\n
+\n
+        isoWeekday : function (input) {\n
+            // behaves the same as moment#day except\n
+            // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)\n
+            // as a setter, sunday should belong to the previous week.\n
+            return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7);\n
+        },\n
+\n
+        get : function (units) {\n
+            units = normalizeUnits(units);\n
+            return this[units]();\n
+        },\n
+\n
+        set : function (units, value) {\n
+            units = normalizeUnits(units);\n
+            if (typeof this[units] === \'function\') {\n
+                this[units](value);\n
+            }\n
+            return this;\n
+        },\n
+\n
+        // If passed a language key, it will set the language for this\n
+        // instance.  Otherwise, it will return the language configuration\n
+        // variables for this instance.\n
+        lang : function (key) {\n
+            if (key === undefined) {\n
+                return this._lang;\n
+            } else {\n
+                this._lang = getLangDefinition(key);\n
+                return this;\n
+            }\n
+        }\n
+    });\n
 \n
-  max_retry = positiveNumberNullOrDefault(\n
-    options.max_retry || options.default_max_retry,\n
-    undefined\n
-  );\n
-  writers_max_retry = positiveNumberNullOrDefault(\n
-    options.writers_max_retry || options.default_writers_max_retry,\n
-    null\n
-  );\n
-  readers_max_retry = positiveNumberNullOrDefault(\n
-    options.readers_max_retry || options.default_readers_max_retry,\n
-    2\n
-  );\n
-\n
-  function initJob(param) {\n
-    if (typeof param.max_retry !== \'number\' || param.max_retry < 0) {\n
-      param.max_retry = positiveNumberOrDefault(\n
-        param.options.max_retry,\n
-        defaultMaxRetry(param)\n
-      );\n
+    // helper for adding shortcuts\n
+    function makeGetterAndSetter(name, key) {\n
+        moment.fn[name] = moment.fn[name + \'s\'] = function (input) {\n
+            var utc = this._isUTC ? \'UTC\' : \'\';\n
+            if (input != null) {\n
+                this._d[\'set\' + utc + key](input);\n
+                moment.updateOffset(this);\n
+                return this;\n
+            } else {\n
+                return this._d[\'get\' + utc + key]();\n
+            }\n
+        };\n
     }\n
-    param.command.reject = function (status) {\n
-      if (constants.http_action[status || 0] === "retry") {\n
-        shared.emit(\'job:retry\', param, arguments);\n
-      } else {\n
-        shared.emit(\'job:reject\', param, arguments);\n
-      }\n
-    };\n
-    param.command.retry = function () {\n
-      shared.emit(\'job:retry\', param, arguments);\n
-    };\n
-  }\n
 \n
-  function retryIfRunning(param, args) {\n
-    if (param.state === \'running\') {\n
-      if (param.max_retry === undefined ||\n
-          param.max_retry === null ||\n
-          param.max_retry >= param.tried) {\n
-        param.state = \'waiting\';\n
-        param.modified = new Date();\n
-        shared.emit(\'job:modified\', param);\n
-        shared.emit(\'job:stopped\', param);\n
-        setTimeout(function () {\n
-          param.state = \'ready\';\n
-          param.modified = new Date();\n
-          shared.emit(\'job:modified\', param);\n
-          shared.emit(\'job:start\', param);\n
-        }, Math.min(10000, param.tried * 2000));\n
-      } else {\n
-        shared.emit(\'job:reject\', param, args);\n
-      }\n
+    // loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds)\n
+    for (i = 0; i < proxyGettersAndSetters.length; i ++) {\n
+        makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase().replace(/s$/, \'\'), proxyGettersAndSetters[i]);\n
     }\n
-  }\n
 \n
-  // listeners\n
+    // add shortcut for year (uses different syntax than the getter/setter \'year\' == \'FullYear\')\n
+    makeGetterAndSetter(\'year\', \'FullYear\');\n
 \n
-  shared.on(\'job:new\', initJob);\n
+    // add plural methods\n
+    moment.fn.days = moment.fn.day;\n
+    moment.fn.months = moment.fn.month;\n
+    moment.fn.weeks = moment.fn.week;\n
+    moment.fn.isoWeeks = moment.fn.isoWeek;\n
 \n
-  shared.on(\'job:retry\', retryIfRunning);\n
-}\n
+    // add aliased format methods\n
+    moment.fn.toJSON = moment.fn.toISOString;\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */\n
-/*global arrayExtend, setTimeout, clearTimeout */\n
+    /************************************\n
+        Duration Prototype\n
+    ************************************/\n
 \n
-function enableJobTimeout(jio, shared, options) {\n
 \n
-  // dependencies\n
-  // - param.tried number > 0\n
-  // - param.state string \'running\'\n
+    extend(moment.duration.fn = Duration.prototype, {\n
 \n
-  // uses\n
-  // - param.tried number > 0\n
-  // - param.timeout number >= 0\n
-  // - param.timeout_ident Timeout\n
-  // - param.state string \'running\'\n
+        _bubble : function () {\n
+            var milliseconds = this._milliseconds,\n
+                days = this._days,\n
+                months = this._months,\n
+                data = this._data,\n
+                seconds, minutes, hours, years;\n
 \n
-  // uses \'job:new\', \'job:stopped\', \'job:started\',\n
-  // \'job:notified\' and \'job:end\' events\n
-  // emits \'job:modified\' event\n
+            // The following code bubbles up values, see the tests for\n
+            // examples of what that means.\n
+            data.milliseconds = milliseconds % 1000;\n
 \n
-  shared.job_keys = arrayExtend(shared.job_keys || [], ["timeout"]);\n
+            seconds = absRound(milliseconds / 1000);\n
+            data.seconds = seconds % 60;\n
 \n
-  function positiveNumberOrDefault(number, default_value) {\n
-    return (typeof number === \'number\' &&\n
-            number >= 0 ?\n
-            number : default_value);\n
-  }\n
+            minutes = absRound(seconds / 60);\n
+            data.minutes = minutes % 60;\n
 \n
-  // Infinity by default\n
-  var default_timeout = positiveNumberOrDefault(options.default_timeout, 0);\n
-\n
-  function timeoutReject(param) {\n
-    return function () {\n
-      param.command.reject(\n
-        \'request_timeout\',\n
-        \'timeout\',\n
-        \'Operation canceled after around \' + (\n
-          Date.now() - param.modified.getTime()\n
-        ) + \' milliseconds of inactivity.\'\n
-      );\n
-    };\n
-  }\n
+            hours = absRound(minutes / 60);\n
+            data.hours = hours % 24;\n
 \n
-  function initJob(job) {\n
-    if (typeof job.timeout !== \'number\' || !isFinite(job.timeout) ||\n
-        job.timeout < 0) {\n
-      job.timeout = positiveNumberOrDefault(\n
-        job.options.timeout,\n
-        default_timeout\n
-      );\n
-    }\n
-    job.modified = new Date();\n
-    shared.emit(\'job:modified\', job);\n
-  }\n
+            days += absRound(hours / 24);\n
+            data.days = days % 30;\n
 \n
-  function clearJobTimeout(job) {\n
-    clearTimeout(job.timeout_ident);\n
-    delete job.timeout_ident;\n
-  }\n
+            months += absRound(days / 30);\n
+            data.months = months % 12;\n
 \n
-  function restartJobTimeoutIfRunning(job) {\n
-    clearTimeout(job.timeout_ident);\n
-    if (job.state === \'running\' && job.timeout > 0) {\n
-      job.timeout_ident = setTimeout(timeoutReject(job), job.timeout);\n
-      job.modified = new Date();\n
-    } else {\n
-      delete job.timeout_ident;\n
-    }\n
-  }\n
+            years = absRound(months / 12);\n
+            data.years = years;\n
+        },\n
 \n
-  // listeners\n
+        weeks : function () {\n
+            return absRound(this.days() / 7);\n
+        },\n
 \n
-  shared.on(\'job:new\', initJob);\n
+        valueOf : function () {\n
+            return this._milliseconds +\n
+              this._days * 864e5 +\n
+              (this._months % 12) * 2592e6 +\n
+              toInt(this._months / 12) * 31536e6;\n
+        },\n
 \n
-  shared.on("job:stopped", clearJobTimeout);\n
-  shared.on("job:end", clearJobTimeout);\n
+        humanize : function (withSuffix) {\n
+            var difference = +this,\n
+                output = relativeTime(difference, !withSuffix, this.lang());\n
 \n
-  shared.on("job:started", restartJobTimeoutIfRunning);\n
-  shared.on("job:notified", restartJobTimeoutIfRunning);\n
-}\n
+            if (withSuffix) {\n
+                output = this.lang().pastFuture(difference, output);\n
+            }\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true */\n
-/*global arrayValuesToTypeDict, dictClear, RSVP, deepClone */\n
-\n
-// adds methods to JIO\n
-// - post\n
-// - put\n
-// - get\n
-// - remove\n
-// - allDocs\n
-// - putAttachment\n
-// - getAttachment\n
-// - removeAttachment\n
-// - check\n
-// - repair\n
-\n
-// event shared objet\n
-// - storage_spec object\n
-// - method string\n
-// - kwargs object\n
-// - options object\n
-// - solver object\n
-// - solver.resolve function\n
-// - solver.reject function\n
-// - solver.notify function\n
-// - cancellers object\n
-// - promise object\n
-\n
-function enableRestAPI(jio, shared) { // (jio, shared, options)\n
-\n
-  shared.rest_method_names = [\n
-    "post",\n
-    "put",\n
-    "get",\n
-    "remove",\n
-    "allDocs",\n
-    "putAttachment",\n
-    "getAttachment",\n
-    "removeAttachment",\n
-    "check",\n
-    "repair"\n
-  ];\n
-\n
-  function prepareParamAndEmit(method, storage_spec, args) {\n
-    var callback, type_dict, param = {};\n
-    type_dict = arrayValuesToTypeDict(Array.prototype.slice.call(args));\n
-    type_dict.object = type_dict.object || [];\n
-    if (method !== \'allDocs\') {\n
-      param.kwargs = type_dict.object.shift();\n
-      if (param.kwargs === undefined) {\n
-        throw new TypeError("JIO()." + method +\n
-                            "(): Argument 1 is not of type \'object\'");\n
-      }\n
-      param.kwargs = deepClone(param.kwargs);\n
-    } else {\n
-      param.kwargs = {};\n
-    }\n
-    param.solver = {};\n
-    param.options = deepClone(type_dict.object.shift()) || {};\n
-    param.promise = new RSVP.Promise(function (resolve, reject, notify) {\n
-      param.solver.resolve = resolve;\n
-      param.solver.reject = reject;\n
-      param.solver.notify = notify;\n
-    }, function () {\n
-      if (!param.cancellers) { return; }\n
-      var k;\n
-      for (k in param.cancellers) {\n
-        if (param.cancellers.hasOwnProperty(k)) {\n
-          param.cancellers[k]();\n
-        }\n
-      }\n
-    });\n
-    type_dict[\'function\'] = type_dict[\'function\'] || [];\n
-    if (type_dict[\'function\'].length === 1) {\n
-      callback = type_dict[\'function\'][0];\n
-      param.promise.then(function (answer) {\n
-        callback(undefined, answer);\n
-      }, function (answer) {\n
-        callback(answer, undefined);\n
-      });\n
-    } else if (type_dict[\'function\'].length > 1) {\n
-      param.promise.then(type_dict[\'function\'][0],\n
-                         type_dict[\'function\'][1],\n
-                         type_dict[\'function\'][2]);\n
-    }\n
-    type_dict = dictClear(type_dict);\n
-    param.storage_spec = storage_spec;\n
-    param.method = method;\n
-    shared.emit(method, param);\n
-    return param.promise;\n
-  }\n
+            return this.lang().postformat(output);\n
+        },\n
 \n
-  shared.createRestApi = function (storage_spec, that) {\n
-    if (that === undefined) {\n
-      that = {};\n
-    }\n
-    shared.rest_method_names.forEach(function (method) {\n
-      that[method] = function () {\n
-        return prepareParamAndEmit(method, storage_spec, arguments);\n
-      };\n
-    });\n
-    return that;\n
-  };\n
+        add : function (input, val) {\n
+            // supports only 2.0-style add(1, \'s\') or add(moment)\n
+            var dur = moment.duration(input, val);\n
 \n
-  shared.createRestApi(shared.storage_spec, jio);\n
-}\n
+            this._milliseconds += dur._milliseconds;\n
+            this._days += dur._days;\n
+            this._months += dur._months;\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */\n
-/*global Blob, restCommandRejecter, Metadata */\n
+            this._bubble();\n
 \n
-function enableRestParamChecker(jio, shared) {\n
+            return this;\n
+        },\n
 \n
-  // dependencies\n
-  // - param.solver\n
-  // - param.kwargs\n
+        subtract : function (input, val) {\n
+            var dur = moment.duration(input, val);\n
 \n
-  // checks the kwargs and convert value if necessary\n
+            this._milliseconds -= dur._milliseconds;\n
+            this._days -= dur._days;\n
+            this._months -= dur._months;\n
 \n
-  // which is a dict of method to use to announce that\n
-  // the command is finished\n
+            this._bubble();\n
 \n
+            return this;\n
+        },\n
 \n
-  // tools\n
+        get : function (units) {\n
+            units = normalizeUnits(units);\n
+            return this[units.toLowerCase() + \'s\']();\n
+        },\n
 \n
-  function checkId(param) {\n
-    if (typeof param.kwargs._id !== \'string\' || param.kwargs._id === \'\') {\n
-      restCommandRejecter(param, [\n
-        \'bad_request\',\n
-        \'wrong document id\',\n
-        \'Document id must be a non empty string.\'\n
-      ]);\n
-      delete param.solver;\n
-      return false;\n
-    }\n
-    return true;\n
-  }\n
+        as : function (units) {\n
+            units = normalizeUnits(units);\n
+            return this[\'as\' + units.charAt(0).toUpperCase() + units.slice(1) + \'s\']();\n
+        },\n
 \n
-  function checkAttachmentId(param) {\n
-    if (typeof param.kwargs._attachment !== \'string\' ||\n
-        param.kwargs._attachment === \'\') {\n
-      restCommandRejecter(param, [\n
-        \'bad_request\',\n
-        \'wrong attachment id\',\n
-        \'Attachment id must be a non empty string.\'\n
-      ]);\n
-      delete param.solver;\n
-      return false;\n
-    }\n
-    return true;\n
-  }\n
+        lang : moment.fn.lang,\n
 \n
-  // listeners\n
+        toIsoString : function () {\n
+            // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js\n
+            var years = Math.abs(this.years()),\n
+                months = Math.abs(this.months()),\n
+                days = Math.abs(this.days()),\n
+                hours = Math.abs(this.hours()),\n
+                minutes = Math.abs(this.minutes()),\n
+                seconds = Math.abs(this.seconds() + this.milliseconds() / 1000);\n
 \n
-  shared.on(\'post\', function (param) {\n
-    if (param.kwargs._id !== undefined) {\n
-      if (!checkId(param)) {\n
-        return;\n
-      }\n
-    }\n
-    new Metadata(param.kwargs).format();\n
-  });\n
+            if (!this.asSeconds()) {\n
+                // this is the same as C#\'s (Noda) and python (isodate)...\n
+                // but not other JS (goog.date)\n
+                return \'P0D\';\n
+            }\n
 \n
-  ["put", "get", "remove"].forEach(function (method) {\n
-    shared.on(method, function (param) {\n
-      if (!checkId(param)) {\n
-        return;\n
-      }\n
-      new Metadata(param.kwargs).format();\n
+            return (this.asSeconds() < 0 ? \'-\' : \'\') +\n
+                \'P\' +\n
+                (years ? years + \'Y\' : \'\') +\n
+                (months ? months + \'M\' : \'\') +\n
+                (days ? days + \'D\' : \'\') +\n
+                ((hours || minutes || seconds) ? \'T\' : \'\') +\n
+                (hours ? hours + \'H\' : \'\') +\n
+                (minutes ? minutes + \'M\' : \'\') +\n
+                (seconds ? seconds + \'S\' : \'\');\n
+        }\n
     });\n
-  });\n
 \n
-  shared.on(\'putAttachment\', function (param) {\n
-    if (!checkId(param) || !checkAttachmentId(param)) {\n
-      return;\n
-    }\n
-    if (!(param.kwargs._blob instanceof Blob) &&\n
-        typeof param.kwargs._data === \'string\') {\n
-      param.kwargs._blob = new Blob([param.kwargs._data], {\n
-        "type": param.kwargs._content_type || param.kwargs._mimetype || ""\n
-      });\n
-      delete param.kwargs._data;\n
-      delete param.kwargs._mimetype;\n
-      delete param.kwargs._content_type;\n
-    } else if (param.kwargs._blob instanceof Blob) {\n
-      delete param.kwargs._data;\n
-      delete param.kwargs._mimetype;\n
-      delete param.kwargs._content_type;\n
-    } else if (param.kwargs._data instanceof Blob) {\n
-      param.kwargs._blob = param.kwargs._data;\n
-      delete param.kwargs._data;\n
-      delete param.kwargs._mimetype;\n
-      delete param.kwargs._content_type;\n
-    } else {\n
-      restCommandRejecter(param, [\n
-        \'bad_request\',\n
-        \'wrong attachment\',\n
-        \'Attachment information must be like {"_id": document id, \' +\n
-          \'"_attachment": attachment name, "_data": string, ["_mimetype": \' +\n
-          \'content type]} or {"_id": document id, "_attachment": \' +\n
-          \'attachment name, "_blob": Blob}\'\n
-      ]);\n
-      delete param.solver;\n
+    function makeDurationGetter(name) {\n
+        moment.duration.fn[name] = function () {\n
+            return this._data[name];\n
+        };\n
     }\n
-  });\n
 \n
-  ["getAttachment", "removeAttachment"].forEach(function (method) {\n
-    shared.on(method, function (param) {\n
-      if (!checkId(param)) {\n
-        checkAttachmentId(param);\n
-      }\n
-    });\n
-  });\n
+    function makeDurationAsGetter(name, factor) {\n
+        moment.duration.fn[\'as\' + name] = function () {\n
+            return +this / factor;\n
+        };\n
+    }\n
 \n
-  ["check", "repair"].forEach(function (method) {\n
-    shared.on(method, function (param) {\n
-      if (param.kwargs._id !== undefined) {\n
-        if (!checkId(param)) {\n
-          return;\n
+    for (i in unitMillisecondFactors) {\n
+        if (unitMillisecondFactors.hasOwnProperty(i)) {\n
+            makeDurationAsGetter(i, unitMillisecondFactors[i]);\n
+            makeDurationGetter(i.toLowerCase());\n
         }\n
-      }\n
-    });\n
-  });\n
-\n
-}\n
-\n
-/*jslint indent: 2, maxlen: 80, sloppy: true */\n
-\n
-var query_class_dict = {};\n
+    }\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */\n
-/*global parseStringToObject: true, emptyFunction: true, sortOn: true, limit:\n
-  true, select: true, exports, stringEscapeRegexpCharacters: true,\n
-  deepClone, RSVP, sequence */\n
+    makeDurationAsGetter(\'Weeks\', 6048e5);\n
+    moment.duration.fn.asMonths = function () {\n
+        return (+this - this.years() * 31536e6) / 2592e6 + this.years() * 12;\n
+    };\n
 \n
-/**\n
- * The query to use to filter a list of objects.\n
- * This is an abstract class.\n
- *\n
- * @class Query\n
- * @constructor\n
- */\n
-function Query() {\n
 \n
-  /**\n
-   * Called before parsing the query. Must be overridden!\n
-   *\n
-   * @method onParseStart\n
-   * @param  {Object} object The object shared in the parse process\n
-   * @param  {Object} option Some option gave in parse()\n
-   */\n
-  this.onParseStart = emptyFunction;\n
+    /************************************\n
+        Default Lang\n
+    ************************************/\n
 \n
-  /**\n
-   * Called when parsing a simple query. Must be overridden!\n
-   *\n
-   * @method onParseSimpleQuery\n
-   * @param  {Object} object The object shared in the parse process\n
-   * @param  {Object} option Some option gave in parse()\n
-   */\n
-  this.onParseSimpleQuery = emptyFunction;\n
 \n
-  /**\n
-   * Called when parsing a complex query. Must be overridden!\n
-   *\n
-   * @method onParseComplexQuery\n
-   * @param  {Object} object The object shared in the parse process\n
-   * @param  {Object} option Some option gave in parse()\n
-   */\n
-  this.onParseComplexQuery = emptyFunction;\n
+    // Set default language, other languages will inherit from English.\n
+    moment.lang(\'en\', {\n
+        ordinal : function (number) {\n
+            var b = number % 10,\n
+                output = (toInt(number % 100 / 10) === 1) ? \'th\' :\n
+                (b === 1) ? \'st\' :\n
+                (b === 2) ? \'nd\' :\n
+                (b === 3) ? \'rd\' : \'th\';\n
+            return number + output;\n
+        }\n
+    });\n
 \n
-  /**\n
-   * Called after parsing the query. Must be overridden!\n
-   *\n
-   * @method onParseEnd\n
-   * @param  {Object} object The object shared in the parse process\n
-   * @param  {Object} option Some option gave in parse()\n
-   */\n
-  this.onParseEnd = emptyFunction;\n
+    /* EMBED_LANGUAGES */\n
 \n
-}\n
+    /************************************\n
+        Exposing Moment\n
+    ************************************/\n
 \n
-/**\n
- * Filter the item list with matching item only\n
- *\n
- * @method exec\n
- * @param  {Array} item_list The list of object\n
- * @param  {Object} [option] Some operation option\n
- * @param  {Array} [option.select_list] A object keys to retrieve\n
- * @param  {Array} [option.sort_on] Couples of object keys and "ascending"\n
- *                 or "descending"\n
- * @param  {Array} [option.limit] Couple of integer, first is an index and\n
- *                 second is the length.\n
- */\n
-Query.prototype.exec = function (item_list, option) {\n
-  var i, promises = [];\n
-  if (!Array.isArray(item_list)) {\n
-    throw new TypeError("Query().exec(): Argument 1 is not of type \'array\'");\n
-  }\n
-  if (option === undefined) {\n
-    option = {};\n
-  }\n
-  if (typeof option !== \'object\') {\n
-    throw new TypeError("Query().exec(): " +\n
-                        "Optional argument 2 is not of type \'object\'");\n
-  }\n
-  for (i = 0; i < item_list.length; i += 1) {\n
-    if (!item_list[i]) {\n
-      promises.push(RSVP.resolve(false));\n
-    } else {\n
-      promises.push(this.match(item_list[i]));\n
-    }\n
-  }\n
-  return sequence([function () {\n
-    return RSVP.all(promises);\n
-  }, function (answers) {\n
-    var j;\n
-    for (j = answers.length - 1; j >= 0; j -= 1) {\n
-      if (!answers[j]) {\n
-        item_list.splice(j, 1);\n
-      }\n
-    }\n
-    if (option.sort_on) {\n
-      return sortOn(option.sort_on, item_list);\n
-    }\n
-  }, function () {\n
-    if (option.limit) {\n
-      return limit(option.limit, item_list);\n
+    function makeGlobal(deprecate) {\n
+        var warned = false, local_moment = moment;\n
+        /*global ender:false */\n
+        if (typeof ender !== \'undefined\') {\n
+            return;\n
+        }\n
+        // here, `this` means `window` in the browser, or `global` on the server\n
+        // add `moment` as a global object via a string identifier,\n
+        // for Closure Compiler "advanced" mode\n
+        if (deprecate) {\n
+            global.moment = function () {\n
+                if (!warned && console && console.warn) {\n
+                    warned = true;\n
+                    console.warn(\n
+                            "Accessing Moment through the global scope is " +\n
+                            "deprecated, and will be removed in an upcoming " +\n
+                            "release.");\n
+                }\n
+                return local_moment.apply(null, arguments);\n
+            };\n
+            extend(global.moment, local_moment);\n
+        } else {\n
+            global[\'moment\'] = moment;\n
+        }\n
     }\n
-  }, function () {\n
-    return select(option.select_list || [], item_list);\n
-  }, function () {\n
-    return item_list;\n
-  }]);\n
-};\n
-\n
-/**\n
- * Test if an item matches this query\n
- *\n
- * @method match\n
- * @param  {Object} item The object to test\n
- * @return {Boolean} true if match, false otherwise\n
- */\n
-Query.prototype.match = function () {\n
-  return RSVP.resolve(true);\n
-};\n
 \n
+    // CommonJS module is defined\n
+    if (hasModule) {\n
+        module.exports = moment;\n
+        makeGlobal(true);\n
+    } else if (typeof define === "function" && define.amd) {\n
+        define("moment", function (require, exports, module) {\n
+            if (module.config && module.config() && module.config().noGlobal !== true) {\n
+                // If user provided noGlobal, he is aware of global\n
+                makeGlobal(module.config().noGlobal === undefined);\n
+            }\n
 \n
-/**\n
- * Browse the Query in deep calling parser method in each step.\n
- *\n
- * `onParseStart` is called first, on end `onParseEnd` is called.\n
- * It starts from the simple queries at the bottom of the tree calling the\n
- * parser method `onParseSimpleQuery`, and go up calling the\n
- * `onParseComplexQuery` method.\n
- *\n
- * @method parse\n
- * @param  {Object} option Any options you want (except \'parsed\')\n
- * @return {Any} The parse result\n
- */\n
-Query.prototype.parse = function (option) {\n
-  var that = this, object;\n
-  /**\n
-   * The recursive parser.\n
-   *\n
-   * @param  {Object} object The object shared in the parse process\n
-   * @param  {Object} options Some options usable in the parseMethods\n
-   * @return {Any} The parser result\n
-   */\n
-  function recParse(object, option) {\n
-    var query = object.parsed;\n
-    if (query.type === "complex") {\n
-      return sequence([function () {\n
-        return sequence(query.query_list.map(function (v, i) {\n
-          /*jslint unparam: true */\n
-          return function () {\n
-            return sequence([function () {\n
-              object.parsed = query.query_list[i];\n
-              return recParse(object, option);\n
-            }, function () {\n
-              query.query_list[i] = object.parsed;\n
-            }]);\n
-          };\n
-        }));\n
-      }, function () {\n
-        object.parsed = query;\n
-        return that.onParseComplexQuery(object, option);\n
-      }]);\n
-    }\n
-    if (query.type === "simple") {\n
-      return that.onParseSimpleQuery(object, option);\n
+            return moment;\n
+        });\n
+    } else {\n
+        makeGlobal();\n
     }\n
-  }\n
-  object = {"parsed": JSON.parse(JSON.stringify(that.serialized()))};\n
-  return sequence([function () {\n
-    return that.onParseStart(object, option);\n
-  }, function () {\n
-    return recParse(object, option);\n
-  }, function () {\n
-    return that.onParseEnd(object, option);\n
-  }, function () {\n
-    return object.parsed;\n
-  }]);\n
-};\n
-\n
-/**\n
- * Convert this query to a parsable string.\n
- *\n
- * @method toString\n
- * @return {String} The string version of this query\n
- */\n
-Query.prototype.toString = function () {\n
-  return "";\n
-};\n
-\n
-/**\n
- * Convert this query to an jsonable object in order to be remake thanks to\n
- * QueryFactory class.\n
- *\n
- * @method serialized\n
- * @return {Object} The jsonable object\n
- */\n
-Query.prototype.serialized = function () {\n
-  return undefined;\n
-};\n
-\n
-exports.Query = Query;\n
-\n
-/**\n
+}).call(this);\n
+;/**\n
  * Parse a text request to a json query object tree\n
  *\n
  * @param  {String} string The string to parse\n
  * @return {Object} The json query tree\n
  */\n
 function parseStringToObject(string) {\n
-\n
-\n
+;\n
 /*\n
 \tDefault template driver for JS/CC generated parsers running as\n
 \tbrowser-based JavaScript/ECMAScript applications.\n
@@ -4081,7 +4088,7 @@ switch( state )\n
 \n
 \tcase 15:\n
 \t\tif( info.src.charCodeAt( pos ) == 34 ) state = 7;\n
-\t\telse if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 33 ) || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 91 ) || ( info.src.charCodeAt( pos ) >= 93 && info.src.charCodeAt( pos ) <= 254 ) ) state = 15;\n
+\telse if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 33 ) || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 91 ) || ( info.src.charCodeAt( pos ) >= 93 && info.src.charCodeAt( pos ) <= 254 ) || info.src.charCodeAt( pos ) > 255 ) state = 15;\n
 \t\telse if( info.src.charCodeAt( pos ) == 92 ) state = 17;\n
 \t\telse state = -1;\n
 \t\tbreak;\n
@@ -4600,996 +4607,6049 @@ var arrayExtend = function () {\n
   error_count = 0,\n
   result;\n
 \n
-if ((error_count = __NODEJS_parse(string, error_offsets, error_lookaheads)) > 0) {\n
-  var i;\n
-  for (i = 0; i < error_count; i += 1) {\n
-    throw new Error("Parse error near \\"" +\n
-                    string.substr(error_offsets[i]) +\n
-                    "\\", expecting \\"" +\n
-                    error_lookaheads[i].join() + "\\"");\n
+if ((error_count = __NODEJS_parse(string, error_offsets, error_lookaheads)) > 0) {\n
+  var i;\n
+  for (i = 0; i < error_count; i += 1) {\n
+    throw new Error("Parse error near \\"" +\n
+                    string.substr(error_offsets[i]) +\n
+                    "\\", expecting \\"" +\n
+                    error_lookaheads[i].join() + "\\"");\n
+  }\n
+}\n
+\n
+;  return result;\n
+} // parseStringToObject\n
+\n
+;/*global RSVP, window, parseStringToObject*/\n
+/*jslint nomen: true, maxlen: 90*/\n
+(function (RSVP, window, parseStringToObject) {\n
+  "use strict";\n
+\n
+  var query_class_dict = {},\n
+    regexp_escape = /[\\-\\[\\]{}()*+?.,\\\\\\^$|#\\s]/g,\n
+    regexp_percent = /%/g,\n
+    regexp_underscore = /_/g,\n
+    regexp_operator = /^(?:AND|OR|NOT)$/i,\n
+    regexp_comparaison = /^(?:!?=|<=?|>=?)$/i;\n
+\n
+  /**\n
+   * Convert metadata values to array of strings. ex:\n
+   *\n
+   *     "a" -> ["a"],\n
+   *     {"content": "a"} -> ["a"]\n
+   *\n
+   * @param  {Any} value The metadata value\n
+   * @return {Array} The value in string array format\n
+   */\n
+  function metadataValueToStringArray(value) {\n
+    var i, new_value = [];\n
+    if (value === undefined) {\n
+      return undefined;\n
+    }\n
+    if (!Array.isArray(value)) {\n
+      value = [value];\n
+    }\n
+    for (i = 0; i < value.length; i += 1) {\n
+      if (typeof value[i] === \'object\') {\n
+        new_value[i] = value[i].content;\n
+      } else {\n
+        new_value[i] = value[i];\n
+      }\n
+    }\n
+    return new_value;\n
+  }\n
+\n
+  /**\n
+   * A sort function to sort items by key\n
+   *\n
+   * @param  {String} key The key to sort on\n
+   * @param  {String} [way="ascending"] \'ascending\' or \'descending\'\n
+   * @return {Function} The sort function\n
+   */\n
+  function sortFunction(key, way) {\n
+    var result;\n
+    if (way === \'descending\') {\n
+      result = 1;\n
+    } else if (way === \'ascending\') {\n
+      result = -1;\n
+    } else {\n
+      throw new TypeError("Query.sortFunction(): " +\n
+                          "Argument 2 must be \'ascending\' or \'descending\'");\n
+    }\n
+    return function (a, b) {\n
+      // this comparison is 5 times faster than json comparison\n
+      var i, l;\n
+      a = metadataValueToStringArray(a[key]) || [];\n
+      b = metadataValueToStringArray(b[key]) || [];\n
+      l = a.length > b.length ? a.length : b.length;\n
+      for (i = 0; i < l; i += 1) {\n
+        if (a[i] === undefined) {\n
+          return result;\n
+        }\n
+        if (b[i] === undefined) {\n
+          return -result;\n
+        }\n
+        if (a[i] > b[i]) {\n
+          return -result;\n
+        }\n
+        if (a[i] < b[i]) {\n
+          return result;\n
+        }\n
+      }\n
+      return 0;\n
+    };\n
+  }\n
+\n
+  /**\n
+   * Sort a list of items, according to keys and directions.\n
+   *\n
+   * @param  {Array} sort_on_option List of couples [key, direction]\n
+   * @param  {Array} list The item list to sort\n
+   * @return {Array} The filtered list\n
+   */\n
+  function sortOn(sort_on_option, list) {\n
+    var sort_index;\n
+    if (!Array.isArray(sort_on_option)) {\n
+      throw new TypeError("jioquery.sortOn(): " +\n
+                          "Argument 1 is not of type \'array\'");\n
+    }\n
+    for (sort_index = sort_on_option.length - 1; sort_index >= 0;\n
+         sort_index -= 1) {\n
+      list.sort(sortFunction(\n
+        sort_on_option[sort_index][0],\n
+        sort_on_option[sort_index][1]\n
+      ));\n
+    }\n
+    return list;\n
+  }\n
+\n
+  /**\n
+   * Limit a list of items, according to index and length.\n
+   *\n
+   * @param  {Array} limit_option A couple [from, length]\n
+   * @param  {Array} list The item list to limit\n
+   * @return {Array} The filtered list\n
+   */\n
+  function limit(limit_option, list) {\n
+    if (!Array.isArray(limit_option)) {\n
+      throw new TypeError("jioquery.limit(): " +\n
+                          "Argument 1 is not of type \'array\'");\n
+    }\n
+    if (!Array.isArray(list)) {\n
+      throw new TypeError("jioquery.limit(): " +\n
+                          "Argument 2 is not of type \'array\'");\n
+    }\n
+    list.splice(0, limit_option[0]);\n
+    if (limit_option[1]) {\n
+      list.splice(limit_option[1]);\n
+    }\n
+    return list;\n
+  }\n
+\n
+  /**\n
+   * Filter a list of items, modifying them to select only wanted keys.\n
+   *\n
+   * @param  {Array} select_option Key list to keep\n
+   * @param  {Array} list The item list to filter\n
+   * @return {Array} The filtered list\n
+   */\n
+  function select(select_option, list) {\n
+    var i, j, new_item;\n
+    if (!Array.isArray(select_option)) {\n
+      throw new TypeError("jioquery.select(): " +\n
+                          "Argument 1 is not of type Array");\n
+    }\n
+    if (!Array.isArray(list)) {\n
+      throw new TypeError("jioquery.select(): " +\n
+                          "Argument 2 is not of type Array");\n
+    }\n
+    for (i = 0; i < list.length; i += 1) {\n
+      new_item = {};\n
+      for (j = 0; j < select_option.length; j += 1) {\n
+        if (list[i].hasOwnProperty([select_option[j]])) {\n
+          new_item[select_option[j]] = list[i][select_option[j]];\n
+        }\n
+      }\n
+      for (j in new_item) {\n
+        if (new_item.hasOwnProperty(j)) {\n
+          list[i] = new_item;\n
+          break;\n
+        }\n
+      }\n
+    }\n
+    return list;\n
+  }\n
+\n
+  /**\n
+   * The query to use to filter a list of objects.\n
+   * This is an abstract class.\n
+   *\n
+   * @class Query\n
+   * @constructor\n
+   */\n
+  function Query() {\n
+\n
+    /**\n
+     * Called before parsing the query. Must be overridden!\n
+     *\n
+     * @method onParseStart\n
+     * @param  {Object} object The object shared in the parse process\n
+     * @param  {Object} option Some option gave in parse()\n
+     */\n
+  //   this.onParseStart = emptyFunction;\n
+\n
+    /**\n
+     * Called when parsing a simple query. Must be overridden!\n
+     *\n
+     * @method onParseSimpleQuery\n
+     * @param  {Object} object The object shared in the parse process\n
+     * @param  {Object} option Some option gave in parse()\n
+     */\n
+  //   this.onParseSimpleQuery = emptyFunction;\n
+\n
+    /**\n
+     * Called when parsing a complex query. Must be overridden!\n
+     *\n
+     * @method onParseComplexQuery\n
+     * @param  {Object} object The object shared in the parse process\n
+     * @param  {Object} option Some option gave in parse()\n
+     */\n
+  //   this.onParseComplexQuery = emptyFunction;\n
+\n
+    /**\n
+     * Called after parsing the query. Must be overridden!\n
+     *\n
+     * @method onParseEnd\n
+     * @param  {Object} object The object shared in the parse process\n
+     * @param  {Object} option Some option gave in parse()\n
+     */\n
+  //   this.onParseEnd = emptyFunction;\n
+\n
+    return;\n
+  }\n
+\n
+  /**\n
+   * Filter the item list with matching item only\n
+   *\n
+   * @method exec\n
+   * @param  {Array} item_list The list of object\n
+   * @param  {Object} [option] Some operation option\n
+   * @param  {Array} [option.select_list] A object keys to retrieve\n
+   * @param  {Array} [option.sort_on] Couples of object keys and "ascending"\n
+   *                 or "descending"\n
+   * @param  {Array} [option.limit] Couple of integer, first is an index and\n
+   *                 second is the length.\n
+   */\n
+  Query.prototype.exec = function (item_list, option) {\n
+    if (!Array.isArray(item_list)) {\n
+      throw new TypeError("Query().exec(): Argument 1 is not of type \'array\'");\n
+    }\n
+    if (option === undefined) {\n
+      option = {};\n
+    }\n
+    if (typeof option !== \'object\') {\n
+      throw new TypeError("Query().exec(): " +\n
+                          "Optional argument 2 is not of type \'object\'");\n
+    }\n
+    var context = this,\n
+      i;\n
+    for (i = item_list.length - 1; i >= 0; i -= 1) {\n
+      if (!context.match(item_list[i])) {\n
+        item_list.splice(i, 1);\n
+      }\n
+    }\n
+\n
+    if (option.sort_on) {\n
+      sortOn(option.sort_on, item_list);\n
+    }\n
+\n
+    if (option.limit) {\n
+      limit(option.limit, item_list);\n
+    }\n
+\n
+    select(option.select_list || [], item_list);\n
+\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return item_list;\n
+      });\n
+  };\n
+\n
+  /**\n
+   * Test if an item matches this query\n
+   *\n
+   * @method match\n
+   * @param  {Object} item The object to test\n
+   * @return {Boolean} true if match, false otherwise\n
+   */\n
+  Query.prototype.match = function () {\n
+    return true;\n
+  };\n
+\n
+  /**\n
+   * Browse the Query in deep calling parser method in each step.\n
+   *\n
+   * `onParseStart` is called first, on end `onParseEnd` is called.\n
+   * It starts from the simple queries at the bottom of the tree calling the\n
+   * parser method `onParseSimpleQuery`, and go up calling the\n
+   * `onParseComplexQuery` method.\n
+   *\n
+   * @method parse\n
+   * @param  {Object} option Any options you want (except \'parsed\')\n
+   * @return {Any} The parse result\n
+   */\n
+  Query.prototype.parse = function (option) {\n
+    var that = this,\n
+      object;\n
+    /**\n
+     * The recursive parser.\n
+     *\n
+     * @param  {Object} object The object shared in the parse process\n
+     * @param  {Object} options Some options usable in the parseMethods\n
+     * @return {Any} The parser result\n
+     */\n
+    function recParse(object, option) {\n
+      var query = object.parsed,\n
+        queue = new RSVP.Queue(),\n
+        i;\n
+\n
+      function enqueue(j) {\n
+        queue\n
+          .push(function () {\n
+            object.parsed = query.query_list[j];\n
+            return recParse(object, option);\n
+          })\n
+          .push(function () {\n
+            query.query_list[j] = object.parsed;\n
+          });\n
+      }\n
+\n
+      if (query.type === "complex") {\n
+\n
+\n
+        for (i = 0; i < query.query_list.length; i += 1) {\n
+          enqueue(i);\n
+        }\n
+\n
+        return queue\n
+          .push(function () {\n
+            object.parsed = query;\n
+            return that.onParseComplexQuery(object, option);\n
+          });\n
+\n
+      }\n
+      if (query.type === "simple") {\n
+        return that.onParseSimpleQuery(object, option);\n
+      }\n
+    }\n
+    object = {\n
+      parsed: JSON.parse(JSON.stringify(that.serialized()))\n
+    };\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return that.onParseStart(object, option);\n
+      })\n
+      .push(function () {\n
+        return recParse(object, option);\n
+      })\n
+      .push(function () {\n
+        return that.onParseEnd(object, option);\n
+      })\n
+      .push(function () {\n
+        return object.parsed;\n
+      });\n
+\n
+  };\n
+\n
+  /**\n
+   * Convert this query to a parsable string.\n
+   *\n
+   * @method toString\n
+   * @return {String} The string version of this query\n
+   */\n
+  Query.prototype.toString = function () {\n
+    return "";\n
+  };\n
+\n
+  /**\n
+   * Convert this query to an jsonable object in order to be remake thanks to\n
+   * QueryFactory class.\n
+   *\n
+   * @method serialized\n
+   * @return {Object} The jsonable object\n
+   */\n
+  Query.prototype.serialized = function () {\n
+    return undefined;\n
+  };\n
+\n
+  /**\n
+   * Provides static methods to create Query object\n
+   *\n
+   * @class QueryFactory\n
+   */\n
+  function QueryFactory() {\n
+    return;\n
+  }\n
+\n
+  /**\n
+   * Escapes regexp special chars from a string.\n
+   *\n
+   * @param  {String} string The string to escape\n
+   * @return {String} The escaped string\n
+   */\n
+  function stringEscapeRegexpCharacters(string) {\n
+    return string.replace(regexp_escape, "\\\\$&");\n
+  }\n
+\n
+  /**\n
+   * Inherits the prototype methods from one constructor into another. The\n
+   * prototype of `constructor` will be set to a new object created from\n
+   * `superConstructor`.\n
+   *\n
+   * @param  {Function} constructor The constructor which inherits the super one\n
+   * @param  {Function} superConstructor The super constructor\n
+   */\n
+  function inherits(constructor, superConstructor) {\n
+    constructor.super_ = superConstructor;\n
+    constructor.prototype = Object.create(superConstructor.prototype, {\n
+      "constructor": {\n
+        "configurable": true,\n
+        "enumerable": false,\n
+        "writable": true,\n
+        "value": constructor\n
+      }\n
+    });\n
+  }\n
+\n
+  /**\n
+   * Convert a search text to a regexp.\n
+   *\n
+   * @param  {String} string The string to convert\n
+   * @param  {Boolean} [use_wildcard_character=true] Use wildcard "%" and "_"\n
+   * @return {RegExp} The search text regexp\n
+   */\n
+  function searchTextToRegExp(string, use_wildcard_characters) {\n
+    if (typeof string !== \'string\') {\n
+      throw new TypeError("jioquery.searchTextToRegExp(): " +\n
+                          "Argument 1 is not of type \'string\'");\n
+    }\n
+    if (use_wildcard_characters === false) {\n
+      return new RegExp("^" + stringEscapeRegexpCharacters(string) + "$");\n
+    }\n
+    return new RegExp("^" + stringEscapeRegexpCharacters(string)\n
+      .replace(regexp_percent, \'.*\')\n
+      .replace(regexp_underscore, \'.\') + "$");\n
+  }\n
+\n
+  /**\n
+   * The ComplexQuery inherits from Query, and compares one or several metadata\n
+   * values.\n
+   *\n
+   * @class ComplexQuery\n
+   * @extends Query\n
+   * @param  {Object} [spec={}] The specifications\n
+   * @param  {String} [spec.operator="AND"] The compare method to use\n
+   * @param  {String} spec.key The metadata key\n
+   * @param  {String} spec.value The value of the metadata to compare\n
+   */\n
+  function ComplexQuery(spec, key_schema) {\n
+    Query.call(this);\n
+\n
+    /**\n
+     * Logical operator to use to compare object values\n
+     *\n
+     * @attribute operator\n
+     * @type String\n
+     * @default "AND"\n
+     * @optional\n
+     */\n
+    this.operator = spec.operator;\n
+\n
+    /**\n
+     * The sub Query list which are used to query an item.\n
+     *\n
+     * @attribute query_list\n
+     * @type Array\n
+     * @default []\n
+     * @optional\n
+     */\n
+    this.query_list = spec.query_list || [];\n
+    this.query_list = this.query_list.map(\n
+      // decorate the map to avoid sending the index as key_schema argument\n
+      function (o) { return QueryFactory.create(o, key_schema); }\n
+    );\n
+\n
+  }\n
+  inherits(ComplexQuery, Query);\n
+\n
+  ComplexQuery.prototype.operator = "AND";\n
+  ComplexQuery.prototype.type = "complex";\n
+\n
+  /**\n
+   * #crossLink "Query/match:method"\n
+   */\n
+  ComplexQuery.prototype.match = function (item) {\n
+    var operator = this.operator;\n
+    if (!(regexp_operator.test(operator))) {\n
+      operator = "AND";\n
+    }\n
+    return this[operator.toUpperCase()](item);\n
+  };\n
+\n
+  /**\n
+   * #crossLink "Query/toString:method"\n
+   */\n
+  ComplexQuery.prototype.toString = function () {\n
+    var str_list = [], this_operator = this.operator;\n
+    if (this.operator === "NOT") {\n
+      str_list.push("NOT (");\n
+      str_list.push(this.query_list[0].toString());\n
+      str_list.push(")");\n
+      return str_list.join(" ");\n
+    }\n
+    this.query_list.forEach(function (query) {\n
+      str_list.push("(");\n
+      str_list.push(query.toString());\n
+      str_list.push(")");\n
+      str_list.push(this_operator);\n
+    });\n
+    str_list.length -= 1;\n
+    return str_list.join(" ");\n
+  };\n
+\n
+  /**\n
+   * #crossLink "Query/serialized:method"\n
+   */\n
+  ComplexQuery.prototype.serialized = function () {\n
+    var s = {\n
+      "type": "complex",\n
+      "operator": this.operator,\n
+      "query_list": []\n
+    };\n
+    this.query_list.forEach(function (query) {\n
+      s.query_list.push(\n
+        typeof query.toJSON === "function" ? query.toJSON() : query\n
+      );\n
+    });\n
+    return s;\n
+  };\n
+  ComplexQuery.prototype.toJSON = ComplexQuery.prototype.serialized;\n
+\n
+  /**\n
+   * Comparison operator, test if all sub queries match the\n
+   * item value\n
+   *\n
+   * @method AND\n
+   * @param  {Object} item The item to match\n
+   * @return {Boolean} true if all match, false otherwise\n
+   */\n
+  ComplexQuery.prototype.AND = function (item) {\n
+    var result = true,\n
+      i = 0;\n
+\n
+    while (result && (i !== this.query_list.length)) {\n
+      result = this.query_list[i].match(item);\n
+      i += 1;\n
+    }\n
+    return result;\n
+\n
+  };\n
+\n
+  /**\n
+   * Comparison operator, test if one of the sub queries matches the\n
+   * item value\n
+   *\n
+   * @method OR\n
+   * @param  {Object} item The item to match\n
+   * @return {Boolean} true if one match, false otherwise\n
+   */\n
+  ComplexQuery.prototype.OR = function (item) {\n
+    var result = false,\n
+      i = 0;\n
+\n
+    while ((!result) && (i !== this.query_list.length)) {\n
+      result = this.query_list[i].match(item);\n
+      i += 1;\n
+    }\n
+\n
+    return result;\n
+  };\n
+\n
+  /**\n
+   * Comparison operator, test if the sub query does not match the\n
+   * item value\n
+   *\n
+   * @method NOT\n
+   * @param  {Object} item The item to match\n
+   * @return {Boolean} true if one match, false otherwise\n
+   */\n
+  ComplexQuery.prototype.NOT = function (item) {\n
+    return !this.query_list[0].match(item);\n
+  };\n
+\n
+  /**\n
+   * Creates Query object from a search text string or a serialized version\n
+   * of a Query.\n
+   *\n
+   * @method create\n
+   * @static\n
+   * @param  {Object,String} object The search text or the serialized version\n
+   *         of a Query\n
+   * @return {Query} A Query object\n
+   */\n
+  QueryFactory.create = function (object, key_schema) {\n
+    if (object === "") {\n
+      return new Query();\n
+    }\n
+    if (typeof object === "string") {\n
+      object = parseStringToObject(object);\n
+    }\n
+    if (typeof (object || {}).type === "string" &&\n
+        query_class_dict[object.type]) {\n
+      return new query_class_dict[object.type](object, key_schema);\n
+    }\n
+    throw new TypeError("QueryFactory.create(): " +\n
+                        "Argument 1 is not a search text or a parsable object");\n
+  };\n
+\n
+  function objectToSearchText(query) {\n
+    var str_list = [];\n
+    if (query.type === "complex") {\n
+      str_list.push("(");\n
+      (query.query_list || []).forEach(function (sub_query) {\n
+        str_list.push(objectToSearchText(sub_query));\n
+        str_list.push(query.operator);\n
+      });\n
+      str_list.length -= 1;\n
+      str_list.push(")");\n
+      return str_list.join(" ");\n
+    }\n
+    if (query.type === "simple") {\n
+      return (query.key ? query.key + ": " : "") +\n
+        (query.operator || "") + \' "\' + query.value + \'"\';\n
+    }\n
+    throw new TypeError("This object is not a query");\n
+  }\n
+\n
+  function checkKeySchema(key_schema) {\n
+    var prop;\n
+\n
+    if (key_schema !== undefined) {\n
+      if (typeof key_schema !== \'object\') {\n
+        throw new TypeError("SimpleQuery().create(): " +\n
+                            "key_schema is not of type \'object\'");\n
+      }\n
+      // key_set is mandatory\n
+      if (key_schema.key_set === undefined) {\n
+        throw new TypeError("SimpleQuery().create(): " +\n
+                            "key_schema has no \'key_set\' property");\n
+      }\n
+      for (prop in key_schema) {\n
+        if (key_schema.hasOwnProperty(prop)) {\n
+          switch (prop) {\n
+          case \'key_set\':\n
+          case \'cast_lookup\':\n
+          case \'match_lookup\':\n
+            break;\n
+          default:\n
+            throw new TypeError("SimpleQuery().create(): " +\n
+                               "key_schema has unknown property \'" + prop + "\'");\n
+          }\n
+        }\n
+      }\n
+    }\n
+  }\n
+\n
+  /**\n
+   * The SimpleQuery inherits from Query, and compares one metadata value\n
+   *\n
+   * @class SimpleQuery\n
+   * @extends Query\n
+   * @param  {Object} [spec={}] The specifications\n
+   * @param  {String} [spec.operator="="] The compare method to use\n
+   * @param  {String} spec.key The metadata key\n
+   * @param  {String} spec.value The value of the metadata to compare\n
+   */\n
+  function SimpleQuery(spec, key_schema) {\n
+    Query.call(this);\n
+\n
+    checkKeySchema(key_schema);\n
+\n
+    this._key_schema = key_schema || {};\n
+\n
+    /**\n
+     * Operator to use to compare object values\n
+     *\n
+     * @attribute operator\n
+     * @type String\n
+     * @optional\n
+     */\n
+    this.operator = spec.operator;\n
+\n
+    /**\n
+     * Key of the object which refers to the value to compare\n
+     *\n
+     * @attribute key\n
+     * @type String\n
+     */\n
+    this.key = spec.key;\n
+\n
+    /**\n
+     * Value is used to do the comparison with the object value\n
+     *\n
+     * @attribute value\n
+     * @type String\n
+     */\n
+    this.value = spec.value;\n
+\n
+  }\n
+  inherits(SimpleQuery, Query);\n
+\n
+  SimpleQuery.prototype.type = "simple";\n
+\n
+  function checkKey(key) {\n
+    var prop;\n
+\n
+    if (key.read_from === undefined) {\n
+      throw new TypeError("Custom key is missing the read_from property");\n
+    }\n
+\n
+    for (prop in key) {\n
+      if (key.hasOwnProperty(prop)) {\n
+        switch (prop) {\n
+        case \'read_from\':\n
+        case \'cast_to\':\n
+        case \'equal_match\':\n
+          break;\n
+        default:\n
+          throw new TypeError("Custom key has unknown property \'" +\n
+                              prop + "\'");\n
+        }\n
+      }\n
+    }\n
+  }\n
+\n
+  /**\n
+   * #crossLink "Query/match:method"\n
+   */\n
+  SimpleQuery.prototype.match = function (item) {\n
+    var object_value = null,\n
+      equal_match = null,\n
+      cast_to = null,\n
+      matchMethod = null,\n
+      operator = this.operator,\n
+      value = null,\n
+      key = this.key;\n
+\n
+    if (!(regexp_comparaison.test(operator))) {\n
+      // `operator` is not correct, we have to change it to "like" or "="\n
+      if (regexp_percent.test(this.value)) {\n
+        // `value` contains a non escaped `%`\n
+        operator = "like";\n
+      } else {\n
+        // `value` does not contain non escaped `%`\n
+        operator = "=";\n
+      }\n
+    }\n
+\n
+    matchMethod = this[operator];\n
+\n
+    if (this._key_schema.key_set && this._key_schema.key_set[key] !== undefined) {\n
+      key = this._key_schema.key_set[key];\n
+    }\n
+\n
+    if (typeof key === \'object\') {\n
+      checkKey(key);\n
+      object_value = item[key.read_from];\n
+\n
+      equal_match = key.equal_match;\n
+\n
+      // equal_match can be a string\n
+      if (typeof equal_match === \'string\') {\n
+        // XXX raise error if equal_match not in match_lookup\n
+        equal_match = this._key_schema.match_lookup[equal_match];\n
+      }\n
+\n
+      // equal_match overrides the default \'=\' operator\n
+      if (equal_match !== undefined) {\n
+        matchMethod = (operator === "=" || operator === "like" ?\n
+                       equal_match : matchMethod);\n
+      }\n
+\n
+      value = this.value;\n
+      cast_to = key.cast_to;\n
+      if (cast_to) {\n
+        // cast_to can be a string\n
+        if (typeof cast_to === \'string\') {\n
+          // XXX raise error if cast_to not in cast_lookup\n
+          cast_to = this._key_schema.cast_lookup[cast_to];\n
+        }\n
+\n
+        try {\n
+          value = cast_to(value);\n
+        } catch (e) {\n
+          value = undefined;\n
+        }\n
+\n
+        try {\n
+          object_value = cast_to(object_value);\n
+        } catch (e) {\n
+          object_value = undefined;\n
+        }\n
+      }\n
+    } else {\n
+      object_value = item[key];\n
+      value = this.value;\n
+    }\n
+    if (object_value === undefined || value === undefined) {\n
+      return false;\n
+    }\n
+    return matchMethod(object_value, value);\n
+  };\n
+\n
+  /**\n
+   * #crossLink "Query/toString:method"\n
+   */\n
+  SimpleQuery.prototype.toString = function () {\n
+    return (this.key ? this.key + ":" : "") +\n
+      (this.operator ? " " + this.operator : "") + \' "\' + this.value + \'"\';\n
+  };\n
+\n
+  /**\n
+   * #crossLink "Query/serialized:method"\n
+   */\n
+  SimpleQuery.prototype.serialized = function () {\n
+    var object = {\n
+      "type": "simple",\n
+      "key": this.key,\n
+      "value": this.value\n
+    };\n
+    if (this.operator !== undefined) {\n
+      object.operator = this.operator;\n
+    }\n
+    return object;\n
+  };\n
+  SimpleQuery.prototype.toJSON = SimpleQuery.prototype.serialized;\n
+\n
+  /**\n
+   * Comparison operator, test if this query value matches the item value\n
+   *\n
+   * @method =\n
+   * @param  {String} object_value The value to compare\n
+   * @param  {String} comparison_value The comparison value\n
+   * @return {Boolean} true if match, false otherwise\n
+   */\n
+  SimpleQuery.prototype["="] = function (object_value, comparison_value) {\n
+    var value, i;\n
+    if (!Array.isArray(object_value)) {\n
+      object_value = [object_value];\n
+    }\n
+    for (i = 0; i < object_value.length; i += 1) {\n
+      value = object_value[i];\n
+      if (typeof value === \'object\' && value.hasOwnProperty(\'content\')) {\n
+        value = value.content;\n
+      }\n
+      if (typeof value.cmp === "function") {\n
+        return (value.cmp(comparison_value) === 0);\n
+      }\n
+      if (comparison_value.toString() === value.toString()) {\n
+        return true;\n
+      }\n
+    }\n
+    return false;\n
+  };\n
+\n
+  /**\n
+   * Comparison operator, test if this query value matches the item value\n
+   *\n
+   * @method like\n
+   * @param  {String} object_value The value to compare\n
+   * @param  {String} comparison_value The comparison value\n
+   * @return {Boolean} true if match, false otherwise\n
+   */\n
+  SimpleQuery.prototype.like = function (object_value, comparison_value) {\n
+    var value, i;\n
+    if (!Array.isArray(object_value)) {\n
+      object_value = [object_value];\n
+    }\n
+    for (i = 0; i < object_value.length; i += 1) {\n
+      value = object_value[i];\n
+      if (typeof value === \'object\' && value.hasOwnProperty(\'content\')) {\n
+        value = value.content;\n
+      }\n
+      if (typeof value.cmp === "function") {\n
+        return (value.cmp(comparison_value) === 0);\n
+      }\n
+      if (\n
+        searchTextToRegExp(comparison_value.toString()).test(value.toString())\n
+      ) {\n
+        return true;\n
+      }\n
+    }\n
+    return false;\n
+  };\n
+\n
+  /**\n
+   * Comparison operator, test if this query value does not match the item value\n
+   *\n
+   * @method !=\n
+   * @param  {String} object_value The value to compare\n
+   * @param  {String} comparison_value The comparison value\n
+   * @return {Boolean} true if not match, false otherwise\n
+   */\n
+  SimpleQuery.prototype["!="] = function (object_value, comparison_value) {\n
+    var value, i;\n
+    if (!Array.isArray(object_value)) {\n
+      object_value = [object_value];\n
+    }\n
+    for (i = 0; i < object_value.length; i += 1) {\n
+      value = object_value[i];\n
+      if (typeof value === \'object\' && value.hasOwnProperty(\'content\')) {\n
+        value = value.content;\n
+      }\n
+      if (typeof value.cmp === "function") {\n
+        return (value.cmp(comparison_value) !== 0);\n
+      }\n
+      if (comparison_value.toString() === value.toString()) {\n
+        return false;\n
+      }\n
+    }\n
+    return true;\n
+  };\n
+\n
+  /**\n
+   * Comparison operator, test if this query value is lower than the item value\n
+   *\n
+   * @method <\n
+   * @param  {Number, String} object_value The value to compare\n
+   * @param  {Number, String} comparison_value The comparison value\n
+   * @return {Boolean} true if lower, false otherwise\n
+   */\n
+  SimpleQuery.prototype["<"] = function (object_value, comparison_value) {\n
+    var value;\n
+    if (!Array.isArray(object_value)) {\n
+      object_value = [object_value];\n
+    }\n
+    value = object_value[0];\n
+    if (typeof value === \'object\' && value.hasOwnProperty(\'content\')) {\n
+      value = value.content;\n
+    }\n
+    if (typeof value.cmp === "function") {\n
+      return (value.cmp(comparison_value) < 0);\n
+    }\n
+    return (value < comparison_value);\n
+  };\n
+\n
+  /**\n
+   * Comparison operator, test if this query value is equal or lower than the\n
+   * item value\n
+   *\n
+   * @method <=\n
+   * @param  {Number, String} object_value The value to compare\n
+   * @param  {Number, String} comparison_value The comparison value\n
+   * @return {Boolean} true if equal or lower, false otherwise\n
+   */\n
+  SimpleQuery.prototype["<="] = function (object_value, comparison_value) {\n
+    var value;\n
+    if (!Array.isArray(object_value)) {\n
+      object_value = [object_value];\n
+    }\n
+    value = object_value[0];\n
+    if (typeof value === \'object\' && value.hasOwnProperty(\'content\')) {\n
+      value = value.content;\n
+    }\n
+    if (typeof value.cmp === "function") {\n
+      return (value.cmp(comparison_value) <= 0);\n
+    }\n
+    return (value <= comparison_value);\n
+  };\n
+\n
+  /**\n
+   * Comparison operator, test if this query value is greater than the item\n
+   * value\n
+   *\n
+   * @method >\n
+   * @param  {Number, String} object_value The value to compare\n
+   * @param  {Number, String} comparison_value The comparison value\n
+   * @return {Boolean} true if greater, false otherwise\n
+   */\n
+  SimpleQuery.prototype[">"] = function (object_value, comparison_value) {\n
+    var value;\n
+    if (!Array.isArray(object_value)) {\n
+      object_value = [object_value];\n
+    }\n
+    value = object_value[0];\n
+    if (typeof value === \'object\' && value.hasOwnProperty(\'content\')) {\n
+      value = value.content;\n
+    }\n
+    if (typeof value.cmp === "function") {\n
+      return (value.cmp(comparison_value) > 0);\n
+    }\n
+    return (value > comparison_value);\n
+  };\n
+\n
+  /**\n
+   * Comparison operator, test if this query value is equal or greater than the\n
+   * item value\n
+   *\n
+   * @method >=\n
+   * @param  {Number, String} object_value The value to compare\n
+   * @param  {Number, String} comparison_value The comparison value\n
+   * @return {Boolean} true if equal or greater, false otherwise\n
+   */\n
+  SimpleQuery.prototype[">="] = function (object_value, comparison_value) {\n
+    var value;\n
+    if (!Array.isArray(object_value)) {\n
+      object_value = [object_value];\n
+    }\n
+    value = object_value[0];\n
+    if (typeof value === \'object\' && value.hasOwnProperty(\'content\')) {\n
+      value = value.content;\n
+    }\n
+    if (typeof value.cmp === "function") {\n
+      return (value.cmp(comparison_value) >= 0);\n
+    }\n
+    return (value >= comparison_value);\n
+  };\n
+\n
+  query_class_dict.simple = SimpleQuery;\n
+  query_class_dict.complex = ComplexQuery;\n
+\n
+  Query.parseStringToObject = parseStringToObject;\n
+  Query.objectToSearchText = objectToSearchText;\n
+\n
+  window.Query = Query;\n
+  window.SimpleQuery = SimpleQuery;\n
+  window.ComplexQuery = ComplexQuery;\n
+  window.QueryFactory = QueryFactory;\n
+\n
+}(RSVP, window, parseStringToObject));\n
+;/*global window, moment */\n
+/*jslint nomen: true, maxlen: 200*/\n
+(function (window, moment) {\n
+  "use strict";\n
+\n
+//   /**\n
+//    * Add a secured (write permission denied) property to an object.\n
+//    *\n
+//    * @param  {Object} object The object to fill\n
+//    * @param  {String} key The object key where to store the property\n
+//    * @param  {Any} value The value to store\n
+//    */\n
+//   function _export(key, value) {\n
+//     Object.defineProperty(to_export, key, {\n
+//       "configurable": false,\n
+//       "enumerable": true,\n
+//       "writable": false,\n
+//       "value": value\n
+//     });\n
+//   }\n
+\n
+  var YEAR = \'year\',\n
+    MONTH = \'month\',\n
+    DAY = \'day\',\n
+    HOUR = \'hour\',\n
+    MIN = \'minute\',\n
+    SEC = \'second\',\n
+    MSEC = \'millisecond\',\n
+    precision_grade = {\n
+      \'year\': 0,\n
+      \'month\': 1,\n
+      \'day\': 2,\n
+      \'hour\': 3,\n
+      \'minute\': 4,\n
+      \'second\': 5,\n
+      \'millisecond\': 6\n
+    },\n
+    lesserPrecision = function (p1, p2) {\n
+      return (precision_grade[p1] < precision_grade[p2]) ? p1 : p2;\n
+    },\n
+    JIODate;\n
+\n
+\n
+  JIODate = function (str) {\n
+    // in case of forgotten \'new\'\n
+    if (!(this instanceof JIODate)) {\n
+      return new JIODate(str);\n
+    }\n
+\n
+    if (str instanceof JIODate) {\n
+      this.mom = str.mom.clone();\n
+      this._precision = str._precision;\n
+      return;\n
+    }\n
+\n
+    if (str === undefined) {\n
+      this.mom = moment();\n
+      this.setPrecision(MSEC);\n
+      return;\n
+    }\n
+\n
+    this.mom = null;\n
+    this._str = str;\n
+\n
+    // http://www.w3.org/TR/NOTE-datetime\n
+    // http://dotat.at/tmp/ISO_8601-2004_E.pdf\n
+\n
+    // XXX these regexps fail to detect many invalid dates.\n
+\n
+    if (str.match(/\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+\\-][0-2]\\d:[0-5]\\d|Z)/)\n
+          || str.match(/\\d\\d\\d\\d-\\d\\d-\\d\\d \\d\\d:\\d\\d:\\d\\d\\.\\d\\d\\d/)) {\n
+      // ISO, milliseconds\n
+      this.mom = moment(str);\n
+      this.setPrecision(MSEC);\n
+    } else if (str.match(/\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d([+\\-][0-2]\\d:[0-5]\\d|Z)/)\n
+          || str.match(/\\d\\d\\d\\d-\\d\\d-\\d\\d \\d\\d:\\d\\d:\\d\\d/)) {\n
+      // ISO, seconds\n
+      this.mom = moment(str);\n
+      this.setPrecision(SEC);\n
+    } else if (str.match(/\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d([+\\-][0-2]\\d:[0-5]\\d|Z)/)\n
+          || str.match(/\\d\\d\\d\\d-\\d\\d-\\d\\d \\d\\d:\\d\\d/)) {\n
+      // ISO, minutes\n
+      this.mom = moment(str);\n
+      this.setPrecision(MIN);\n
+    } else if (str.match(/\\d\\d\\d\\d-\\d\\d-\\d\\d \\d\\d/)) {\n
+      this.mom = moment(str);\n
+      this.setPrecision(HOUR);\n
+    } else if (str.match(/\\d\\d\\d\\d-\\d\\d-\\d\\d/)) {\n
+      this.mom = moment(str);\n
+      this.setPrecision(DAY);\n
+    } else if (str.match(/\\d\\d\\d\\d-\\d\\d/)) {\n
+      this.mom = moment(str);\n
+      this.setPrecision(MONTH);\n
+    } else if (str.match(/\\d\\d\\d\\d/)) {\n
+      this.mom = moment(str);\n
+      this.setPrecision(YEAR);\n
+    }\n
+\n
+    if (!this.mom) {\n
+      throw new Error("Cannot parse: " + str);\n
+    }\n
+\n
+  };\n
+\n
+\n
+  JIODate.prototype.setPrecision = function (prec) {\n
+    this._precision = prec;\n
+  };\n
+\n
+\n
+  JIODate.prototype.getPrecision = function () {\n
+    return this._precision;\n
+  };\n
+\n
+\n
+  JIODate.prototype.cmp = function (other) {\n
+    var m1 = this.mom,\n
+      m2 = other.mom,\n
+      p = lesserPrecision(this._precision, other._precision);\n
+    return m1.isBefore(m2, p) ? -1 : (m1.isSame(m2, p) ? 0 : +1);\n
+  };\n
+\n
+\n
+  JIODate.prototype.toPrecisionString = function (precision) {\n
+    var fmt;\n
+\n
+    precision = precision || this._precision;\n
+\n
+    fmt = {\n
+      \'millisecond\': \'YYYY-MM-DD HH:mm:ss.SSS\',\n
+      \'second\': \'YYYY-MM-DD HH:mm:ss\',\n
+      \'minute\': \'YYYY-MM-DD HH:mm\',\n
+      \'hour\': \'YYYY-MM-DD HH\',\n
+      \'day\': \'YYYY-MM-DD\',\n
+      \'month\': \'YYYY-MM\',\n
+      \'year\': \'YYYY\'\n
+    }[precision];\n
+\n
+    if (!fmt) {\n
+      throw new TypeError("Unsupported precision value \'" + precision + "\'");\n
+    }\n
+\n
+    return this.mom.format(fmt);\n
+  };\n
+\n
+\n
+  JIODate.prototype.toString = function () {\n
+    return this._str;\n
+  };\n
+\n
+\n
+//   _export(\'JIODate\', JIODate);\n
+// \n
+//   _export(\'YEAR\', YEAR);\n
+//   _export(\'MONTH\', MONTH);\n
+//   _export(\'DAY\', DAY);\n
+//   _export(\'HOUR\', HOUR);\n
+//   _export(\'MIN\', MIN);\n
+//   _export(\'SEC\', SEC);\n
+//   _export(\'MSEC\', MSEC);\n
+\n
+  window.jiodate = {\n
+    JIODate: JIODate,\n
+    YEAR: YEAR,\n
+    MONTH: MONTH,\n
+    DAY: DAY,\n
+    HOUR: HOUR,\n
+    MIN: MIN,\n
+    SEC: SEC,\n
+    MSEC: MSEC\n
+  };\n
+}(window, moment));\n
+;/*global window, RSVP, Blob, XMLHttpRequest, QueryFactory, Query, atob,\n
+  FileReader, ArrayBuffer, Uint8Array */\n
+(function (window, RSVP, Blob, QueryFactory, Query, atob,\n
+           FileReader, ArrayBuffer, Uint8Array) {\n
+  "use strict";\n
+\n
+  var util = {},\n
+    jIO;\n
+\n
+  function jIOError(message, status_code) {\n
+    if ((message !== undefined) && (typeof message !== "string")) {\n
+      throw new TypeError(\'You must pass a string.\');\n
+    }\n
+    this.message = message || "Default Message";\n
+    this.status_code = status_code || 500;\n
+  }\n
+  jIOError.prototype = new Error();\n
+  jIOError.prototype.constructor = jIOError;\n
+  util.jIOError = jIOError;\n
+\n
+  /**\n
+   * Send request with XHR and return a promise. xhr.onload: The promise is\n
+   * resolved when the status code is lower than 400 with the xhr object as\n
+   * first parameter. xhr.onerror: reject with xhr object as first\n
+   * parameter. xhr.onprogress: notifies the xhr object.\n
+   *\n
+   * @param  {Object} param The parameters\n
+   * @param  {String} [param.type="GET"] The request method\n
+   * @param  {String} [param.dataType=""] The data type to retrieve\n
+   * @param  {String} param.url The url\n
+   * @param  {Any} [param.data] The data to send\n
+   * @param  {Function} [param.beforeSend] A function called just before the\n
+   *    send request. The first parameter of this function is the XHR object.\n
+   * @return {Promise} The promise\n
+   */\n
+  function ajax(param) {\n
+    var xhr = new XMLHttpRequest();\n
+    return new RSVP.Promise(function (resolve, reject, notify) {\n
+      var k;\n
+      xhr.open(param.type || "GET", param.url, true);\n
+      xhr.responseType = param.dataType || "";\n
+      if (typeof param.headers === \'object\' && param.headers !== null) {\n
+        for (k in param.headers) {\n
+          if (param.headers.hasOwnProperty(k)) {\n
+            xhr.setRequestHeader(k, param.headers[k]);\n
+          }\n
+        }\n
+      }\n
+      xhr.addEventListener("load", function (e) {\n
+        if (e.target.status >= 400) {\n
+          return reject(e);\n
+        }\n
+        resolve(e);\n
+      });\n
+      xhr.addEventListener("error", reject);\n
+      xhr.addEventListener("progress", notify);\n
+      if (typeof param.xhrFields === \'object\' && param.xhrFields !== null) {\n
+        for (k in param.xhrFields) {\n
+          if (param.xhrFields.hasOwnProperty(k)) {\n
+            xhr[k] = param.xhrFields[k];\n
+          }\n
+        }\n
+      }\n
+      if (typeof param.beforeSend === \'function\') {\n
+        param.beforeSend(xhr);\n
+      }\n
+      xhr.send(param.data);\n
+    }, function () {\n
+      xhr.abort();\n
+    });\n
+  }\n
+  util.ajax = ajax;\n
+\n
+  function readBlobAsText(blob, encoding) {\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.readAsText(blob, encoding);\n
+    }, function () {\n
+      fr.abort();\n
+    });\n
+  }\n
+  util.readBlobAsText = readBlobAsText;\n
+\n
+  function readBlobAsArrayBuffer(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.readAsArrayBuffer(blob);\n
+    }, function () {\n
+      fr.abort();\n
+    });\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
+  // https://gist.github.com/davoclavo/4424731\n
+  function dataURItoBlob(dataURI) {\n
+    // convert base64 to raw binary data held in a string\n
+    var byteString = atob(dataURI.split(\',\')[1]),\n
+    // separate out the mime component\n
+      mimeString = dataURI.split(\',\')[0].split(\':\')[1],\n
+    // write the bytes of the string to an ArrayBuffer\n
+      arrayBuffer = new ArrayBuffer(byteString.length),\n
+      _ia = new Uint8Array(arrayBuffer),\n
+      i;\n
+    mimeString = mimeString.slice(0, mimeString.length - ";base64".length);\n
+    for (i = 0; i < byteString.length; i += 1) {\n
+      _ia[i] = byteString.charCodeAt(i);\n
+    }\n
+    return new Blob([arrayBuffer], {type: mimeString});\n
+  }\n
+\n
+  util.dataURItoBlob = dataURItoBlob;\n
+\n
+  // tools\n
+  function checkId(argument_list, storage, method_name) {\n
+    if (typeof argument_list[0] !== \'string\' || argument_list[0] === \'\') {\n
+      throw new jIO.util.jIOError(\n
+        "Document id must be a non empty string on \'" + storage.__type +\n
+          "." + method_name + "\'.",\n
+        400\n
+      );\n
+    }\n
+  }\n
+\n
+  function checkAttachmentId(argument_list, storage, method_name) {\n
+    if (typeof argument_list[1] !== \'string\' || argument_list[1] === \'\') {\n
+      throw new jIO.util.jIOError(\n
+        "Attachment id must be a non empty string on \'" + storage.__type +\n
+          "." + method_name + "\'.",\n
+        400\n
+      );\n
+    }\n
+  }\n
+\n
+  function declareMethod(klass, name, precondition_function, post_function) {\n
+    klass.prototype[name] = function () {\n
+      var argument_list = arguments,\n
+        context = this,\n
+        precondition_result;\n
+\n
+      return new RSVP.Queue()\n
+        .push(function () {\n
+          if (precondition_function !== undefined) {\n
+            return precondition_function.apply(\n
+              context.__storage,\n
+              [argument_list, context, name]\n
+            );\n
+          }\n
+        })\n
+        .push(function (result) {\n
+          var storage_method = context.__storage[name];\n
+          precondition_result = result;\n
+          if (storage_method === undefined) {\n
+            throw new jIO.util.jIOError(\n
+              "Capacity \'" + name + "\' is not implemented on \'" +\n
+                context.__type + "\'",\n
+              501\n
+            );\n
+          }\n
+          return storage_method.apply(\n
+            context.__storage,\n
+            argument_list\n
+          );\n
+        })\n
+        .push(function (result) {\n
+          if (post_function !== undefined) {\n
+            return post_function.call(\n
+              context,\n
+              argument_list,\n
+              result,\n
+              precondition_result\n
+            );\n
+          }\n
+          return result;\n
+        });\n
+    };\n
+    // Allow chain\n
+    return this;\n
+  }\n
+\n
+\n
+\n
+\n
+  /////////////////////////////////////////////////////////////////\n
+  // jIO Storage Proxy\n
+  /////////////////////////////////////////////////////////////////\n
+  function JioProxyStorage(type, storage) {\n
+    if (!(this instanceof JioProxyStorage)) {\n
+      return new JioProxyStorage();\n
+    }\n
+    this.__type = type;\n
+    this.__storage = storage;\n
+  }\n
+\n
+  declareMethod(JioProxyStorage, "put", checkId, function (argument_list) {\n
+    return argument_list[0];\n
+  });\n
+  declareMethod(JioProxyStorage, "get", checkId);\n
+  declareMethod(JioProxyStorage, "bulk");\n
+  declareMethod(JioProxyStorage, "remove", checkId, function (argument_list) {\n
+    return argument_list[0];\n
+  });\n
+\n
+  JioProxyStorage.prototype.post = function () {\n
+    var context = this,\n
+      argument_list = arguments;\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        var storage_method = context.__storage.post;\n
+        if (storage_method === undefined) {\n
+          throw new jIO.util.jIOError(\n
+            "Capacity \'post\' is not implemented on \'" + context.__type + "\'",\n
+            501\n
+          );\n
+        }\n
+        return context.__storage.post.apply(context.__storage, argument_list);\n
+      });\n
+  };\n
+\n
+  declareMethod(JioProxyStorage, \'putAttachment\', function (argument_list,\n
+                                                            storage,\n
+                                                            method_name) {\n
+    checkId(argument_list, storage, method_name);\n
+    checkAttachmentId(argument_list, storage, method_name);\n
+\n
+    var options = argument_list[3] || {};\n
+\n
+    if (typeof argument_list[2] === \'string\') {\n
+      argument_list[2] = new Blob([argument_list[2]], {\n
+        "type": options._content_type || options._mimetype ||\n
+                "text/plain;charset=utf-8"\n
+      });\n
+    } else if (!(argument_list[2] instanceof Blob)) {\n
+      throw new jIO.util.jIOError(\n
+        \'Attachment content is not a blob\',\n
+        400\n
+      );\n
+    }\n
+  });\n
+\n
+  declareMethod(JioProxyStorage, \'removeAttachment\', function (argument_list,\n
+                                                               storage,\n
+                                                               method_name) {\n
+    checkId(argument_list, storage, method_name);\n
+    checkAttachmentId(argument_list, storage, method_name);\n
+  });\n
+\n
+  declareMethod(JioProxyStorage, \'getAttachment\', function (argument_list,\n
+                                                            storage,\n
+                                                            method_name) {\n
+    var result = "blob";\n
+//     if (param.storage_spec.type !== "indexeddb" &&\n
+//         param.storage_spec.type !== "dav" &&\n
+//         (param.kwargs._start !== undefined\n
+//          || param.kwargs._end !== undefined)) {\n
+//       restCommandRejecter(param, [\n
+//         \'bad_request\',\n
+//         \'unsupport\',\n
+//         \'_start, _end not support\'\n
+//       ]);\n
+//       return false;\n
+//     }\n
+    checkId(argument_list, storage, method_name);\n
+    checkAttachmentId(argument_list, storage, method_name);\n
+    // Drop optional parameters, which are only used in postfunction\n
+    if (argument_list[2] !== undefined) {\n
+      result = argument_list[2].format || result;\n
+      delete argument_list[2].format;\n
+    }\n
+    return result;\n
+  }, function (argument_list, blob, convert) {\n
+    var result;\n
+    if (!(blob instanceof Blob)) {\n
+      throw new jIO.util.jIOError(\n
+        "\'getAttachment\' (" + argument_list[0] + " , " +\n
+          argument_list[1] + ") on \'" + this.__type +\n
+          "\' does not return a Blob.",\n
+        501\n
+      );\n
+    }\n
+    if (convert === "blob") {\n
+      result = blob;\n
+    } else if (convert === "data_url") {\n
+      result = new RSVP.Queue()\n
+        .push(function () {\n
+          return jIO.util.readBlobAsDataURL(blob);\n
+        })\n
+        .push(function (evt) {\n
+          return evt.target.result;\n
+        });\n
+    } else if (convert === "array_buffer") {\n
+      result = new RSVP.Queue()\n
+        .push(function () {\n
+          return jIO.util.readBlobAsArrayBuffer(blob);\n
+        })\n
+        .push(function (evt) {\n
+          return evt.target.result;\n
+        });\n
+    } else if (convert === "text") {\n
+      result = new RSVP.Queue()\n
+        .push(function () {\n
+          return jIO.util.readBlobAsText(blob);\n
+        })\n
+        .push(function (evt) {\n
+          return evt.target.result;\n
+        });\n
+    } else if (convert === "json") {\n
+      result = new RSVP.Queue()\n
+        .push(function () {\n
+          return jIO.util.readBlobAsText(blob);\n
+        })\n
+        .push(function (evt) {\n
+          return JSON.parse(evt.target.result);\n
+        });\n
+    } else {\n
+      throw new jIO.util.jIOError(\n
+        this.__type + ".getAttachment format: \'" + convert +\n
+          "\' is not supported",\n
+        400\n
+      );\n
+    }\n
+    return result;\n
+  });\n
+\n
+  JioProxyStorage.prototype.buildQuery = function () {\n
+    var storage_method = this.__storage.buildQuery,\n
+      context = this,\n
+      argument_list = arguments;\n
+    if (storage_method === undefined) {\n
+      throw new jIO.util.jIOError(\n
+        "Capacity \'buildQuery\' is not implemented on \'" + this.__type + "\'",\n
+        501\n
+      );\n
+    }\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return storage_method.apply(\n
+          context.__storage,\n
+          argument_list\n
+        );\n
+      });\n
+  };\n
+\n
+  JioProxyStorage.prototype.hasCapacity = function (name) {\n
+    var storage_method = this.__storage.hasCapacity,\n
+      capacity_method = this.__storage[name];\n
+    if (capacity_method !== undefined) {\n
+      return true;\n
+    }\n
+    if ((storage_method === undefined) ||\n
+        !storage_method.apply(this.__storage, arguments)) {\n
+      throw new jIO.util.jIOError(\n
+        "Capacity \'" + name + "\' is not implemented on \'" + this.__type + "\'",\n
+        501\n
+      );\n
+    }\n
+    return true;\n
+  };\n
+\n
+  JioProxyStorage.prototype.allDocs = function (options) {\n
+    var context = this;\n
+    if (options === undefined) {\n
+      options = {};\n
+    }\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        if (context.hasCapacity("list") &&\n
+            ((options.query === undefined) || context.hasCapacity("query")) &&\n
+            ((options.sort_on === undefined) || context.hasCapacity("sort")) &&\n
+            ((options.select_list === undefined) ||\n
+             context.hasCapacity("select")) &&\n
+            ((options.include_docs === undefined) ||\n
+             context.hasCapacity("include")) &&\n
+            ((options.limit === undefined) || context.hasCapacity("limit"))) {\n
+          return context.buildQuery(options);\n
+        }\n
+      })\n
+      .push(function (result) {\n
+        return {\n
+          data: {\n
+            rows: result,\n
+            total_rows: result.length\n
+          }\n
+        };\n
+      });\n
+  };\n
+\n
+  declareMethod(JioProxyStorage, "allAttachments", checkId);\n
+  declareMethod(JioProxyStorage, "repair");\n
+\n
+  JioProxyStorage.prototype.repair = function () {\n
+    var context = this,\n
+      argument_list = arguments;\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        var storage_method = context.__storage.repair;\n
+        if (storage_method !== undefined) {\n
+          return context.__storage.repair.apply(context.__storage,\n
+                                                argument_list);\n
+        }\n
+      });\n
+  };\n
+\n
+  /////////////////////////////////////////////////////////////////\n
+  // Storage builder\n
+  /////////////////////////////////////////////////////////////////\n
+  function JioBuilder() {\n
+    if (!(this instanceof JioBuilder)) {\n
+      return new JioBuilder();\n
+    }\n
+    this.__storage_types = {};\n
+  }\n
+\n
+  JioBuilder.prototype.createJIO = function (storage_spec, util) {\n
+\n
+    if (typeof storage_spec.type !== \'string\') {\n
+      throw new TypeError("Invalid storage description");\n
+    }\n
+    if (!this.__storage_types[storage_spec.type]) {\n
+      throw new TypeError("Unknown storage \'" + storage_spec.type + "\'");\n
+    }\n
+\n
+    return new JioProxyStorage(\n
+      storage_spec.type,\n
+      new this.__storage_types[storage_spec.type](storage_spec, util)\n
+    );\n
+\n
+  };\n
+\n
+  JioBuilder.prototype.addStorage = function (type, Constructor) {\n
+    if (typeof type !== \'string\') {\n
+      throw new TypeError(\n
+        "jIO.addStorage(): Argument 1 is not of type \'string\'"\n
+      );\n
+    }\n
+    if (typeof Constructor !== \'function\') {\n
+      throw new TypeError("jIO.addStorage(): " +\n
+                          "Argument 2 is not of type \'function\'");\n
+    }\n
+    if (this.__storage_types[type] !== undefined) {\n
+      throw new TypeError("jIO.addStorage(): Storage type already exists");\n
+    }\n
+    this.__storage_types[type] = Constructor;\n
+  };\n
+\n
+  JioBuilder.prototype.util = util;\n
+  JioBuilder.prototype.QueryFactory = QueryFactory;\n
+  JioBuilder.prototype.Query = Query;\n
+\n
+  /////////////////////////////////////////////////////////////////\n
+  // global\n
+  /////////////////////////////////////////////////////////////////\n
+  jIO = new JioBuilder();\n
+  window.jIO = jIO;\n
+\n
+}(window, RSVP, Blob, QueryFactory, Query, atob,\n
+  FileReader, ArrayBuffer, Uint8Array));\n
+;/*\n
+ * Rusha, a JavaScript implementation of the Secure Hash Algorithm, SHA-1,\n
+ * as defined in FIPS PUB 180-1, tuned for high performance with large inputs.\n
+ * (http://github.com/srijs/rusha)\n
+ *\n
+ * Inspired by Paul Johnstons implementation (http://pajhome.org.uk/crypt/md5).\n
+ *\n
+ * Copyright (c) 2013 Sam Rijs (http://awesam.de).\n
+ * Released under the terms of the MIT license as follows:\n
+ *\n
+ * Permission is hereby granted, free of charge, to any person obtaining a\n
+ * copy of this software and associated documentation files (the "Software"),\n
+ * to deal in the Software without restriction, including without limitation\n
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,\n
+ * and/or sell copies of the Software, and to permit persons to whom the\n
+ * Software is furnished to do so, subject to the following conditions:\n
+ *\n
+ * The above copyright notice and this permission notice shall be included in\n
+ * all copies or substantial portions of the Software.\n
+ *\n
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n
+ * IN THE SOFTWARE.\n
+ */\n
+(function () {\n
+    // If we\'e running in Node.JS, export a module.\n
+    if (typeof module !== \'undefined\') {\n
+        module.exports = Rusha;\n
+    } else if (typeof window !== \'undefined\') {\n
+        window.Rusha = Rusha;\n
+    }\n
+    // If we\'re running in a webworker, accept\n
+    // messages containing a jobid and a buffer\n
+    // or blob object, and return the hash result.\n
+    if (typeof FileReaderSync !== \'undefined\') {\n
+        var reader = new FileReaderSync(), hasher = new Rusha(4 * 1024 * 1024);\n
+        self.onmessage = function onMessage(event) {\n
+            var hash, data = event.data.data;\n
+            try {\n
+                hash = hasher.digest(data);\n
+                self.postMessage({\n
+                    id: event.data.id,\n
+                    hash: hash\n
+                });\n
+            } catch (e) {\n
+                self.postMessage({\n
+                    id: event.data.id,\n
+                    error: e.name\n
+                });\n
+            }\n
+        };\n
+    }\n
+    var util = {\n
+            getDataType: function (data) {\n
+                if (typeof data === \'string\') {\n
+                    return \'string\';\n
+                }\n
+                if (data instanceof Array) {\n
+                    return \'array\';\n
+                }\n
+                if (typeof global !== \'undefined\' && global.Buffer && global.Buffer.isBuffer(data)) {\n
+                    return \'buffer\';\n
+                }\n
+                if (data instanceof ArrayBuffer) {\n
+                    return \'arraybuffer\';\n
+                }\n
+                if (data.buffer instanceof ArrayBuffer) {\n
+                    return \'view\';\n
+                }\n
+                if (data instanceof Blob) {\n
+                    return \'blob\';\n
+                }\n
+                throw new Error(\'Unsupported data type.\');\n
+            }\n
+        };\n
+    // The Rusha object is a wrapper around the low-level RushaCore.\n
+    // It provides means of converting different inputs to the\n
+    // format accepted by RushaCore as well as other utility methods.\n
+    function Rusha(chunkSize) {\n
+        \'use strict\';\n
+        // Private object structure.\n
+        var self$2 = { fill: 0 };\n
+        // Calculate the length of buffer that the sha1 routine uses\n
+        // including the padding.\n
+        var padlen = function (len) {\n
+            for (len += 9; len % 64 > 0; len += 1);\n
+            return len;\n
+        };\n
+        var padZeroes = function (bin, len) {\n
+            for (var i = len >> 2; i < bin.length; i++)\n
+                bin[i] = 0;\n
+        };\n
+        var padData = function (bin, chunkLen, msgLen) {\n
+            bin[chunkLen >> 2] |= 128 << 24 - (chunkLen % 4 << 3);\n
+            bin[((chunkLen >> 2) + 2 & ~15) + 14] = msgLen >> 29;\n
+            bin[((chunkLen >> 2) + 2 & ~15) + 15] = msgLen << 3;\n
+        };\n
+        // Convert a binary string and write it to the heap.\n
+        // A binary string is expected to only contain char codes < 256.\n
+        var convStr = function (H8, H32, start, len, off) {\n
+            var str = this, i, om = off % 4, lm = len % 4, j = len - lm;\n
+            if (j > 0) {\n
+                switch (om) {\n
+                case 0:\n
+                    H8[off + 3 | 0] = str.charCodeAt(start);\n
+                case 1:\n
+                    H8[off + 2 | 0] = str.charCodeAt(start + 1);\n
+                case 2:\n
+                    H8[off + 1 | 0] = str.charCodeAt(start + 2);\n
+                case 3:\n
+                    H8[off | 0] = str.charCodeAt(start + 3);\n
+                }\n
+            }\n
+            for (i = om; i < j; i = i + 4 | 0) {\n
+                H32[off + i >> 2] = str.charCodeAt(start + i) << 24 | str.charCodeAt(start + i + 1) << 16 | str.charCodeAt(start + i + 2) << 8 | str.charCodeAt(start + i + 3);\n
+            }\n
+            switch (lm) {\n
+            case 3:\n
+                H8[off + j + 1 | 0] = str.charCodeAt(start + j + 2);\n
+            case 2:\n
+                H8[off + j + 2 | 0] = str.charCodeAt(start + j + 1);\n
+            case 1:\n
+                H8[off + j + 3 | 0] = str.charCodeAt(start + j);\n
+            }\n
+        };\n
+        // Convert a buffer or array and write it to the heap.\n
+        // The buffer or array is expected to only contain elements < 256.\n
+        var convBuf = function (H8, H32, start, len, off) {\n
+            var buf = this, i, om = off % 4, lm = len % 4, j = len - lm;\n
+            if (j > 0) {\n
+                switch (om) {\n
+                case 0:\n
+                    H8[off + 3 | 0] = buf[start];\n
+                case 1:\n
+                    H8[off + 2 | 0] = buf[start + 1];\n
+                case 2:\n
+                    H8[off + 1 | 0] = buf[start + 2];\n
+                case 3:\n
+                    H8[off | 0] = buf[start + 3];\n
+                }\n
+            }\n
+            for (i = 4 - om; i < j; i = i += 4 | 0) {\n
+                H32[off + i >> 2] = buf[start + i] << 24 | buf[start + i + 1] << 16 | buf[start + i + 2] << 8 | buf[start + i + 3];\n
+            }\n
+            switch (lm) {\n
+            case 3:\n
+                H8[off + j + 1 | 0] = buf[start + j + 2];\n
+            case 2:\n
+                H8[off + j + 2 | 0] = buf[start + j + 1];\n
+            case 1:\n
+                H8[off + j + 3 | 0] = buf[start + j];\n
+            }\n
+        };\n
+        var convBlob = function (H8, H32, start, len, off) {\n
+            var blob = this, i, om = off % 4, lm = len % 4, j = len - lm;\n
+            var buf = new Uint8Array(reader.readAsArrayBuffer(blob.slice(start, start + len)));\n
+            if (j > 0) {\n
+                switch (om) {\n
+                case 0:\n
+                    H8[off + 3 | 0] = buf[0];\n
+                case 1:\n
+                    H8[off + 2 | 0] = buf[1];\n
+                case 2:\n
+                    H8[off + 1 | 0] = buf[2];\n
+                case 3:\n
+                    H8[off | 0] = buf[3];\n
+                }\n
+            }\n
+            for (i = 4 - om; i < j; i = i += 4 | 0) {\n
+                H32[off + i >> 2] = buf[i] << 24 | buf[i + 1] << 16 | buf[i + 2] << 8 | buf[i + 3];\n
+            }\n
+            switch (lm) {\n
+            case 3:\n
+                H8[off + j + 1 | 0] = buf[j + 2];\n
+            case 2:\n
+                H8[off + j + 2 | 0] = buf[j + 1];\n
+            case 1:\n
+                H8[off + j + 3 | 0] = buf[j];\n
+            }\n
+        };\n
+        var convFn = function (data) {\n
+            switch (util.getDataType(data)) {\n
+            case \'string\':\n
+                return convStr.bind(data);\n
+            case \'array\':\n
+                return convBuf.bind(data);\n
+            case \'buffer\':\n
+                return convBuf.bind(data);\n
+            case \'arraybuffer\':\n
+                return convBuf.bind(new Uint8Array(data));\n
+            case \'view\':\n
+                return convBuf.bind(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));\n
+            case \'blob\':\n
+                return convBlob.bind(data);\n
+            }\n
+        };\n
+        var slice = function (data, offset) {\n
+            switch (util.getDataType(data)) {\n
+            case \'string\':\n
+                return data.slice(offset);\n
+            case \'array\':\n
+                return data.slice(offset);\n
+            case \'buffer\':\n
+                return data.slice(offset);\n
+            case \'arraybuffer\':\n
+                return data.slice(offset);\n
+            case \'view\':\n
+                return data.buffer.slice(offset);\n
+            }\n
+        };\n
+        // Convert an ArrayBuffer into its hexadecimal string representation.\n
+        var hex = function (arrayBuffer) {\n
+            var i, x, hex_tab = \'0123456789abcdef\', res = [], binarray = new Uint8Array(arrayBuffer);\n
+            for (i = 0; i < binarray.length; i++) {\n
+                x = binarray[i];\n
+                res[i] = hex_tab.charAt(x >> 4 & 15) + hex_tab.charAt(x >> 0 & 15);\n
+            }\n
+            return res.join(\'\');\n
+        };\n
+        var ceilHeapSize = function (v) {\n
+            // The asm.js spec says:\n
+            // The heap object\'s byteLength must be either\n
+            // 2^n for n in [12, 24) or 2^24 * n for n ≥ 1.\n
+            // Also, byteLengths smaller than 2^16 are deprecated.\n
+            var p;\n
+            // If v is smaller than 2^16, the smallest possible solution\n
+            // is 2^16.\n
+            if (v <= 65536)\n
+                return 65536;\n
+            // If v < 2^24, we round up to 2^n,\n
+            // otherwise we round up to 2^24 * n.\n
+            if (v < 16777216) {\n
+                for (p = 1; p < v; p = p << 1);\n
+            } else {\n
+                for (p = 16777216; p < v; p += 16777216);\n
+            }\n
+            return p;\n
+        };\n
+        // Initialize the internal data structures to a new capacity.\n
+        var init = function (size) {\n
+            if (size % 64 > 0) {\n
+                throw new Error(\'Chunk size must be a multiple of 128 bit\');\n
+            }\n
+            self$2.maxChunkLen = size;\n
+            self$2.padMaxChunkLen = padlen(size);\n
+            // The size of the heap is the sum of:\n
+            // 1. The padded input message size\n
+            // 2. The extended space the algorithm needs (320 byte)\n
+            // 3. The 160 bit state the algoritm uses\n
+            self$2.heap = new ArrayBuffer(ceilHeapSize(self$2.padMaxChunkLen + 320 + 20));\n
+            self$2.h32 = new Int32Array(self$2.heap);\n
+            self$2.h8 = new Int8Array(self$2.heap);\n
+            self$2.core = RushaCore({\n
+                Int32Array: Int32Array,\n
+                DataView: DataView\n
+            }, {}, self$2.heap);\n
+            self$2.buffer = null;\n
+        };\n
+        // Iinitializethe datastructures according\n
+        // to a chunk siyze.\n
+        init(chunkSize || 64 * 1024);\n
+        var initState = function (heap, padMsgLen) {\n
+            var io = new Int32Array(heap, padMsgLen + 320, 5);\n
+            io[0] = 1732584193;\n
+            io[1] = -271733879;\n
+            io[2] = -1732584194;\n
+            io[3] = 271733878;\n
+            io[4] = -1009589776;\n
+        };\n
+        var padChunk = function (chunkLen, msgLen) {\n
+            var padChunkLen = padlen(chunkLen);\n
+            var view = new Int32Array(self$2.heap, 0, padChunkLen >> 2);\n
+            padZeroes(view, chunkLen);\n
+            padData(view, chunkLen, msgLen);\n
+            return padChunkLen;\n
+        };\n
+        // Write data to the heap.\n
+        var write = function (data, chunkOffset, chunkLen) {\n
+            convFn(data)(self$2.h8, self$2.h32, chunkOffset, chunkLen, 0);\n
+        };\n
+        // Initialize and call the RushaCore,\n
+        // assuming an input buffer of length len * 4.\n
+        var coreCall = function (data, chunkOffset, chunkLen, msgLen, finalize) {\n
+            var padChunkLen = chunkLen;\n
+            if (finalize) {\n
+                padChunkLen = padChunk(chunkLen, msgLen);\n
+            }\n
+            write(data, chunkOffset, chunkLen);\n
+            self$2.core.hash(padChunkLen, self$2.padMaxChunkLen);\n
+        };\n
+        var getRawDigest = function (heap, padMaxChunkLen) {\n
+            var io = new Int32Array(heap, padMaxChunkLen + 320, 5);\n
+            var out = new Int32Array(5);\n
+            var arr = new DataView(out.buffer);\n
+            arr.setInt32(0, io[0], false);\n
+            arr.setInt32(4, io[1], false);\n
+            arr.setInt32(8, io[2], false);\n
+            arr.setInt32(12, io[3], false);\n
+            arr.setInt32(16, io[4], false);\n
+            return out;\n
+        };\n
+        // Calculate the hash digest as an array of 5 32bit integers.\n
+        var rawDigest = this.rawDigest = function (str) {\n
+                var msgLen = str.byteLength || str.length || str.size || 0;\n
+                initState(self$2.heap, self$2.padMaxChunkLen);\n
+                var chunkOffset = 0, chunkLen = self$2.maxChunkLen, last;\n
+                for (chunkOffset = 0; msgLen > chunkOffset + chunkLen; chunkOffset += chunkLen) {\n
+                    coreCall(str, chunkOffset, chunkLen, msgLen, false);\n
+                }\n
+                coreCall(str, chunkOffset, msgLen - chunkOffset, msgLen, true);\n
+                return getRawDigest(self$2.heap, self$2.padMaxChunkLen);\n
+            };\n
+        // The digest and digestFrom* interface returns the hash digest\n
+        // as a hex string.\n
+        this.digest = this.digestFromString = this.digestFromBuffer = this.digestFromArrayBuffer = function (str) {\n
+            return hex(rawDigest(str).buffer);\n
+        };\n
+    }\n
+    ;\n
+    // The low-level RushCore module provides the heart of Rusha,\n
+    // a high-speed sha1 implementation working on an Int32Array heap.\n
+    // At first glance, the implementation seems complicated, however\n
+    // with the SHA1 spec at hand, it is obvious this almost a textbook\n
+    // implementation that has a few functions hand-inlined and a few loops\n
+    // hand-unrolled.\n
+    function RushaCore(stdlib, foreign, heap) {\n
+        \'use asm\';\n
+        var H = new stdlib.Int32Array(heap);\n
+        function hash(k, x) {\n
+            // k in bytes\n
+            k = k | 0;\n
+            x = x | 0;\n
+            var i = 0, j = 0, y0 = 0, z0 = 0, y1 = 0, z1 = 0, y2 = 0, z2 = 0, y3 = 0, z3 = 0, y4 = 0, z4 = 0, t0 = 0, t1 = 0;\n
+            y0 = H[x + 320 >> 2] | 0;\n
+            y1 = H[x + 324 >> 2] | 0;\n
+            y2 = H[x + 328 >> 2] | 0;\n
+            y3 = H[x + 332 >> 2] | 0;\n
+            y4 = H[x + 336 >> 2] | 0;\n
+            for (i = 0; (i | 0) < (k | 0); i = i + 64 | 0) {\n
+                z0 = y0;\n
+                z1 = y1;\n
+                z2 = y2;\n
+                z3 = y3;\n
+                z4 = y4;\n
+                for (j = 0; (j | 0) < 64; j = j + 4 | 0) {\n
+                    t1 = H[i + j >> 2] | 0;\n
+                    t0 = ((y0 << 5 | y0 >>> 27) + (y1 & y2 | ~y1 & y3) | 0) + ((t1 + y4 | 0) + 1518500249 | 0) | 0;\n
+                    y4 = y3;\n
+                    y3 = y2;\n
+                    y2 = y1 << 30 | y1 >>> 2;\n
+                    y1 = y0;\n
+                    y0 = t0;\n
+                    ;\n
+                    H[k + j >> 2] = t1;\n
+                }\n
+                for (j = k + 64 | 0; (j | 0) < (k + 80 | 0); j = j + 4 | 0) {\n
+                    t1 = (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) << 1 | (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) >>> 31;\n
+                    t0 = ((y0 << 5 | y0 >>> 27) + (y1 & y2 | ~y1 & y3) | 0) + ((t1 + y4 | 0) + 1518500249 | 0) | 0;\n
+                    y4 = y3;\n
+                    y3 = y2;\n
+                    y2 = y1 << 30 | y1 >>> 2;\n
+                    y1 = y0;\n
+                    y0 = t0;\n
+                    ;\n
+                    H[j >> 2] = t1;\n
+                }\n
+                for (j = k + 80 | 0; (j | 0) < (k + 160 | 0); j = j + 4 | 0) {\n
+                    t1 = (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) << 1 | (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) >>> 31;\n
+                    t0 = ((y0 << 5 | y0 >>> 27) + (y1 ^ y2 ^ y3) | 0) + ((t1 + y4 | 0) + 1859775393 | 0) | 0;\n
+                    y4 = y3;\n
+                    y3 = y2;\n
+                    y2 = y1 << 30 | y1 >>> 2;\n
+                    y1 = y0;\n
+                    y0 = t0;\n
+                    ;\n
+                    H[j >> 2] = t1;\n
+                }\n
+                for (j = k + 160 | 0; (j | 0) < (k + 240 | 0); j = j + 4 | 0) {\n
+                    t1 = (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) << 1 | (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) >>> 31;\n
+                    t0 = ((y0 << 5 | y0 >>> 27) + (y1 & y2 | y1 & y3 | y2 & y3) | 0) + ((t1 + y4 | 0) - 1894007588 | 0) | 0;\n
+                    y4 = y3;\n
+                    y3 = y2;\n
+                    y2 = y1 << 30 | y1 >>> 2;\n
+                    y1 = y0;\n
+                    y0 = t0;\n
+                    ;\n
+                    H[j >> 2] = t1;\n
+                }\n
+                for (j = k + 240 | 0; (j | 0) < (k + 320 | 0); j = j + 4 | 0) {\n
+                    t1 = (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) << 1 | (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) >>> 31;\n
+                    t0 = ((y0 << 5 | y0 >>> 27) + (y1 ^ y2 ^ y3) | 0) + ((t1 + y4 | 0) - 899497514 | 0) | 0;\n
+                    y4 = y3;\n
+                    y3 = y2;\n
+                    y2 = y1 << 30 | y1 >>> 2;\n
+                    y1 = y0;\n
+                    y0 = t0;\n
+                    ;\n
+                    H[j >> 2] = t1;\n
+                }\n
+                y0 = y0 + z0 | 0;\n
+                y1 = y1 + z1 | 0;\n
+                y2 = y2 + z2 | 0;\n
+                y3 = y3 + z3 | 0;\n
+                y4 = y4 + z4 | 0;\n
+            }\n
+            H[x + 320 >> 2] = y0;\n
+            H[x + 324 >> 2] = y1;\n
+            H[x + 328 >> 2] = y2;\n
+            H[x + 332 >> 2] = y3;\n
+            H[x + 336 >> 2] = y4;\n
+        }\n
+        return { hash: hash };\n
+    }\n
+}());;/*\n
+ * JIO extension for resource replication.\n
+ * Copyright (C) 2013, 2015  Nexedi SA\n
+ *\n
+ *   This library is free software: you can redistribute it and/or modify\n
+ *   it under the terms of the GNU Lesser General Public License as published by\n
+ *   the Free Software Foundation, either version 3 of the License, or\n
+ *   (at your option) any later version.\n
+ *\n
+ *   This library is distributed in the hope that it will be useful,\n
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of\n
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n
+ *   GNU Lesser General Public License for more details.\n
+ *\n
+ *   You should have received a copy of the GNU Lesser General Public License\n
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n
+ */\n
+\n
+/*jslint nomen: true*/\n
+/*global jIO, RSVP, Rusha*/\n
+\n
+(function (jIO, RSVP, Rusha) {\n
+  "use strict";\n
+\n
+  var rusha = new Rusha(),\n
+    CONFLICT_THROW = 0,\n
+    CONFLICT_KEEP_LOCAL = 1,\n
+    CONFLICT_KEEP_REMOTE = 2,\n
+    CONFLICT_CONTINUE = 3;\n
+\n
+  /****************************************************\n
+   Use a local jIO to read/write/search documents\n
+   Synchronize in background those document with a remote jIO.\n
+   Synchronization status is stored for each document as an local attachment.\n
+  ****************************************************/\n
+\n
+  function generateHash(content) {\n
+    // XXX Improve performance by moving calculation to WebWorker\n
+    return rusha.digestFromString(content);\n
+  }\n
+\n
+  function ReplicateStorage(spec) {\n
+    this._query_options = spec.query || {};\n
+\n
+    this._local_sub_storage = jIO.createJIO(spec.local_sub_storage);\n
+    this._remote_sub_storage = jIO.createJIO(spec.remote_sub_storage);\n
+\n
+    this._signature_hash = "_replicate_" + generateHash(\n
+      JSON.stringify(spec.local_sub_storage) +\n
+        JSON.stringify(spec.remote_sub_storage) +\n
+        JSON.stringify(this._query_options)\n
+    );\n
+    this._signature_sub_storage = jIO.createJIO({\n
+      type: "document",\n
+      document_id: this._signature_hash,\n
+      sub_storage: spec.local_sub_storage\n
+    });\n
+\n
+    this._use_remote_post = spec.use_remote_post || false;\n
+\n
+    this._conflict_handling = spec.conflict_handling || 0;\n
+    // 0: no resolution (ie, throw an Error)\n
+    // 1: keep the local state\n
+    //    (overwrites the remote document with local content)\n
+    //    (delete remote document if local is deleted)\n
+    // 2: keep the remote state\n
+    //    (overwrites the local document with remote content)\n
+    //    (delete local document if remote is deleted)\n
+    // 3: keep both copies (leave documents untouched, no signature update)\n
+    if ((this._conflict_handling !== CONFLICT_THROW) &&\n
+        (this._conflict_handling !== CONFLICT_KEEP_LOCAL) &&\n
+        (this._conflict_handling !== CONFLICT_KEEP_REMOTE) &&\n
+        (this._conflict_handling !== CONFLICT_CONTINUE)) {\n
+      throw new jIO.util.jIOError("Unsupported conflict handling: " +\n
+                                  this._conflict_handling, 400);\n
+    }\n
+\n
+    this._check_local_modification = spec.check_local_modification;\n
+    if (this._check_local_modification === undefined) {\n
+      this._check_local_modification = true;\n
+    }\n
+    this._check_local_creation = spec.check_local_creation;\n
+    if (this._check_local_creation === undefined) {\n
+      this._check_local_creation = true;\n
+    }\n
+    this._check_local_deletion = spec.check_local_deletion;\n
+    if (this._check_local_deletion === undefined) {\n
+      this._check_local_deletion = true;\n
+    }\n
+    this._check_remote_modification = spec.check_remote_modification;\n
+    if (this._check_remote_modification === undefined) {\n
+      this._check_remote_modification = true;\n
+    }\n
+    this._check_remote_creation = spec.check_remote_creation;\n
+    if (this._check_remote_creation === undefined) {\n
+      this._check_remote_creation = true;\n
+    }\n
+    this._check_remote_deletion = spec.check_remote_deletion;\n
+    if (this._check_remote_deletion === undefined) {\n
+      this._check_remote_deletion = true;\n
+    }\n
+  }\n
+\n
+  ReplicateStorage.prototype.remove = function (id) {\n
+    if (id === this._signature_hash) {\n
+      throw new jIO.util.jIOError(this._signature_hash + " is frozen",\n
+                                  403);\n
+    }\n
+    return this._local_sub_storage.remove.apply(this._local_sub_storage,\n
+                                                arguments);\n
+  };\n
+  ReplicateStorage.prototype.post = function () {\n
+    return this._local_sub_storage.post.apply(this._local_sub_storage,\n
+                                              arguments);\n
+  };\n
+  ReplicateStorage.prototype.put = function (id) {\n
+    if (id === this._signature_hash) {\n
+      throw new jIO.util.jIOError(this._signature_hash + " is frozen",\n
+                                  403);\n
+    }\n
+    return this._local_sub_storage.put.apply(this._local_sub_storage,\n
+                                             arguments);\n
+  };\n
+  ReplicateStorage.prototype.get = function () {\n
+    return this._local_sub_storage.get.apply(this._local_sub_storage,\n
+                                             arguments);\n
+  };\n
+  ReplicateStorage.prototype.hasCapacity = function () {\n
+    return this._local_sub_storage.hasCapacity.apply(this._local_sub_storage,\n
+                                                     arguments);\n
+  };\n
+  ReplicateStorage.prototype.buildQuery = function () {\n
+    // XXX Remove signature document?\n
+    return this._local_sub_storage.buildQuery.apply(this._local_sub_storage,\n
+                                                    arguments);\n
+  };\n
+\n
+  ReplicateStorage.prototype.repair = function () {\n
+    var context = this,\n
+      argument_list = arguments,\n
+      skip_document_dict = {};\n
+\n
+    // Do not sync the signature document\n
+    skip_document_dict[context._signature_hash] = null;\n
+\n
+    function propagateModification(source, destination, doc, hash, id,\n
+                                   options) {\n
+      var result,\n
+        post_id,\n
+        to_skip = true;\n
+      if (options === undefined) {\n
+        options = {};\n
+      }\n
+      if (options.use_post) {\n
+        result = destination.post(doc)\n
+          .push(function (new_id) {\n
+            to_skip = false;\n
+            post_id = new_id;\n
+            return source.put(post_id, doc);\n
+          })\n
+          .push(function () {\n
+            return source.remove(id);\n
+          })\n
+          .push(function () {\n
+            return context._signature_sub_storage.remove(id);\n
+          })\n
+          .push(function () {\n
+            to_skip = true;\n
+            return context._signature_sub_storage.put(post_id, {\n
+              "hash": hash\n
+            });\n
+          })\n
+          .push(function () {\n
+            skip_document_dict[post_id] = null;\n
+          });\n
+      } else {\n
+        result = destination.put(id, doc)\n
+          .push(function () {\n
+            return context._signature_sub_storage.put(id, {\n
+              "hash": hash\n
+            });\n
+          });\n
+      }\n
+      return result\n
+        .push(function () {\n
+          if (to_skip) {\n
+            skip_document_dict[id] = null;\n
+          }\n
+        });\n
+    }\n
+\n
+    function checkLocalCreation(queue, source, destination, id, options,\n
+                                getMethod) {\n
+      var remote_doc;\n
+      queue\n
+        .push(function () {\n
+          return destination.get(id);\n
+        })\n
+        .push(function (doc) {\n
+          remote_doc = doc;\n
+        }, function (error) {\n
+          if ((error instanceof jIO.util.jIOError) &&\n
+              (error.status_code === 404)) {\n
+            // This document was never synced.\n
+            // Push it to the remote storage and store sync information\n
+            return;\n
+          }\n
+          throw error;\n
+        })\n
+        .push(function () {\n
+          // This document was never synced.\n
+          // Push it to the remote storage and store sync information\n
+          return getMethod(id);\n
+        })\n
+        .push(function (doc) {\n
+          var local_hash = generateHash(JSON.stringify(doc)),\n
+            remote_hash;\n
+          if (remote_doc === undefined) {\n
+            return propagateModification(source, destination, doc, local_hash,\n
+                                         id, options);\n
+          }\n
+\n
+          remote_hash = generateHash(JSON.stringify(remote_doc));\n
+          if (local_hash === remote_hash) {\n
+            // Same document\n
+            return context._signature_sub_storage.put(id, {\n
+              "hash": local_hash\n
+            })\n
+              .push(function () {\n
+                skip_document_dict[id] = null;\n
+              });\n
+          }\n
+          if (options.conflict_ignore === true) {\n
+            return;\n
+          }\n
+          if (options.conflict_force === true) {\n
+            return propagateModification(source, destination, doc, local_hash,\n
+                                         id, options);\n
+          }\n
+          // Already exists on destination\n
+          throw new jIO.util.jIOError("Conflict on \'" + id + "\'",\n
+                                      409);\n
+        });\n
+    }\n
+\n
+    function checkBulkLocalCreation(queue, source, destination, id_list,\n
+                                    options) {\n
+      queue\n
+        .push(function () {\n
+          return source.bulk(id_list);\n
+        })\n
+        .push(function (result_list) {\n
+          var i,\n
+            sub_queue = new RSVP.Queue();\n
+\n
+          function getResult(j) {\n
+            return function (id) {\n
+              if (id !== id_list[j].parameter_list[0]) {\n
+                throw new Error("Does not access expected ID " + id);\n
+              }\n
+              return result_list[j];\n
+            };\n
+          }\n
+\n
+          for (i = 0; i < result_list.length; i += 1) {\n
+            checkLocalCreation(sub_queue, source, destination,\n
+                               id_list[i].parameter_list[0],\n
+                               options, getResult(i));\n
+          }\n
+          return sub_queue;\n
+        });\n
+    }\n
+\n
+    function checkLocalDeletion(queue, destination, id, source) {\n
+      var status_hash;\n
+      queue\n
+        .push(function () {\n
+          return context._signature_sub_storage.get(id);\n
+        })\n
+        .push(function (result) {\n
+          status_hash = result.hash;\n
+          return destination.get(id)\n
+            .push(function (doc) {\n
+              var remote_hash = generateHash(JSON.stringify(doc));\n
+              if (remote_hash === status_hash) {\n
+                return destination.remove(id)\n
+                  .push(function () {\n
+                    return context._signature_sub_storage.remove(id);\n
+                  })\n
+                  .push(function () {\n
+                    skip_document_dict[id] = null;\n
+                  });\n
+              }\n
+              // Modifications on remote side\n
+              // Push them locally\n
+              return propagateModification(destination, source, doc,\n
+                                           remote_hash, id);\n
+            }, function (error) {\n
+              if ((error instanceof jIO.util.jIOError) &&\n
+                  (error.status_code === 404)) {\n
+                return context._signature_sub_storage.remove(id)\n
+                  .push(function () {\n
+                    skip_document_dict[id] = null;\n
+                  });\n
+              }\n
+              throw error;\n
+            });\n
+        });\n
+    }\n
+\n
+    function checkSignatureDifference(queue, source, destination, id,\n
+                                      conflict_force, conflict_ignore) {\n
+      queue\n
+        .push(function () {\n
+          return RSVP.all([\n
+            source.get(id),\n
+            context._signature_sub_storage.get(id)\n
+          ]);\n
+        })\n
+        .push(function (result_list) {\n
+          var doc = result_list[0],\n
+            local_hash = generateHash(JSON.stringify(doc)),\n
+            status_hash = result_list[1].hash;\n
+\n
+          if (local_hash !== status_hash) {\n
+            // Local modifications\n
+            return destination.get(id)\n
+              .push(function (remote_doc) {\n
+                var remote_hash = generateHash(JSON.stringify(remote_doc));\n
+                if (remote_hash !== status_hash) {\n
+                  // Modifications on both sides\n
+                  if (local_hash === remote_hash) {\n
+                    // Same modifications on both side \\o/\n
+                    return context._signature_sub_storage.put(id, {\n
+                      "hash": local_hash\n
+                    })\n
+                      .push(function () {\n
+                        skip_document_dict[id] = null;\n
+                      });\n
+                  }\n
+                  if (conflict_ignore === true) {\n
+                    return;\n
+                  }\n
+                  if (conflict_force !== true) {\n
+                    throw new jIO.util.jIOError("Conflict on \'" + id + "\'",\n
+                                                409);\n
+                  }\n
+                }\n
+                return propagateModification(source, destination, doc,\n
+                                             local_hash, id);\n
+              }, function (error) {\n
+                if ((error instanceof jIO.util.jIOError) &&\n
+                    (error.status_code === 404)) {\n
+                  // Document has been deleted remotely\n
+                  return propagateModification(source, destination, doc,\n
+                                               local_hash, id);\n
+                }\n
+                throw error;\n
+              });\n
+          }\n
+        });\n
+    }\n
+\n
+    function pushStorage(source, destination, options) {\n
+      var queue = new RSVP.Queue();\n
+      if (!options.hasOwnProperty("use_post")) {\n
+        options.use_post = false;\n
+      }\n
+      return queue\n
+        .push(function () {\n
+          return RSVP.all([\n
+            source.allDocs(context._query_options),\n
+            context._signature_sub_storage.allDocs()\n
+          ]);\n
+        })\n
+        .push(function (result_list) {\n
+          var i,\n
+            local_dict = {},\n
+            new_list = [],\n
+            signature_dict = {},\n
+            key;\n
+          for (i = 0; i < result_list[0].data.total_rows; i += 1) {\n
+            if (!skip_document_dict.hasOwnProperty(\n
+                result_list[0].data.rows[i].id\n
+              )) {\n
+              local_dict[result_list[0].data.rows[i].id] = i;\n
+            }\n
+          }\n
+          for (i = 0; i < result_list[1].data.total_rows; i += 1) {\n
+            if (!skip_document_dict.hasOwnProperty(\n
+                result_list[1].data.rows[i].id\n
+              )) {\n
+              signature_dict[result_list[1].data.rows[i].id] = i;\n
+            }\n
+          }\n
+\n
+          if (options.check_creation === true) {\n
+            for (key in local_dict) {\n
+              if (local_dict.hasOwnProperty(key)) {\n
+                if (!signature_dict.hasOwnProperty(key)) {\n
+                  if (options.use_bulk_get === true) {\n
+                    new_list.push({\n
+                      method: "get",\n
+                      parameter_list: [key]\n
+                    });\n
+                  } else {\n
+                    checkLocalCreation(queue, source, destination, key,\n
+                                       options, source.get.bind(source));\n
+                  }\n
+                }\n
+              }\n
+            }\n
+            if ((options.use_bulk_get === true) && (new_list.length !== 0)) {\n
+              checkBulkLocalCreation(queue, source, destination, new_list,\n
+                                     options);\n
+            }\n
+          }\n
+          for (key in signature_dict) {\n
+            if (signature_dict.hasOwnProperty(key)) {\n
+              if (local_dict.hasOwnProperty(key)) {\n
+                if (options.check_modification === true) {\n
+                  checkSignatureDifference(queue, source, destination, key,\n
+                                           options.conflict_force,\n
+                                           options.conflict_ignore);\n
+                }\n
+              } else {\n
+                if (options.check_deletion === true) {\n
+                  checkLocalDeletion(queue, destination, key, source);\n
+                }\n
+              }\n
+            }\n
+          }\n
+        });\n
+    }\n
+\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        // Ensure that the document storage is usable\n
+        return context._signature_sub_storage.__storage._sub_storage.get(\n
+          context._signature_hash\n
+        );\n
+      })\n
+      .push(undefined, function (error) {\n
+        if ((error instanceof jIO.util.jIOError) &&\n
+            (error.status_code === 404)) {\n
+          return context._signature_sub_storage.__storage._sub_storage.put(\n
+            context._signature_hash,\n
+            {}\n
+          );\n
+        }\n
+        throw error;\n
+      })\n
+\n
+      .push(function () {\n
+        return RSVP.all([\n
+// Don\'t repair local_sub_storage twice\n
+//           context._signature_sub_storage.repair.apply(\n
+//             context._signature_sub_storage,\n
+//             argument_list\n
+//           ),\n
+          context._local_sub_storage.repair.apply(\n
+            context._local_sub_storage,\n
+            argument_list\n
+          ),\n
+          context._remote_sub_storage.repair.apply(\n
+            context._remote_sub_storage,\n
+            argument_list\n
+          )\n
+        ]);\n
+      })\n
+\n
+      .push(function () {\n
+        if (context._check_local_modification ||\n
+            context._check_local_creation ||\n
+            context._check_local_deletion) {\n
+          return pushStorage(context._local_sub_storage,\n
+                             context._remote_sub_storage,\n
+                             {\n
+              use_post: context._use_remote_post,\n
+              conflict_force: (context._conflict_handling ===\n
+                               CONFLICT_KEEP_LOCAL),\n
+              conflict_ignore: ((context._conflict_handling ===\n
+                                 CONFLICT_CONTINUE) ||\n
+                                (context._conflict_handling ===\n
+                                 CONFLICT_KEEP_REMOTE)),\n
+              check_modification: context._check_local_modification,\n
+              check_creation: context._check_local_creation,\n
+              check_deletion: context._check_local_deletion\n
+            });\n
+        }\n
+      })\n
+      .push(function () {\n
+        // Autoactivate bulk if substorage implements it\n
+        // Keep it like this until the bulk API is stabilized\n
+        var use_bulk_get = false;\n
+        try {\n
+          use_bulk_get = context._remote_sub_storage.hasCapacity("bulk");\n
+        } catch (error) {\n
+          if (!((error instanceof jIO.util.jIOError) &&\n
+               (error.status_code === 501))) {\n
+            throw error;\n
+          }\n
+        }\n
+        if (context._check_remote_modification ||\n
+            context._check_remote_creation ||\n
+            context._check_remote_deletion) {\n
+          return pushStorage(context._remote_sub_storage,\n
+                             context._local_sub_storage, {\n
+              use_bulk_get: use_bulk_get,\n
+              conflict_force: (context._conflict_handling ===\n
+                               CONFLICT_KEEP_REMOTE),\n
+              conflict_ignore: (context._conflict_handling ===\n
+                                CONFLICT_CONTINUE),\n
+              check_modification: context._check_remote_modification,\n
+              check_creation: context._check_remote_creation,\n
+              check_deletion: context._check_remote_deletion\n
+            });\n
+        }\n
+      });\n
+  };\n
+\n
+  jIO.addStorage(\'replicate\', ReplicateStorage);\n
+\n
+}(jIO, RSVP, Rusha));\n
+;/*\n
+ * Copyright 2015, Nexedi SA\n
+ * Released under the LGPL license.\n
+ * http://www.gnu.org/licenses/lgpl.html\n
+ */\n
+\n
+/*jslint nomen: true*/\n
+/*global Rusha*/\n
+\n
+/**\n
+ * JIO Sha Storage. Type = \'sha\'.\n
+ */\n
+\n
+(function (Rusha) {\n
+  "use strict";\n
+\n
+  var rusha = new Rusha();\n
+\n
+  function ShaStorage(spec) {\n
+    this._sub_storage = jIO.createJIO(spec.sub_storage);\n
+  }\n
+\n
+  ShaStorage.prototype.post = function (param) {\n
+    return this._sub_storage.put(\n
+      rusha.digestFromString(JSON.stringify(param)),\n
+      param\n
+    );\n
+  };\n
+\n
+  ShaStorage.prototype.get = function () {\n
+    return this._sub_storage.get.apply(this._sub_storage, arguments);\n
+  };\n
+  ShaStorage.prototype.remove = function () {\n
+    return this._sub_storage.remove.apply(this._sub_storage, arguments);\n
+  };\n
+  ShaStorage.prototype.hasCapacity = function () {\n
+    return this._sub_storage.hasCapacity.apply(this._sub_storage, arguments);\n
+  };\n
+  ShaStorage.prototype.buildQuery = function () {\n
+    return this._sub_storage.buildQuery.apply(this._sub_storage, arguments);\n
+  };\n
+  ShaStorage.prototype.getAttachment = function () {\n
+    return this._sub_storage.getAttachment.apply(this._sub_storage, arguments);\n
+  };\n
+  ShaStorage.prototype.putAttachment = function () {\n
+    return this._sub_storage.putAttachment.apply(this._sub_storage, arguments);\n
+  };\n
+  ShaStorage.prototype.removeAttachment = function () {\n
+    return this._sub_storage.removeAttachment.apply(this._sub_storage,\n
+                                                    arguments);\n
+  };\n
+  ShaStorage.prototype.allAttachments = function () {\n
+    return this._sub_storage.allAttachments.apply(this._sub_storage, arguments);\n
+  };\n
+  ShaStorage.prototype.repair = function () {\n
+    return this._sub_storage.repair.apply(this._sub_storage, arguments);\n
+  };\n
+\n
+  jIO.addStorage(\'sha\', ShaStorage);\n
+\n
+}(Rusha));\n
+;/*jslint nomen: true*/\n
+(function (jIO) {\n
+  "use strict";\n
+\n
+  /**\n
+   * The jIO UUIDStorage extension\n
+   *\n
+   * @class UUIDStorage\n
+   * @constructor\n
+   */\n
+  function UUIDStorage(spec) {\n
+    this._sub_storage = jIO.createJIO(spec.sub_storage);\n
+  }\n
+\n
+  UUIDStorage.prototype.get = function () {\n
+    return this._sub_storage.get.apply(this._sub_storage, arguments);\n
+  };\n
+  UUIDStorage.prototype.allAttachments = function () {\n
+    return this._sub_storage.allAttachments.apply(this._sub_storage, arguments);\n
+  };\n
+  UUIDStorage.prototype.post = function (param) {\n
+\n
+    function S4() {\n
+      return (\'0000\' + Math.floor(\n
+        Math.random() * 0x10000 /* 65536 */\n
+      ).toString(16)).slice(-4);\n
+    }\n
+\n
+    var id = S4() + S4() + "-" +\n
+      S4() + "-" +\n
+      S4() + "-" +\n
+      S4() + "-" +\n
+      S4() + S4() + S4();\n
+\n
+    return this.put(id, param);\n
+  };\n
+  UUIDStorage.prototype.put = function () {\n
+    return this._sub_storage.put.apply(this._sub_storage, arguments);\n
+  };\n
+  UUIDStorage.prototype.remove = function () {\n
+    return this._sub_storage.remove.apply(this._sub_storage, arguments);\n
+  };\n
+  UUIDStorage.prototype.getAttachment = function () {\n
+    return this._sub_storage.getAttachment.apply(this._sub_storage, arguments);\n
+  };\n
+  UUIDStorage.prototype.putAttachment = function () {\n
+    return this._sub_storage.putAttachment.apply(this._sub_storage, arguments);\n
+  };\n
+  UUIDStorage.prototype.removeAttachment = function () {\n
+    return this._sub_storage.removeAttachment.apply(this._sub_storage,\n
+                                                    arguments);\n
+  };\n
+  UUIDStorage.prototype.repair = function () {\n
+    return this._sub_storage.repair.apply(this._sub_storage, arguments);\n
+  };\n
+  UUIDStorage.prototype.hasCapacity = function (name) {\n
+    return this._sub_storage.hasCapacity(name);\n
+  };\n
+  UUIDStorage.prototype.buildQuery = function () {\n
+    return this._sub_storage.buildQuery.apply(this._sub_storage,\n
+                                              arguments);\n
+  };\n
+\n
+  jIO.addStorage(\'uuid\', UUIDStorage);\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*/\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.put = function (id, metadata) {\n
+    if (!this._database.hasOwnProperty(id)) {\n
+      this._database[id] = {\n
+        attachments: {}\n
+      };\n
+    }\n
+    this._database[id].doc = metadata;\n
+    return id;\n
+  };\n
+\n
+  MemoryStorage.prototype.get = function (id) {\n
+    try {\n
+      return this._database[id].doc;\n
+    } catch (error) {\n
+      if (error instanceof TypeError) {\n
+        throw new jIO.util.jIOError(\n
+          "Cannot find document: " + id,\n
+          404\n
+        );\n
+      }\n
+      throw error;\n
+    }\n
+  };\n
+\n
+  MemoryStorage.prototype.allAttachments = function (id) {\n
+    var key,\n
+      attachments = {};\n
+    try {\n
+      for (key in this._database[id].attachments) {\n
+        if (this._database[id].attachments.hasOwnProperty(key)) {\n
+          attachments[key] = {};\n
+        }\n
+      }\n
+    } catch (error) {\n
+      if (error instanceof TypeError) {\n
+        throw new jIO.util.jIOError(\n
+          "Cannot find document: " + id,\n
+          404\n
+        );\n
+      }\n
+      throw error;\n
+    }\n
+    return attachments;\n
+  };\n
+\n
+  MemoryStorage.prototype.remove = function (id) {\n
+    delete this._database[id];\n
+    return id;\n
+  };\n
+\n
+  MemoryStorage.prototype.getAttachment = function (id, name) {\n
+    try {\n
+      var result = this._database[id].attachments[name];\n
+      if (result === undefined) {\n
+        throw new jIO.util.jIOError(\n
+          "Cannot find attachment: " + id + " , " + name,\n
+          404\n
+        );\n
+      }\n
+      return result;\n
+    } catch (error) {\n
+      if (error instanceof TypeError) {\n
+        throw new jIO.util.jIOError(\n
+          "Cannot find attachment: " + id + " , " + name,\n
+          404\n
+        );\n
+      }\n
+      throw error;\n
+    }\n
+  };\n
+\n
+  MemoryStorage.prototype.putAttachment = function (id, name, blob) {\n
+    var attachment_dict;\n
+    try {\n
+      attachment_dict = this._database[id].attachments;\n
+    } catch (error) {\n
+      if (error instanceof TypeError) {\n
+        throw new jIO.util.jIOError("Cannot find document: " + id, 404);\n
+      }\n
+      throw error;\n
+    }\n
+    attachment_dict[name] = blob;\n
+  };\n
+\n
+  MemoryStorage.prototype.removeAttachment = function (id, name) {\n
+    try {\n
+      delete this._database[id].attachments[name];\n
+    } catch (error) {\n
+      if (error instanceof TypeError) {\n
+        throw new jIO.util.jIOError(\n
+          "Cannot find document: " + id,\n
+          404\n
+        );\n
+      }\n
+      throw error;\n
+    }\n
+  };\n
+\n
+\n
+  MemoryStorage.prototype.hasCapacity = function (name) {\n
+    return ((name === "list") || (name === "include"));\n
+  };\n
+\n
+  MemoryStorage.prototype.buildQuery = function (options) {\n
+    var rows = [],\n
+      i;\n
+    for (i in this._database) {\n
+      if (this._database.hasOwnProperty(i)) {\n
+        if (options.include_docs === true) {\n
+          rows.push({\n
+            id: i,\n
+            value: {},\n
+            doc: this._database[i]\n
+          });\n
+        } else {\n
+          rows.push({\n
+            id: i,\n
+            value: {}\n
+          });\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, sessionStorage, localStorage, RSVP */\n
+\n
+/**\n
+ * JIO Local Storage. Type = \'local\'.\n
+ * Local browser "database" storage.\n
+ *\n
+ * Storage Description:\n
+ *\n
+ *     {\n
+ *       "type": "local",\n
+ *       "sessiononly": false\n
+ *     }\n
+ *\n
+ * @class LocalStorage\n
+ */\n
+\n
+(function (jIO, sessionStorage, localStorage, RSVP) {\n
+  "use strict";\n
+\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
+    if (id !== "/") {\n
+      throw new jIO.util.jIOError("id " + id + " is forbidden (!== /)",\n
+                                  400);\n
+    }\n
+  }\n
+\n
+  LocalStorage.prototype.get = function (id) {\n
+    restrictDocumentId(id);\n
+    return {};\n
+  };\n
+\n
+  LocalStorage.prototype.allAttachments = function (id) {\n
+    restrictDocumentId(id);\n
+\n
+    var attachments = {},\n
+      key;\n
+\n
+    for (key in this._storage) {\n
+      if (this._storage.hasOwnProperty(key)) {\n
+        attachments[key] = {};\n
+      }\n
+    }\n
+    return attachments;\n
+  };\n
+\n
+  LocalStorage.prototype.getAttachment = function (id, name) {\n
+    restrictDocumentId(id);\n
+\n
+    var textstring = this._storage.getItem(name);\n
+\n
+    if (textstring === null) {\n
+      throw new jIO.util.jIOError(\n
+        "Cannot find attachment " + name,\n
+        404\n
+      );\n
+    }\n
+    return jIO.util.dataURItoBlob(textstring);\n
+  };\n
+\n
+  LocalStorage.prototype.putAttachment = function (id, name, blob) {\n
+    var context = this;\n
+    restrictDocumentId(id);\n
+\n
+    // the document already exists\n
+    // download data\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return jIO.util.readBlobAsDataURL(blob);\n
+      })\n
+      .push(function (e) {\n
+        context._storage.setItem(name, e.target.result);\n
+      });\n
+  };\n
+\n
+  LocalStorage.prototype.removeAttachment = function (id, name) {\n
+    restrictDocumentId(id);\n
+    return this._storage.removeItem(name);\n
+  };\n
+\n
+\n
+  LocalStorage.prototype.hasCapacity = function (name) {\n
+    return (name === "list");\n
+  };\n
+\n
+  LocalStorage.prototype.buildQuery = function () {\n
+    return [{\n
+      id: "/",\n
+      value: {}\n
+    }];\n
+  };\n
+\n
+  jIO.addStorage(\'local\', LocalStorage);\n
+\n
+}(jIO, sessionStorage, localStorage, RSVP));\n
+;/*jslint nomen: true*/\n
+/*global RSVP, Blob, LZString, DOMException*/\n
+(function (RSVP, Blob, LZString, DOMException) {\n
+  "use strict";\n
+\n
+  /**\n
+   * The jIO ZipStorage extension\n
+   *\n
+   * @class ZipStorage\n
+   * @constructor\n
+   */\n
+\n
+  var MIME_TYPE = "application/x-jio-utf16_lz_string";\n
+\n
+  function ZipStorage(spec) {\n
+    this._sub_storage = jIO.createJIO(spec.sub_storage);\n
+  }\n
+\n
+  ZipStorage.prototype.get = function () {\n
+    return this._sub_storage.get.apply(this._sub_storage,\n
+                                        arguments);\n
+  };\n
+\n
+  ZipStorage.prototype.post = function () {\n
+    return this._sub_storage.post.apply(this._sub_storage,\n
+                                        arguments);\n
+  };\n
+\n
+  ZipStorage.prototype.put = function () {\n
+    return this._sub_storage.put.apply(this._sub_storage,\n
+                                       arguments);\n
+  };\n
+\n
+  ZipStorage.prototype.remove = function () {\n
+    return this._sub_storage.remove.apply(this._sub_storage,\n
+                                          arguments);\n
+  };\n
+\n
+  ZipStorage.prototype.hasCapacity = function () {\n
+    return this._sub_storage.hasCapacity.apply(this._sub_storage,\n
+                                               arguments);\n
+  };\n
+\n
+  ZipStorage.prototype.buildQuery = function () {\n
+    return this._sub_storage.buildQuery.apply(this._sub_storage,\n
+                                              arguments);\n
+  };\n
+\n
+  ZipStorage.prototype.getAttachment = function (id, name) {\n
+    var that = this;\n
+    return that._sub_storage.getAttachment(id, name)\n
+      .push(function (blob) {\n
+        if (blob.type !== MIME_TYPE) {\n
+          return blob;\n
+        }\n
+        return new RSVP.Queue()\n
+          .push(function () {\n
+            return jIO.util.readBlobAsText(blob, \'utf16\');\n
+          })\n
+          .push(function (evt) {\n
+            var result =\n
+              LZString.decompressFromUTF16(evt.target.result);\n
+            if (result === \'\') {\n
+              return blob;\n
+            }\n
+            try {\n
+              return jIO.util.dataURItoBlob(\n
+                result\n
+              );\n
+            } catch (error) {\n
+              if (error instanceof DOMException) {\n
+                return blob;\n
+              }\n
+              throw error;\n
+            }\n
+          });\n
+      });\n
+  };\n
+\n
+  function myEndsWith(str, query) {\n
+    return (str.indexOf(query) === str.length - query.length);\n
+  }\n
+\n
+  ZipStorage.prototype.putAttachment = function (id, name, blob) {\n
+    var that = this;\n
+    if ((blob.type.indexOf("text/") === 0) || myEndsWith(blob.type, "xml") ||\n
+        myEndsWith(blob.type, "json")) {\n
+      return new RSVP.Queue()\n
+        .push(function () {\n
+          return jIO.util.readBlobAsDataURL(blob);\n
+        })\n
+        .push(function (data) {\n
+          var result = LZString.compressToUTF16(data.target.result);\n
+          blob = new Blob([result],\n
+                          {type: MIME_TYPE});\n
+          return that._sub_storage.putAttachment(id, name, blob);\n
+        });\n
+    }\n
+    return this._sub_storage.putAttachment.apply(this._sub_storage,\n
+                                                 arguments);\n
+  };\n
+\n
+  ZipStorage.prototype.removeAttachment = function () {\n
+    return this._sub_storage.removeAttachment.apply(this._sub_storage,\n
+                                                    arguments);\n
+  };\n
+\n
+  ZipStorage.prototype.allAttachments = function () {\n
+    return this._sub_storage.allAttachments.apply(this._sub_storage,\n
+                                                  arguments);\n
+  };\n
+\n
+  jIO.addStorage(\'zip\', ZipStorage);\n
+}(RSVP, Blob, LZString, DOMException));\n
+;/*\n
+ * Copyright 2015, 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, RSVP, DOMException, Blob, crypto, Uint8Array, ArrayBuffer*/\n
+\n
+(function (jIO, RSVP, DOMException, Blob, crypto, Uint8Array, ArrayBuffer) {\n
+  "use strict";\n
+\n
+\n
+  // you the cryptography system used by this storage is AES-GCM.\n
+  // here is an example of how to generate a key to the json format.\n
+\n
+  // var key,\n
+  //     jsonKey;\n
+  // crypto.subtle.generateKey({name: "AES-GCM",length: 256},\n
+  //                           (true), ["encrypt", "decrypt"])\n
+  // .then(function(res){key = res;});\n
+  //\n
+  // window.crypto.subtle.exportKey("jwk", key)\n
+  // .then(function(res){jsonKey = val})\n
+  //\n
+  //var storage = jIO.createJIO({type: "crypt", key: jsonKey,\n
+  //                             sub_storage: {...}});\n
+\n
+  // find more informations about this cryptography system on\n
+  // https://github.com/diafygi/webcrypto-examples#aes-gcm\n
+\n
+  /**\n
+   * The JIO Cryptography Storage extension\n
+   *\n
+   * @class CryptStorage\n
+   * @constructor\n
+   */\n
+\n
+  var MIME_TYPE = "application/x-jio-aes-gcm-encryption";\n
+\n
+  function CryptStorage(spec) {\n
+    this._key = spec.key;\n
+    this._jsonKey = true;\n
+    this._sub_storage = jIO.createJIO(spec.sub_storage);\n
+  }\n
+\n
+  function convertKey(that) {\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return crypto.subtle.importKey("jwk", that._key,\n
+                                       "AES-GCM", false,\n
+                                       ["encrypt", "decrypt"]);\n
+      })\n
+      .push(function (res) {\n
+        that._key = res;\n
+        that._jsonKey = false;\n
+        return;\n
+      }, function () {\n
+        throw new TypeError(\n
+          "\'key\' must be a CryptoKey to JSON Web Key format"\n
+        );\n
+      });\n
+  }\n
+\n
+  CryptStorage.prototype.get = function () {\n
+    return this._sub_storage.get.apply(this._sub_storage,\n
+                                       arguments);\n
+  };\n
+\n
+  CryptStorage.prototype.post = function () {\n
+    return this._sub_storage.post.apply(this._sub_storage,\n
+                                        arguments);\n
+  };\n
+\n
+  CryptStorage.prototype.put = function () {\n
+    return this._sub_storage.put.apply(this._sub_storage,\n
+                                       arguments);\n
+  };\n
+\n
+  CryptStorage.prototype.remove = function () {\n
+    return this._sub_storage.remove.apply(this._sub_storage,\n
+                                          arguments);\n
+  };\n
+\n
+  CryptStorage.prototype.hasCapacity = function () {\n
+    return this._sub_storage.hasCapacity.apply(this._sub_storage,\n
+                                               arguments);\n
+  };\n
+\n
+  CryptStorage.prototype.buildQuery = function () {\n
+    return this._sub_storage.buildQuery.apply(this._sub_storage,\n
+                                              arguments);\n
+  };\n
+\n
+\n
+  CryptStorage.prototype.putAttachment = function (id, name, blob) {\n
+    var initializaton_vector = crypto.getRandomValues(new Uint8Array(12)),\n
+      that = this;\n
+\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        if (that._jsonKey === true) {\n
+          return convertKey(that);\n
+        }\n
+        return;\n
+      })\n
+      .push(function () {\n
+        return jIO.util.readBlobAsDataURL(blob);\n
+      })\n
+      .push(function (dataURL) {\n
+        //string->arraybuffer\n
+        var strLen = dataURL.currentTarget.result.length,\n
+          buf = new ArrayBuffer(strLen),\n
+          bufView = new Uint8Array(buf),\n
+          i;\n
+\n
+        dataURL = dataURL.currentTarget.result;\n
+        for (i = 0; i < strLen; i += 1) {\n
+          bufView[i] = dataURL.charCodeAt(i);\n
+        }\n
+        return crypto.subtle.encrypt({\n
+          name : "AES-GCM",\n
+          iv : initializaton_vector\n
+        },\n
+                                     that._key, buf);\n
+      })\n
+      .push(function (coded) {\n
+        var blob = new Blob([initializaton_vector, coded], {type: MIME_TYPE});\n
+        return that._sub_storage.putAttachment(id, name, blob);\n
+      });\n
+  };\n
+\n
+  CryptStorage.prototype.getAttachment = function (id, name) {\n
+    var that = this;\n
+\n
+    return that._sub_storage.getAttachment(id, name)\n
+      .push(function (blob) {\n
+        if (blob.type !== MIME_TYPE) {\n
+          return blob;\n
+        }\n
+        return new RSVP.Queue()\n
+          .push(function () {\n
+            if (that._jsonKey === true) {\n
+              return convertKey(that);\n
+            }\n
+            return;\n
+          })\n
+          .push(function () {\n
+            return jIO.util.readBlobAsArrayBuffer(blob);\n
+          })\n
+          .push(function (coded) {\n
+            var initializaton_vector;\n
+\n
+            coded = coded.currentTarget.result;\n
+            initializaton_vector = new Uint8Array(coded.slice(0, 12));\n
+            return crypto.subtle.decrypt({\n
+              name : "AES-GCM",\n
+              iv : initializaton_vector\n
+            },\n
+                                         that._key, coded.slice(12));\n
+          })\n
+          .push(function (arr) {\n
+            //arraybuffer->string\n
+            arr = String.fromCharCode.apply(null, new Uint8Array(arr));\n
+            try {\n
+              return jIO.util.dataURItoBlob(arr);\n
+            } catch (error) {\n
+              if (error instanceof DOMException) {\n
+                return blob;\n
+              }\n
+              throw error;\n
+            }\n
+          }, function () { return blob; });\n
+      });\n
+  };\n
+\n
+  CryptStorage.prototype.removeAttachment = function () {\n
+    return this._sub_storage.removeAttachment.apply(this._sub_storage,\n
+                                                    arguments);\n
+  };\n
+\n
+  CryptStorage.prototype.allAttachments = function () {\n
+    return this._sub_storage.allAttachments.apply(this._sub_storage,\n
+                                                  arguments);\n
+  };\n
+\n
+  jIO.addStorage(\'crypt\', CryptStorage);\n
+\n
+}(jIO, RSVP, DOMException, Blob, crypto, Uint8Array, ArrayBuffer));\n
+;/*\n
+ * Copyright 2013, Nexedi SA\n
+ * Released under the LGPL license.\n
+ * http://www.gnu.org/licenses/lgpl.html\n
+ */\n
+/**\n
+ * JIO Dropbox Storage. Type = "dropbox".\n
+ * Dropbox "database" storage.\n
+ */\n
+/*global Blob, jIO, RSVP, UriTemplate*/\n
+/*jslint nomen: true*/\n
+\n
+(function (jIO, RSVP, Blob, UriTemplate) {\n
+  "use strict";\n
+  var UPLOAD_URL = "https://content.dropboxapi.com/1/files_put/" +\n
+      "{+root}{+id}{+name}{?access_token}",\n
+    upload_template = UriTemplate.parse(UPLOAD_URL),\n
+    CREATE_DIR_URL = "https://api.dropboxapi.com/1/fileops/create_folder" +\n
+      "{?access_token,root,path}",\n
+    create_dir_template = UriTemplate.parse(CREATE_DIR_URL),\n
+    REMOVE_URL = "https://api.dropboxapi.com/1/fileops/delete/" +\n
+      "{?access_token,root,path}",\n
+    remote_template = UriTemplate.parse(REMOVE_URL),\n
+    GET_URL = "https://content.dropboxapi.com/1/files" +\n
+      "{/root,id}{+name}{?access_token}",\n
+    get_template = UriTemplate.parse(GET_URL),\n
+    //LIST_URL = \'https://api.dropboxapi.com/1/metadata/sandbox/\';\n
+    METADATA_URL = "https://api.dropboxapi.com/1/metadata" +\n
+      "{/root}{+id}{?access_token}",\n
+    metadata_template = UriTemplate.parse(METADATA_URL);\n
+\n
+  function restrictDocumentId(id) {\n
+    if (id.indexOf("/") !== 0) {\n
+      throw new jIO.util.jIOError("id " + id + " is forbidden (no begin /)",\n
+                                  400);\n
+    }\n
+    if (id.lastIndexOf("/") !== (id.length - 1)) {\n
+      throw new jIO.util.jIOError("id " + id + " is forbidden (no end /)",\n
+                                  400);\n
+    }\n
+    return id;\n
+  }\n
+\n
+  function restrictAttachmentId(id) {\n
+    if (id.indexOf("/") !== -1) {\n
+      throw new jIO.util.jIOError("attachment " + id + " is forbidden",\n
+                                  400);\n
+    }\n
+  }\n
+\n
+  /**\n
+   * The JIO Dropbox Storage extension\n
+   *\n
+   * @class DropboxStorage\n
+   * @constructor\n
+   */\n
+  function DropboxStorage(spec) {\n
+    if (typeof spec.access_token !== \'string\' || !spec.access_token) {\n
+      throw new TypeError("Access Token\' must be a string " +\n
+                          "which contains more than one character.");\n
+    }\n
+    if (typeof spec.root !== \'string\' || !spec.root ||\n
+        (spec.root !== "dropbox" && spec.root !== "sandbox")) {\n
+      throw new TypeError("root must be \'dropbox\' or \'sandbox\'");\n
+    }\n
+    this._access_token = spec.access_token;\n
+    this._root = spec.root;\n
+  }\n
+\n
+  DropboxStorage.prototype.put = function (id, param) {\n
+    var that = this;\n
+    id = restrictDocumentId(id);\n
+    if (Object.getOwnPropertyNames(param).length > 0) {\n
+      // Reject if param has some properties\n
+      throw new jIO.util.jIOError("Can not store properties: " +\n
+                                  Object.getOwnPropertyNames(param), 400);\n
+    }\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return jIO.util.ajax({\n
+          type: "POST",\n
+          url: create_dir_template.expand({\n
+            access_token: that._access_token,\n
+            root: that._root,\n
+            path: id\n
+          })\n
+        });\n
+      })\n
+      .push(undefined, function (err) {\n
+        if ((err.target !== undefined) &&\n
+            (err.target.status === 405)) {\n
+          // Directory already exists, no need to fail\n
+          return;\n
+        }\n
+        throw err;\n
+      });\n
+  };\n
+\n
+  DropboxStorage.prototype.remove = function (id) {\n
+    id = restrictDocumentId(id);\n
+    return jIO.util.ajax({\n
+      type: "POST",\n
+      url: remote_template.expand({\n
+        access_token: this._access_token,\n
+        root: this._root,\n
+        path: id\n
+      })\n
+    });\n
+  };\n
+\n
+  DropboxStorage.prototype.get = function (id) {\n
+    var that = this;\n
+\n
+    if (id === "/") {\n
+      return {};\n
+    }\n
+    id = restrictDocumentId(id);\n
+\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return jIO.util.ajax({\n
+          type: "GET",\n
+          url: metadata_template.expand({\n
+            access_token: that._access_token,\n
+            root: that._root,\n
+            id: id\n
+          })\n
+        });\n
+      })\n
+      .push(function (evt) {\n
+        var obj = JSON.parse(evt.target.response ||\n
+                             evt.target.responseText);\n
+        if (obj.is_dir) {\n
+          return {};\n
+        }\n
+        throw new jIO.util.jIOError("Not a directory: " + id, 404);\n
+      }, function (error) {\n
+        if (error.target !== undefined && error.target.status === 404) {\n
+          throw new jIO.util.jIOError("Cannot find document: " + id, 404);\n
+        }\n
+        throw error;\n
+      });\n
+  };\n
+\n
+  DropboxStorage.prototype.allAttachments = function (id) {\n
+\n
+    var that = this;\n
+    id = restrictDocumentId(id);\n
+\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return jIO.util.ajax({\n
+          type: "GET",\n
+          url: metadata_template.expand({\n
+            access_token: that._access_token,\n
+            root: that._root,\n
+            id: id\n
+          })\n
+        });\n
+      })\n
+      .push(function (evt) {\n
+        var obj = JSON.parse(evt.target.response || evt.target.responseText),\n
+          i,\n
+          result = {};\n
+        if (!obj.is_dir) {\n
+          throw new jIO.util.jIOError("Not a directory: " + id, 404);\n
+        }\n
+        for (i = 0; i < obj.contents.length; i += 1) {\n
+          if (!obj.contents[i].is_dir) {\n
+            result[obj.contents[i].path.split("/").pop()] = {};\n
+          }\n
+        }\n
+        return result;\n
+      }, function (error) {\n
+        if (error.target !== undefined && error.target.status === 404) {\n
+          throw new jIO.util.jIOError("Cannot find document: " + id, 404);\n
+        }\n
+        throw error;\n
+      });\n
+  };\n
+\n
+  //currently, putAttachment will fail with files larger than 150MB,\n
+  //due to the Dropbox API. the API provides the "chunked_upload" method\n
+  //to pass this limit, but upload process becomes more complex to implement.\n
+  //\n
+  //putAttachment will also create a folder if you try to put an attachment\n
+  //to an inexisting foler.\n
+\n
+  DropboxStorage.prototype.putAttachment = function (id, name, blob) {\n
+    id = restrictDocumentId(id);\n
+    restrictAttachmentId(name);\n
+\n
+    return jIO.util.ajax({\n
+      type: "PUT",\n
+      url: upload_template.expand({\n
+        root: this._root,\n
+        id: id,\n
+        name: name,\n
+        access_token: this._access_token\n
+      }),\n
+      dataType: blob.type,\n
+      data: blob\n
+    });\n
+  };\n
+\n
+  DropboxStorage.prototype.getAttachment = function (id, name) {\n
+    var that = this;\n
+\n
+    id = restrictDocumentId(id);\n
+    restrictAttachmentId(name);\n
+\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return jIO.util.ajax({\n
+          type: "GET",\n
+          dataType: "blob",\n
+          url: get_template.expand({\n
+            root: that._root,\n
+            id: id,\n
+            name: name,\n
+            access_token: that._access_token\n
+          })\n
+        });\n
+      })\n
+      .push(function (evt) {\n
+        return new Blob(\n
+          [evt.target.response || evt.target.responseText],\n
+          {"type": evt.target.getResponseHeader(\'Content-Type\') ||\n
+            "application/octet-stream"}\n
+        );\n
+      }, function (error) {\n
+        if (error.target !== undefined && error.target.status === 404) {\n
+          throw new jIO.util.jIOError("Cannot find attachment: " +\n
+                                      id + ", " + name, 404);\n
+        }\n
+        throw error;\n
+      });\n
+  };\n
+\n
+  //removeAttachment removes also directories.(due to Dropbox API)\n
+\n
+  DropboxStorage.prototype.removeAttachment = function (id, name) {\n
+    var that = this;\n
+    id = restrictDocumentId(id);\n
+    restrictAttachmentId(name);\n
+\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return jIO.util.ajax({\n
+          type: "POST",\n
+          url: remote_template.expand({\n
+            access_token: that._access_token,\n
+            root: that._root,\n
+            path: id + name\n
+          })\n
+        });\n
+      }).push(undefined, function (error) {\n
+        if (error.target !== undefined && error.target.status === 404) {\n
+          throw new jIO.util.jIOError("Cannot find attachment: " +\n
+                                      id + ", " + name, 404);\n
+        }\n
+        throw error;\n
+      });\n
+  };\n
+\n
+  jIO.addStorage(\'dropbox\', DropboxStorage);\n
+\n
+}(jIO, RSVP, Blob, UriTemplate));\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, RSVP, DOMParser, Blob */\n
+\n
+// JIO Dav Storage Description :\n
+// {\n
+//   type: "dav",\n
+//   url: {string},\n
+//   basic_login: {string} // Basic authentication\n
+// }\n
+\n
+// NOTE: to get the authentication type ->\n
+// curl --verbose  -X OPTION http://domain/\n
+// In the headers: "WWW-Authenticate: Basic realm="DAV-upload"\n
+\n
+(function (jIO, RSVP, DOMParser, Blob) {\n
+  "use strict";\n
+\n
+  function ajax(storage, options) {\n
+    if (options === undefined) {\n
+      options = {};\n
+    }\n
+    if (storage._authorization !== undefined) {\n
+      if (options.headers === undefined) {\n
+        options.headers = {};\n
+      }\n
+      options.headers.Authorization = storage._authorization;\n
+    }\n
+//       if (start !== undefined) {\n
+//         if (end !== undefined) {\n
+//           headers.Range = "bytes=" + start + "-" + end;\n
+//         } else {\n
+//           headers.Range = "bytes=" + start + "-";\n
+//         }\n
+//       }\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return jIO.util.ajax(options);\n
+      });\n
+  }\n
+\n
+  function restrictDocumentId(id) {\n
+    if (id.indexOf("/") !== 0) {\n
+      throw new jIO.util.jIOError("id " + id + " is forbidden (no begin /)",\n
+                                  400);\n
+    }\n
+    if (id.lastIndexOf("/") !== (id.length - 1)) {\n
+      throw new jIO.util.jIOError("id " + id + " is forbidden (no end /)",\n
+                                  400);\n
+    }\n
+    return id;\n
+  }\n
+\n
+  function restrictAttachmentId(id) {\n
+    if (id.indexOf("/") !== -1) {\n
+      throw new jIO.util.jIOError("attachment " + id + " is forbidden",\n
+                                  400);\n
+    }\n
+  }\n
+\n
+  /**\n
+   * The JIO WebDAV Storage extension\n
+   *\n
+   * @class DavStorage\n
+   * @constructor\n
+   */\n
+  function DavStorage(spec) {\n
+    if (typeof spec.url !== \'string\') {\n
+      throw new TypeError("DavStorage \'url\' is not of type string");\n
+    }\n
+    this._url = spec.url;\n
+    // XXX digest login\n
+    if (typeof spec.basic_login === \'string\') {\n
+      this._authorization = "Basic " + spec.basic_login;\n
+    }\n
+\n
+  }\n
+\n
+  DavStorage.prototype.put = function (id, param) {\n
+    var that = this;\n
+    id = restrictDocumentId(id);\n
+    if (Object.getOwnPropertyNames(param).length > 0) {\n
+      // Reject if param has some properties\n
+      throw new jIO.util.jIOError("Can not store properties: " +\n
+                                  Object.getOwnPropertyNames(param), 400);\n
+    }\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return ajax(that, {\n
+          type: "MKCOL",\n
+          url: that._url + id\n
+        });\n
+      })\n
+      .push(undefined, function (err) {\n
+        if ((err.target !== undefined) &&\n
+            (err.target.status === 405)) {\n
+          return;\n
+        }\n
+        throw err;\n
+      });\n
+  };\n
+\n
+  DavStorage.prototype.remove = function (id) {\n
+    id = restrictDocumentId(id);\n
+    return ajax(this, {\n
+      type: "DELETE",\n
+      url: this._url + id\n
+    });\n
+  };\n
+\n
+  DavStorage.prototype.get = function (id) {\n
+    var context = this;\n
+    id = restrictDocumentId(id);\n
+\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return ajax(context, {\n
+          type: "PROPFIND",\n
+          url: context._url + id,\n
+          dataType: "text",\n
+          headers: {\n
+            // Increasing this value is a performance killer\n
+            Depth: "1"\n
+          }\n
+        });\n
+      })\n
+      .push(function () {\n
+        return {};\n
+      }, function (error) {\n
+        if ((error.target !== undefined) &&\n
+            (error.target.status === 404)) {\n
+          throw new jIO.util.jIOError("Cannot find document", 404);\n
+        }\n
+        throw error;\n
+      });\n
+  };\n
+\n
+  DavStorage.prototype.allAttachments = function (id) {\n
+\n
+    var context = this;\n
+    id = restrictDocumentId(id);\n
+\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return ajax(context, {\n
+          type: "PROPFIND",\n
+          url: context._url + id,\n
+          dataType: "text",\n
+          headers: {\n
+            // Increasing this value is a performance killer\n
+            Depth: "1"\n
+          }\n
+        });\n
+      })\n
+\n
+\n
+      .push(function (response) {\n
+        // Extract all meta informations and return them to JSON\n
+\n
+        var i,\n
+          attachment = {},\n
+          id,\n
+          attachment_list = new DOMParser().parseFromString(\n
+            response.target.responseText,\n
+            "text/xml"\n
+          ).querySelectorAll(\n
+            "D\\\\:response, response"\n
+          );\n
+\n
+        // exclude parent folder and browse\n
+        for (i = 1; i < attachment_list.length; i += 1) {\n
+          // XXX Only get files for now\n
+          id = attachment_list[i].querySelector("D\\\\:href, href").\n
+            textContent.split(\'/\').slice(-1)[0];\n
+          // XXX Ugly\n
+          if ((id !== undefined) && (id !== "")) {\n
+            attachment[id] = {};\n
+          }\n
+        }\n
+        return attachment;\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
+        }\n
+        throw error;\n
+      });\n
+\n
+  };\n
+\n
+\n
+  DavStorage.prototype.putAttachment = function (id, name, blob) {\n
+    var that = this;\n
+    id = restrictDocumentId(id);\n
+    restrictAttachmentId(name);\n
+\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return ajax(that, {\n
+          type: "PUT",\n
+          url: that._url + id + name,\n
+          data: blob\n
+        });\n
+      })\n
+      .push(undefined, function (error) {\n
+        if (error.target.status === 403 || error.target.status === 424) {\n
+          throw new jIO.util.jIOError("Cannot access subdocument", 404);\n
+        }\n
+        throw error;\n
+      });\n
+  };\n
+\n
+  DavStorage.prototype.getAttachment = function (id, name) {\n
+    var context = this;\n
+    id = restrictDocumentId(id);\n
+    restrictAttachmentId(name);\n
+\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return ajax(context, {\n
+          type: "GET",\n
+          url: context._url + id + name,\n
+          dataType: "blob"\n
+        });\n
+      })\n
+      .push(function (response) {\n
+        return new Blob(\n
+          [response.target.response || response.target.responseText],\n
+          {"type": response.target.getResponseHeader(\'Content-Type\') ||\n
+                   "application/octet-stream"}\n
+        );\n
+      }, function (error) {\n
+        if ((error.target !== undefined) &&\n
+            (error.target.status === 404)) {\n
+          throw new jIO.util.jIOError("Cannot find attachment: "\n
+                                      + id + " , " + name,\n
+                                      404);\n
+        }\n
+        throw error;\n
+      });\n
+\n
+  };\n
+\n
+  DavStorage.prototype.removeAttachment = function (id, name) {\n
+    var context = this;\n
+    id = restrictDocumentId(id);\n
+    restrictAttachmentId(name);\n
+\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return ajax(context, {\n
+          type: "DELETE",\n
+          url: context._url + id + name\n
+        });\n
+      })\n
+      .push(undefined, function (error) {\n
+        if ((error.target !== undefined) &&\n
+            (error.target.status === 404)) {\n
+          throw new jIO.util.jIOError("Cannot find attachment: "\n
+                                      + id + " , " + name,\n
+                                      404);\n
+        }\n
+        throw error;\n
+      });\n
+  };\n
+\n
+  // JIO COMMANDS //\n
+\n
+  // wedDav methods rfc4918 (short summary)\n
+  // COPY     Reproduces single resources (files) and collections (directory\n
+  //          trees). Will overwrite files (if specified by request) but will\n
+  //          respond 209 (Conflict) if it would overwrite a tree\n
+  // DELETE   deletes files and directory trees\n
+  // GET      just the vanilla HTTP/1.1 behaviour\n
+  // HEAD     ditto\n
+  // LOCK     locks a resources\n
+  // MKCOL    creates a directory\n
+  // MOVE     Moves (rename or copy) a file or a directory tree. Will\n
+  //          \'overwrite\' files (if specified by the request) but will respond\n
+  //          209 (Conflict) if it would overwrite a tree.\n
+  // OPTIONS  If WebDAV is enabled and available for the path this reports the\n
+  //          WebDAV extension methods\n
+  // PROPFIND Retrieves the requested file characteristics, DAV lock status\n
+  //          and \'dead\' properties for individual files, a directory and its\n
+  //          child files, or a directory tree\n
+  // PROPPATCHset and remove \'dead\' meta-data properties\n
+  // PUT      Update or create resource or collections\n
+  // UNLOCK   unlocks a resource\n
+\n
+  // Notes: all Ajax requests should be CORS (cross-domain)\n
+  // adding custom headers triggers preflight OPTIONS request!\n
+  // http://remysharp.com/2011/04/21/getting-cors-working/\n
+\n
+  jIO.addStorage(\'dav\', DavStorage);\n
+\n
+}(jIO, RSVP, DOMParser, Blob));\n
+;/*\n
+ * Copyright 2015, Nexedi SA\n
+ * Released under the LGPL license.\n
+ * http://www.gnu.org/licenses/lgpl.html\n
+ */\n
+/**\n
+ * JIO Google Drive Storage. Type = "gdrive".\n
+ * Google Drive "database" storage.\n
+ */\n
+/*global jIO, Blob, RSVP, UriTemplate, JSON*/\n
+/*jslint nomen: true*/\n
+\n
+(function (jIO, Blob, RSVP, UriTemplate, JSON) {\n
+  "use strict";\n
+\n
+  var UPLOAD_URL = "https://www.googleapis.com{/upload}/drive/v2/files{/id}" +\n
+      "{?uploadType,access_token}",\n
+    upload_template = UriTemplate.parse(UPLOAD_URL),\n
+    REMOVE_URL = "https://www.googleapis.com/drive/v2/" +\n
+      "files{/id,trash}{?access_token}",\n
+    remove_template = UriTemplate.parse(REMOVE_URL),\n
+    LIST_URL = "https://www.googleapis.com/drive/v2/files" +\n
+      "?prettyPrint=false{&pageToken}&q=trashed=false" +\n
+      "&fields=nextPageToken,items(id){&access_token}",\n
+    list_template = UriTemplate.parse(LIST_URL),\n
+    GET_URL = "https://www.googleapis.com/drive/v2/files{/id}{?alt}",\n
+    get_template = UriTemplate.parse(GET_URL);\n
+\n
+  function handleError(error, id) {\n
+    if (error.target.status === 404) {\n
+      throw new jIO.util.jIOError(\n
+        "Cannot find document: " + id,\n
+        404\n
+      );\n
+    }\n
+    throw error;\n
+  }\n
+\n
+  function listPage(result, token) {\n
+    var i,\n
+      obj;\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return jIO.util.ajax({\n
+          "type": "GET",\n
+          "url": list_template.expand({\n
+            pageToken : (result.nextPageToken || ""),\n
+            access_token: token\n
+          })\n
+        });\n
+      })\n
+      .push(function (data) {\n
+        obj = JSON.parse(data.target.response || data.target.responseText);\n
+        for (i = 0; i < obj.items.length; i += 1) {\n
+          obj.items[i].value = {};\n
+          result.push(obj.items[i]);\n
+        }\n
+        result.nextPageToken = obj.nextPageToken;\n
+        return result;\n
+      }, handleError);\n
+  }\n
+\n
+  function checkName(name) {\n
+    if (name !== "enclosure") {\n
+      throw new jIO.util.jIOError("Only support \'enclosure\' attachment", 400);\n
+    }\n
+  }\n
+\n
+  /**\n
+   * The JIO Google Drive Storage extension\n
+   *\n
+   * @class GdriveStorage\n
+   * @constructor\n
+   */\n
+  function GdriveStorage(spec) {\n
+    if (spec === undefined || spec.access_token === undefined ||\n
+        typeof spec.access_token !== \'string\') {\n
+      throw new TypeError("Access Token must be a string " +\n
+                          "which contains more than one character.");\n
+    }\n
+    if (spec.trashing !== undefined &&\n
+        (spec.trashing !== true && spec.trashing !== false)) {\n
+      throw new TypeError("trashing parameter" +\n
+                          " must be a boolean (true or false)");\n
+    }\n
+    this._trashing = spec.trashing || true;\n
+    this._access_token = spec.access_token;\n
+    return;\n
+  }\n
+\n
+  function recursiveAllDocs(result, accessToken) {\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return listPage(result, accessToken);\n
+      })\n
+      .push(function () {\n
+        if (result.nextPageToken) {\n
+          return recursiveAllDocs(result, accessToken);\n
+        }\n
+        return result;\n
+      });\n
+  }\n
+\n
+  GdriveStorage.prototype.hasCapacity = function (name) {\n
+    return (name === "list");\n
+  };\n
+\n
+  GdriveStorage.prototype.buildQuery = function () {\n
+    return recursiveAllDocs([], this._access_token);\n
+  };\n
+\n
+  function sendMetaData(id, param, token) {\n
+    var boundary = "-------314159265358979323846";\n
+\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return jIO.util.ajax({\n
+          "type": id ? "PUT" : "POST",\n
+          "url": upload_template.expand({\n
+            access_token: token,\n
+            id: id || [],\n
+            upload: id ? [] : "upload",\n
+            uploadType: "multipart"\n
+          }),\n
+          headers: {\n
+            "Content-Type" : \'multipart/related; boundary="\' + boundary + \'"\'\n
+          },\n
+          data: \'--\' + boundary + \'\\n\' +\n
+            \'Content-Type: application/json; charset=UTF-8\\n\\n\' +\n
+            JSON.stringify(param) + \'\\n\\n--\' + boundary + "--"\n
+        });\n
+      })\n
+      .push(function (result) {\n
+        var obj = JSON.parse(result.target.responseText);\n
+\n
+        return obj.id;\n
+      },\n
+            function (error) {handleError(error, id); });\n
+  }\n
+\n
+  GdriveStorage.prototype.put = function (id, param) {\n
+    return sendMetaData(id, param, this._access_token);\n
+  };\n
+\n
+  GdriveStorage.prototype.post = function (param) {\n
+    return sendMetaData(undefined, param, this._access_token);\n
+  };\n
+\n
+  function sendData(id, blob, token) {\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return jIO.util.ajax({\n
+          "type": "PUT",\n
+          "url": upload_template.expand({\n
+            access_token: token,\n
+            upload: "upload",\n
+            id: id,\n
+            uploadType: "media"\n
+          }),\n
+          data: blob\n
+        });\n
+      })\n
+      .push(function (data) {\n
+        data = JSON.parse(data.target.responseText);\n
+        if (data.mimeType === "application/vnd.google-apps.folder") {\n
+          throw new jIO.util.jIOError("cannot put attachments to folder", 400);\n
+        }\n
+        return data;\n
+      }, function (error) {handleError(error, id); });\n
+  }\n
+\n
+  GdriveStorage.prototype.putAttachment = function (id, name, blob) {\n
+    checkName(name);\n
+    return sendData(id, blob, this._access_token);\n
+  };\n
+\n
+  GdriveStorage.prototype.remove = function (id) {\n
+    var that  = this;\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return jIO.util.ajax({\n
+          type: that._trashing ? "POST" : "DELETE",\n
+          url: remove_template.expand({\n
+            id : id,\n
+            access_token : that._access_token,\n
+            trash : that._trashing ? "trash" : []\n
+          })\n
+        });\n
+      })\n
+      .push(undefined, function (error) {handleError(error, id); });\n
+  };\n
+\n
+  function getData(id, attach, token) {\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return jIO.util.ajax({\n
+          type: "GET",\n
+          dataType: attach ? "blob" : "json",\n
+          url: get_template.expand({\n
+            id: id,\n
+            alt: attach ? "media" : [],\n
+            access_token: token\n
+          }),\n
+          headers: {\n
+            "Authorization" : "Bearer " + token\n
+          }\n
+        });\n
+      })\n
+      .push(function (evt) {\n
+        return evt.target.response ||\n
+          (attach ? new Blob([evt.target.responseText],\n
+                             {"type" :\n
+                              evt.target.responseHeaders["Content-Type"]}) :\n
+              JSON.parse(evt.target.responseText));\n
+      }, function (error) {handleError(error, id); });\n
+  }\n
+\n
+  GdriveStorage.prototype.get = function (id) {\n
+    return getData(id, false, this._access_token);\n
+  };\n
+\n
+  GdriveStorage.prototype.getAttachment = function (id, name) {\n
+    checkName(name);\n
+    return getData(id, true, this._access_token);\n
+  };\n
+\n
+  GdriveStorage.prototype.allAttachments = function (id) {\n
+    var token = this._access_token;\n
+\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return getData(id, false, token);\n
+      })\n
+      .push(function (data) {\n
+        if (data.mimeType === "application/vnd.google-apps.folder") {\n
+          return {};\n
+        }\n
+        return {"enclosure": {}};\n
+      });\n
+  };\n
+\n
+  jIO.addStorage(\'gdrive\', GdriveStorage);\n
+\n
+}(jIO, Blob, RSVP, UriTemplate, JSON));\n
+;/*jslint nomen: true */\n
+/*global RSVP*/\n
+\n
+/**\n
+ * JIO Union Storage. Type = \'union\'.\n
+ * This provide a unified access other multiple storage.\n
+ * New document are created in the first sub storage.\n
+ * Document are searched in each sub storage until it is found.\n
+ * \n
+ *\n
+ * Storage Description:\n
+ *\n
+ *     {\n
+ *       "type": "union",\n
+ *       "storage_list": [\n
+ *         sub_storage_description_1,\n
+ *         sub_storage_description_2,\n
+ *\n
+ *         sub_storage_description_X,\n
+ *       ]\n
+ *     }\n
+ *\n
+ * @class UnionStorage\n
+ */\n
+\n
+(function (jIO, RSVP) {\n
+  "use strict";\n
+\n
+  /**\n
+   * The JIO UnionStorage extension\n
+   *\n
+   * @class UnionStorage\n
+   * @constructor\n
+   */\n
+  function UnionStorage(spec) {\n
+    if (!Array.isArray(spec.storage_list)) {\n
+      throw new jIO.util.jIOError("storage_list is not an Array", 400);\n
+    }\n
+    var i;\n
+    this._storage_list = [];\n
+    for (i = 0; i < spec.storage_list.length; i += 1) {\n
+      this._storage_list.push(jIO.createJIO(spec.storage_list[i]));\n
+    }\n
+  }\n
+\n
+  UnionStorage.prototype._getWithStorageIndex = function () {\n
+    var i,\n
+      index = 0,\n
+      context = this,\n
+      arg = arguments,\n
+      result = this._storage_list[0].get.apply(this._storage_list[0], arg);\n
+\n
+    function handle404(j) {\n
+      result\n
+        .push(undefined, function (error) {\n
+          if ((error instanceof jIO.util.jIOError) &&\n
+              (error.status_code === 404)) {\n
+            return context._storage_list[j].get.apply(context._storage_list[j],\n
+                                                      arg)\n
+              .push(function (doc) {\n
+                index = j;\n
+                return doc;\n
+              });\n
+          }\n
+          throw error;\n
+        });\n
+    }\n
+\n
+    for (i = 1; i < this._storage_list.length; i += 1) {\n
+      handle404(i);\n
+    }\n
+    return result\n
+      .push(function (doc) {\n
+        return [index, doc];\n
+      });\n
+  };\n
+\n
+  /*\n
+   * Get a document\n
+   * Try on each substorage on after the other\n
+   */\n
+  UnionStorage.prototype.get = function () {\n
+    return this._getWithStorageIndex.apply(this, arguments)\n
+      .push(function (result) {\n
+        return result[1];\n
+      });\n
+  };\n
+\n
+  /*\n
+   * Get attachments list\n
+   * Try on each substorage on after the other\n
+   */\n
+  UnionStorage.prototype.allAttachments = function () {\n
+    var argument_list = arguments,\n
+      context = this;\n
+    return this._getWithStorageIndex.apply(this, arguments)\n
+      .push(function (result) {\n
+        var sub_storage = context._storage_list[result[0]];\n
+        return sub_storage.allAttachments.apply(sub_storage, argument_list);\n
+      });\n
+  };\n
+\n
+  /*\n
+   * Post a document\n
+   * Simply store on the first substorage\n
+   */\n
+  UnionStorage.prototype.post = function () {\n
+    return this._storage_list[0].post.apply(this._storage_list[0], arguments);\n
+  };\n
+\n
+  /*\n
+   * Put a document\n
+   * Search the document location, and modify it in its storage.\n
+   */\n
+  UnionStorage.prototype.put = function () {\n
+    var arg = arguments,\n
+      context = this;\n
+    return this._getWithStorageIndex(arg[0])\n
+      .push(undefined, function (error) {\n
+        if ((error instanceof jIO.util.jIOError) &&\n
+            (error.status_code === 404)) {\n
+          // Document does not exist, create in first substorage\n
+          return [0];\n
+        }\n
+        throw error;\n
+      })\n
+      .push(function (result) {\n
+        // Storage found, modify in it directly\n
+        var sub_storage = context._storage_list[result[0]];\n
+        return sub_storage.put.apply(sub_storage, arg);\n
+      });\n
+  };\n
+\n
+  /*\n
+   * Remove a document\n
+   * Search the document location, and remove it from its storage.\n
+   */\n
+  UnionStorage.prototype.remove = function () {\n
+    var arg = arguments,\n
+      context = this;\n
+    return this._getWithStorageIndex(arg[0])\n
+      .push(function (result) {\n
+        // Storage found, remove from it directly\n
+        var sub_storage = context._storage_list[result[0]];\n
+        return sub_storage.remove.apply(sub_storage, arg);\n
+      });\n
+  };\n
+\n
+  UnionStorage.prototype.buildQuery = function () {\n
+    var promise_list = [],\n
+      i,\n
+      id_dict = {},\n
+      len = this._storage_list.length,\n
+      sub_storage;\n
+    for (i = 0; i < len; i += 1) {\n
+      sub_storage = this._storage_list[i];\n
+      promise_list.push(sub_storage.buildQuery.apply(sub_storage, arguments));\n
+    }\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return RSVP.all(promise_list);\n
+      })\n
+      .push(function (result_list) {\n
+        var result = [],\n
+          sub_result,\n
+          sub_result_len,\n
+          j;\n
+        len = result_list.length;\n
+        for (i = 0; i < len; i += 1) {\n
+          sub_result = result_list[i];\n
+          sub_result_len = sub_result.length;\n
+          for (j = 0; j < sub_result_len; j += 1) {\n
+            if (!id_dict.hasOwnProperty(sub_result[j].id)) {\n
+              id_dict[sub_result[j].id] = null;\n
+              result.push(sub_result[j]);\n
+            }\n
+          }\n
+        }\n
+        return result;\n
+      });\n
+  };\n
+\n
+  UnionStorage.prototype.hasCapacity = function (name) {\n
+    var i,\n
+      len,\n
+      result,\n
+      sub_storage;\n
+    if ((name === "list") ||\n
+            (name === "query") ||\n
+            (name === "select")) {\n
+      result = true;\n
+      len = this._storage_list.length;\n
+      for (i = 0; i < len; i += 1) {\n
+        sub_storage = this._storage_list[i];\n
+        result = result && sub_storage.hasCapacity(name);\n
+      }\n
+      return result;\n
+    }\n
+    return false;\n
+  };\n
+\n
+  UnionStorage.prototype.repair = function () {\n
+    var i,\n
+      promise_list = [];\n
+    for (i = 0; i < this._storage_list.length; i += 1) {\n
+      promise_list.push(this._storage_list[i].repair.apply(\n
+        this._storage_list[i],\n
+        arguments\n
+      ));\n
+    }\n
+    return RSVP.all(promise_list);\n
+  };\n
+\n
+  UnionStorage.prototype.getAttachment = function () {\n
+    var argument_list = arguments,\n
+      context = this;\n
+    return this._getWithStorageIndex.apply(this, arguments)\n
+      .push(function (result) {\n
+        var sub_storage = context._storage_list[result[0]];\n
+        return sub_storage.getAttachment.apply(sub_storage, argument_list);\n
+      });\n
+  };\n
+\n
+  UnionStorage.prototype.putAttachment = function () {\n
+    var argument_list = arguments,\n
+      context = this;\n
+    return this._getWithStorageIndex.apply(this, arguments)\n
+      .push(function (result) {\n
+        var sub_storage = context._storage_list[result[0]];\n
+        return sub_storage.putAttachment.apply(sub_storage, argument_list);\n
+      });\n
+  };\n
+\n
+  UnionStorage.prototype.removeAttachment = function () {\n
+    var argument_list = arguments,\n
+      context = this;\n
+    return this._getWithStorageIndex.apply(this, arguments)\n
+      .push(function (result) {\n
+        var sub_storage = context._storage_list[result[0]];\n
+        return sub_storage.removeAttachment.apply(sub_storage, argument_list);\n
+      });\n
+  };\n
+\n
+  jIO.addStorage(\'union\', UnionStorage);\n
+\n
+}(jIO, RSVP));\n
+;/*\n
+ * Copyright 2013, Nexedi SA\n
+ * Released under the LGPL license.\n
+ * http://www.gnu.org/licenses/lgpl.html\n
+ */\n
+// JIO ERP5 Storage Description :\n
+// {\n
+//   type: "erp5"\n
+//   url: {string}\n
+// }\n
+\n
+/*jslint nomen: true, unparam: true */\n
+/*global jIO, UriTemplate, FormData, RSVP, URI, Blob,\n
+         SimpleQuery, ComplexQuery*/\n
+\n
+(function (jIO, UriTemplate, FormData, RSVP, URI, Blob,\n
+           SimpleQuery, ComplexQuery) {\n
+  "use strict";\n
+\n
+  function getSiteDocument(storage) {\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return jIO.util.ajax({\n
+          "type": "GET",\n
+          "url": storage._url,\n
+          "xhrFields": {\n
+            withCredentials: true\n
+          }\n
+        });\n
+      })\n
+      .push(function (event) {\n
+        return JSON.parse(event.target.responseText);\n
+      });\n
+  }\n
+\n
+  function getDocumentAndHateoas(storage, id, options) {\n
+    if (options === undefined) {\n
+      options = {};\n
+    }\n
+    return getSiteDocument(storage)\n
+      .push(function (site_hal) {\n
+        // XXX need to get modified metadata\n
+        return new RSVP.Queue()\n
+          .push(function () {\n
+            return jIO.util.ajax({\n
+              "type": "GET",\n
+              "url": UriTemplate.parse(site_hal._links.traverse.href)\n
+                                .expand({\n
+                  relative_url: id,\n
+                  view: options._view\n
+                }),\n
+              "xhrFields": {\n
+                withCredentials: true\n
+              }\n
+            });\n
+          })\n
+          .push(undefined, function (error) {\n
+            if ((error.target !== undefined) &&\n
+                (error.target.status === 404)) {\n
+              throw new jIO.util.jIOError("Cannot find document: " + id, 404);\n
+            }\n
+            throw error;\n
+          });\n
+      });\n
+  }\n
+\n
+  var allowed_field_dict = {\n
+    "StringField": null,\n
+    "EmailField": null,\n
+    "IntegerField": null,\n
+    "FloatField": null,\n
+    "TextAreaField": null\n
+  };\n
+\n
+  function extractPropertyFromFormJSON(json) {\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        var form = json._embedded._view,\n
+          converted_json = {\n
+            portal_type: json.portal_type\n
+          },\n
+          form_data_json = {},\n
+          field,\n
+          key,\n
+          prefix_length;\n
+\n
+        form_data_json.form_id = {\n
+          "key": [form.form_id.key],\n
+          "default": form.form_id["default"]\n
+        };\n
+        // XXX How to store datetime\n
+        for (key in form) {\n
+          if (form.hasOwnProperty(key)) {\n
+            field = form[key];\n
+            prefix_length = 0;\n
+            if (key.indexOf(\'my_\') === 0 && field.editable) {\n
+              prefix_length = 3;\n
+            }\n
+            if (key.indexOf(\'your_\') === 0) {\n
+              prefix_length = 5;\n
+            }\n
+            if ((prefix_length !== 0) &&\n
+                (allowed_field_dict.hasOwnProperty(field.type))) {\n
+              form_data_json[key.substring(prefix_length)] = {\n
+                "default": field["default"],\n
+                "key": field.key\n
+              };\n
+              converted_json[key.substring(prefix_length)] = field["default"];\n
+            }\n
+          }\n
+        }\n
+\n
+        return {\n
+          action_href: form._actions.put.href,\n
+          data: converted_json,\n
+          form_data: form_data_json\n
+        };\n
+      });\n
+  }\n
+\n
+  function extractPropertyFromForm(context, id) {\n
+    return context.getAttachment(id, "view")\n
+      .push(function (blob) {\n
+        return jIO.util.readBlobAsText(blob);\n
+      })\n
+      .push(function (evt) {\n
+        return JSON.parse(evt.target.result);\n
+      })\n
+      .push(function (json) {\n
+        return extractPropertyFromFormJSON(json);\n
+      });\n
+  }\n
+\n
+  // XXX docstring\n
+  function ERP5Storage(spec) {\n
+    if (typeof spec.url !== "string" || !spec.url) {\n
+      throw new TypeError("ERP5 \'url\' must be a string " +\n
+                          "which contains more than one character.");\n
+    }\n
+    this._url = spec.url;\n
+    this._default_view_reference = spec.default_view_reference;\n
+  }\n
+\n
+  function convertJSONToGet(json) {\n
+    var key,\n
+      result = json.data;\n
+    // Remove all ERP5 hateoas links / convert them into jIO ID\n
+    for (key in result) {\n
+      if (result.hasOwnProperty(key)) {\n
+        if (!result[key]) {\n
+          delete result[key];\n
+        }\n
+      }\n
+    }\n
+    return result;\n
+  }\n
+\n
+  ERP5Storage.prototype.get = function (id) {\n
+    return extractPropertyFromForm(this, id)\n
+      .push(function (result) {\n
+        return convertJSONToGet(result);\n
+      });\n
+  };\n
+\n
+  ERP5Storage.prototype.bulk = function (request_list) {\n
+    var i,\n
+      storage = this,\n
+      bulk_list = [];\n
+\n
+\n
+    for (i = 0; i < request_list.length; i += 1) {\n
+      if (request_list[i].method !== "get") {\n
+        throw new Error("ERP5Storage: not supported " +\n
+                        request_list[i].method + " in bulk");\n
+      }\n
+      bulk_list.push({\n
+        relative_url: request_list[i].parameter_list[0],\n
+        view: storage._default_view_reference\n
+      });\n
+    }\n
+    return getSiteDocument(storage)\n
+      .push(function (site_hal) {\n
+        var form_data = new FormData();\n
+        form_data.append("bulk_list", JSON.stringify(bulk_list));\n
+        return jIO.util.ajax({\n
+          "type": "POST",\n
+          "url": site_hal._actions.bulk.href,\n
+          "data": form_data,\n
+//           "headers": {\n
+//             "Content-Type": "application/json"\n
+//           },\n
+          "xhrFields": {\n
+            withCredentials: true\n
+          }\n
+        });\n
+      })\n
+      .push(function (response) {\n
+        var result_list = [],\n
+          hateoas = JSON.parse(response.target.responseText);\n
+\n
+        function pushResult(json) {\n
+          json.portal_type = json._links.type.name;\n
+          return extractPropertyFromFormJSON(json)\n
+            .push(function (json2) {\n
+              return convertJSONToGet(json2);\n
+            });\n
+        }\n
+\n
+        for (i = 0; i < hateoas.result_list.length; i += 1) {\n
+          result_list.push(pushResult(hateoas.result_list[i]));\n
+        }\n
+        return RSVP.all(result_list);\n
+      });\n
+  };\n
+\n
+  ERP5Storage.prototype.post = function (data) {\n
+    var context = this,\n
+      new_id;\n
+\n
+    return getSiteDocument(this)\n
+      .push(function (site_hal) {\n
+        var form_data = new FormData();\n
+        form_data.append("portal_type", data.portal_type);\n
+        form_data.append("parent_relative_url", data.parent_relative_url);\n
+        return jIO.util.ajax({\n
+          type: "POST",\n
+          url: site_hal._actions.add.href,\n
+          data: form_data,\n
+          xhrFields: {\n
+            withCredentials: true\n
+          }\n
+        });\n
+      })\n
+      .push(function (evt) {\n
+        var location = evt.target.getResponseHeader("X-Location"),\n
+          uri = new URI(location);\n
+        new_id = uri.segment(2);\n
+        return context.put(new_id, data);\n
+      })\n
+      .push(function () {\n
+        return new_id;\n
+      });\n
+  };\n
+\n
+  ERP5Storage.prototype.put = function (id, data) {\n
+    var context = this;\n
+\n
+    return extractPropertyFromForm(context, id)\n
+      .push(function (result) {\n
+        var key,\n
+          json = result.form_data,\n
+          form_data = {};\n
+        form_data[json.form_id.key] = json.form_id["default"];\n
+\n
+        // XXX How to store datetime:!!!!!\n
+        for (key in data) {\n
+          if (data.hasOwnProperty(key)) {\n
+            if (key === "form_id") {\n
+              throw new jIO.util.jIOError(\n
+                "ERP5: forbidden property: " + key,\n
+                400\n
+              );\n
+            }\n
+            if ((key !== "portal_type") && (key !== "parent_relative_url")) {\n
+              if (!json.hasOwnProperty(key)) {\n
+                throw new jIO.util.jIOError(\n
+                  "ERP5: can not store property: " + key,\n
+                  400\n
+                );\n
+              }\n
+              form_data[json[key].key] = data[key];\n
+            }\n
+          }\n
+        }\n
+        return context.putAttachment(\n
+          id,\n
+          result.action_href,\n
+          new Blob([JSON.stringify(form_data)], {type: "application/json"})\n
+        );\n
+      });\n
+  };\n
+\n
+  ERP5Storage.prototype.allAttachments = function (id) {\n
+    var context = this;\n
+    return getDocumentAndHateoas(this, id)\n
+      .push(function () {\n
+        if (context._default_view_reference === undefined) {\n
+          return {\n
+            links: {}\n
+          };\n
+        }\n
+        return {\n
+          view: {},\n
+          links: {}\n
+        };\n
+      });\n
+  };\n
+\n
+  ERP5Storage.prototype.getAttachment = function (id, action) {\n
+\n
+    if (action === "view") {\n
+      if (this._default_view_reference === undefined) {\n
+        throw new jIO.util.jIOError(\n
+          "Cannot find attachment view for: " + id,\n
+          404\n
+        );\n
+      }\n
+      return getDocumentAndHateoas(this, id,\n
+                                   {"_view": this._default_view_reference})\n
+        .push(function (response) {\n
+          var result = JSON.parse(response.target.responseText);\n
+          result._id = id;\n
+          result.portal_type = result._links.type.name;\n
+          // Remove all ERP5 hateoas links / convert them into jIO ID\n
+\n
+          // XXX Change default action to an jio urn with attachment name inside\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
+            [JSON.stringify(result)],\n
+            {"type": \'application/hal+json\'}\n
+          );\n
+        });\n
+    }\n
+    if (action === "links") {\n
+      return getDocumentAndHateoas(this, id)\n
+        .push(function (response) {\n
+          return new Blob(\n
+            [JSON.stringify(JSON.parse(response.target.responseText))],\n
+            {"type": \'application/hal+json\'}\n
+          );\n
+        });\n
+    }\n
+    if (action.indexOf(this._url) === 0) {\n
+      return new RSVP.Queue()\n
+        .push(function () {\n
+          return jIO.util.ajax({\n
+            "type": "GET",\n
+            "url": action,\n
+            "xhrFields": {\n
+              withCredentials: true\n
+            }\n
+          });\n
+        })\n
+        .push(function (evt) {\n
+          var result = JSON.parse(evt.target.responseText);\n
+          result._id = id;\n
+          return new Blob(\n
+            [JSON.stringify(result)],\n
+            {"type": evt.target.getResponseHeader("Content-Type")}\n
+          );\n
+        });\n
+    }\n
+    throw new jIO.util.jIOError("ERP5: not support get attachment: " + action,\n
+                                400);\n
+  };\n
+\n
+  ERP5Storage.prototype.putAttachment = function (id, name, blob) {\n
+    // Assert we use a callable on a document from the ERP5 site\n
+    if (name.indexOf(this._url) !== 0) {\n
+      throw new jIO.util.jIOError("Can not store outside ERP5: " +\n
+                                  name, 400);\n
+    }\n
+\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return jIO.util.readBlobAsText(blob);\n
+      })\n
+      .push(function (evt) {\n
+        var form_data = JSON.parse(evt.target.result),\n
+          data = new FormData(),\n
+          i,\n
+          key;\n
+        for (key in form_data) {\n
+          if (form_data.hasOwnProperty(key)) {\n
+            if (Array.isArray(form_data[key])) {\n
+              for (i = 0; i < form_data[key].length; i += 1) {\n
+                data.append(key, form_data[key][i]);\n
+              }\n
+            } else {\n
+              data.append(key, form_data[key]);\n
+            }\n
+          }\n
+        }\n
+        return jIO.util.ajax({\n
+          "type": "POST",\n
+          "url": name,\n
+          "data": data,\n
+          "xhrFields": {\n
+            withCredentials: true\n
+          }\n
+        });\n
+      });\n
+  };\n
+\n
+  ERP5Storage.prototype.hasCapacity = function (name) {\n
+    return ((name === "list") || (name === "query") ||\n
+            (name === "select") || (name === "limit") ||\n
+            (name === "sort"));\n
+  };\n
+\n
+  function isSingleLocalRoles(parsed_query) {\n
+    if ((parsed_query instanceof SimpleQuery) &&\n
+        (parsed_query.key === \'local_roles\')) {\n
+      // local_roles:"Assignee"\n
+      return parsed_query.value;\n
+    }\n
   }\n
-}\n
 \n
+  function isMultipleLocalRoles(parsed_query) {\n
+    var i,\n
+      sub_query,\n
+      is_multiple = true,\n
+      local_role_list = [];\n
+    if ((parsed_query instanceof ComplexQuery) &&\n
+        (parsed_query.operator === \'OR\')) {\n
 \n
-  return result;\n
-} // parseStringToObject\n
+      for (i = 0; i < parsed_query.query_list.length; i += 1) {\n
+        sub_query = parsed_query.query_list[i];\n
+        if ((sub_query instanceof SimpleQuery) &&\n
+            (sub_query.key === \'local_roles\')) {\n
+          local_role_list.push(sub_query.value);\n
+        } else {\n
+          is_multiple = false;\n
+        }\n
+      }\n
+      if (is_multiple) {\n
+        // local_roles:"Assignee" OR local_roles:"Assignor"\n
+        return local_role_list;\n
+      }\n
+    }\n
+  }\n
 \n
-Query.parseStringToObject = parseStringToObject;\n
+  ERP5Storage.prototype.buildQuery = function (options) {\n
+//     if (typeof options.query !== "string") {\n
+//       options.query = (options.query ?\n
+//                        jIO.Query.objectToSearchText(options.query) :\n
+//                        undefined);\n
+//     }\n
+    return getSiteDocument(this)\n
+      .push(function (site_hal) {\n
+        var query = options.query,\n
+          i,\n
+          parsed_query,\n
+          sub_query,\n
+          result_list,\n
+          local_roles;\n
+        if (options.query) {\n
+          parsed_query = jIO.QueryFactory.create(options.query);\n
+\n
+          result_list = isSingleLocalRoles(parsed_query);\n
+          if (result_list) {\n
+            query = undefined;\n
+            local_roles = result_list;\n
+          } else {\n
+\n
+            result_list = isMultipleLocalRoles(parsed_query);\n
+            if (result_list) {\n
+              query = undefined;\n
+              local_roles = result_list;\n
+            } else if ((parsed_query instanceof ComplexQuery) &&\n
+                       (parsed_query.operator === \'AND\')) {\n
+\n
+              // portal_type:"Person" AND local_roles:"Assignee"\n
+              for (i = 0; i < parsed_query.query_list.length; i += 1) {\n
+                sub_query = parsed_query.query_list[i];\n
+\n
+                result_list = isSingleLocalRoles(sub_query);\n
+                if (result_list) {\n
+                  local_roles = result_list;\n
+                  parsed_query.query_list.splice(i, 1);\n
+                  query = jIO.Query.objectToSearchText(parsed_query);\n
+                  i = parsed_query.query_list.length;\n
+                } else {\n
+                  result_list = isMultipleLocalRoles(sub_query);\n
+                  if (result_list) {\n
+                    local_roles = result_list;\n
+                    parsed_query.query_list.splice(i, 1);\n
+                    query = jIO.Query.objectToSearchText(parsed_query);\n
+                    i = parsed_query.query_list.length;\n
+                  }\n
+                }\n
+              }\n
+            }\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */\n
-/*global Query: true, query_class_dict: true, inherits: true,\n
-         exports, QueryFactory, RSVP, sequence */\n
+          }\n
+        }\n
 \n
-/**\n
- * The ComplexQuery inherits from Query, and compares one or several metadata\n
- * values.\n
- *\n
- * @class ComplexQuery\n
- * @extends Query\n
- * @param  {Object} [spec={}] The specifications\n
- * @param  {String} [spec.operator="AND"] The compare method to use\n
- * @param  {String} spec.key The metadata key\n
- * @param  {String} spec.value The value of the metadata to compare\n
- */\n
-function ComplexQuery(spec, key_schema) {\n
-  Query.call(this);\n
+        return jIO.util.ajax({\n
+          "type": "GET",\n
+          "url": UriTemplate.parse(site_hal._links.raw_search.href)\n
+                            .expand({\n
+              query: query,\n
+              // XXX Force erp5 to return embedded document\n
+              select_list: options.select_list || ["title", "reference"],\n
+              limit: options.limit,\n
+              sort_on: options.sort_on,\n
+              local_roles: local_roles\n
+            }),\n
+          "xhrFields": {\n
+            withCredentials: true\n
+          }\n
+        });\n
+      })\n
+      .push(function (response) {\n
+        return JSON.parse(response.target.responseText);\n
+      })\n
+      .push(function (catalog_json) {\n
+        var data = catalog_json._embedded.contents,\n
+          count = data.length,\n
+          i,\n
+          uri,\n
+          item,\n
+          result = [];\n
+        for (i = 0; i < count; i += 1) {\n
+          item = data[i];\n
+          uri = new URI(item._links.self.href);\n
+          delete item._links;\n
+          result.push({\n
+            id: uri.segment(2),\n
+            value: item\n
+          });\n
+        }\n
+        return result;\n
+      });\n
+  };\n
 \n
-  /**\n
-   * Logical operator to use to compare object values\n
-   *\n
-   * @attribute operator\n
-   * @type String\n
-   * @default "AND"\n
-   * @optional\n
-   */\n
-  this.operator = spec.operator;\n
+  jIO.addStorage("erp5", ERP5Storage);\n
+\n
+}(jIO, UriTemplate, FormData, RSVP, URI, Blob,\n
+  SimpleQuery, ComplexQuery));\n
+;/*jslint nomen: true*/\n
+/*global RSVP*/\n
+(function (jIO, RSVP) {\n
+  "use strict";\n
 \n
   /**\n
-   * The sub Query list which are used to query an item.\n
+   * The jIO QueryStorage extension\n
    *\n
-   * @attribute query_list\n
-   * @type Array\n
-   * @default []\n
-   * @optional\n
+   * @class QueryStorage\n
+   * @constructor\n
    */\n
-  this.query_list = spec.query_list || [];\n
-  /*jslint unparam: true*/\n
-  this.query_list = this.query_list.map(\n
-    // decorate the map to avoid sending the index as key_schema argument\n
-    function (o, i) { return QueryFactory.create(o, key_schema); }\n
-  );\n
-  /*jslint unparam: false*/\n
-\n
-}\n
-inherits(ComplexQuery, Query);\n
-\n
-ComplexQuery.prototype.operator = "AND";\n
-ComplexQuery.prototype.type = "complex";\n
-\n
-/**\n
- * #crossLink "Query/match:method"\n
- */\n
-ComplexQuery.prototype.match = function (item) {\n
-  var operator = this.operator;\n
-  if (!(/^(?:AND|OR|NOT)$/i.test(operator))) {\n
-    operator = "AND";\n
+  function QueryStorage(spec) {\n
+    this._sub_storage = jIO.createJIO(spec.sub_storage);\n
+    this._key_schema = spec.key_schema;\n
   }\n
-  return this[operator.toUpperCase()](item);\n
-};\n
 \n
-/**\n
- * #crossLink "Query/toString:method"\n
- */\n
-ComplexQuery.prototype.toString = function () {\n
-  var str_list = [], this_operator = this.operator;\n
-  if (this.operator === "NOT") {\n
-    str_list.push("NOT (");\n
-    str_list.push(this.query_list[0].toString());\n
-    str_list.push(")");\n
-    return str_list.join(" ");\n
-  }\n
-  this.query_list.forEach(function (query) {\n
-    str_list.push("(");\n
-    str_list.push(query.toString());\n
-    str_list.push(")");\n
-    str_list.push(this_operator);\n
-  });\n
-  str_list.length -= 1;\n
-  return str_list.join(" ");\n
-};\n
+  QueryStorage.prototype.get = function () {\n
+    return this._sub_storage.get.apply(this._sub_storage, arguments);\n
+  };\n
+  QueryStorage.prototype.allAttachments = function () {\n
+    return this._sub_storage.allAttachments.apply(this._sub_storage, arguments);\n
+  };\n
+  QueryStorage.prototype.post = function () {\n
+    return this._sub_storage.post.apply(this._sub_storage, arguments);\n
+  };\n
+  QueryStorage.prototype.put = function () {\n
+    return this._sub_storage.put.apply(this._sub_storage, arguments);\n
+  };\n
+  QueryStorage.prototype.remove = function () {\n
+    return this._sub_storage.remove.apply(this._sub_storage, arguments);\n
+  };\n
+  QueryStorage.prototype.getAttachment = function () {\n
+    return this._sub_storage.getAttachment.apply(this._sub_storage, arguments);\n
+  };\n
+  QueryStorage.prototype.putAttachment = function () {\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,\n
+                                                    arguments);\n
+  };\n
+  QueryStorage.prototype.repair = function () {\n
+    return this._sub_storage.repair.apply(this._sub_storage, arguments);\n
+  };\n
 \n
-/**\n
- * #crossLink "Query/serialized:method"\n
- */\n
-ComplexQuery.prototype.serialized = function () {\n
-  var s = {\n
-    "type": "complex",\n
-    "operator": this.operator,\n
-    "query_list": []\n
-  };\n
-  this.query_list.forEach(function (query) {\n
-    s.query_list.push(\n
-      typeof query.toJSON === "function" ? query.toJSON() : query\n
-    );\n
-  });\n
-  return s;\n
-};\n
-ComplexQuery.prototype.toJSON = ComplexQuery.prototype.serialized;\n
+  QueryStorage.prototype.hasCapacity = function (name) {\n
+    if (name === "list") {\n
+      return this._sub_storage.hasCapacity(name);\n
+    }\n
+    return true;\n
+  };\n
+  QueryStorage.prototype.buildQuery = function (options) {\n
+    var substorage = this._sub_storage,\n
+      context = this,\n
+      sub_options = {},\n
+      is_manual_query_needed = false,\n
+      is_manual_include_needed = false;\n
 \n
-/**\n
- * Comparison operator, test if all sub queries match the\n
- * item value\n
- *\n
- * @method AND\n
- * @param  {Object} item The item to match\n
- * @return {Boolean} true if all match, false otherwise\n
- */\n
-ComplexQuery.prototype.AND = function (item) {\n
-  var j, promises = [];\n
-  for (j = 0; j < this.query_list.length; j += 1) {\n
-    promises.push(this.query_list[j].match(item));\n
-  }\n
+    if (substorage.hasCapacity("list")) {\n
 \n
-  function cancel() {\n
-    var i;\n
-    for (i = 0; i < promises.length; i += 1) {\n
-      if (typeof promises.cancel === \'function\') {\n
-        promises.cancel();\n
+      // Can substorage handle the queries if needed?\n
+      try {\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) &&\n
+            (error.status_code === 501)) {\n
+          is_manual_query_needed = true;\n
+        } else {\n
+          throw error;\n
+        }\n
       }\n
-    }\n
-  }\n
 \n
-  return new RSVP.Promise(function (resolve, reject) {\n
-    var i, count = 0;\n
-    function resolver(value) {\n
-      if (!value) {\n
-        resolve(false);\n
-      }\n
-      count += 1;\n
-      if (count === promises.length) {\n
-        resolve(true);\n
+      // Can substorage include the docs if needed?\n
+      try {\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) &&\n
+            (error.status_code === 501)) {\n
+          is_manual_include_needed = true;\n
+        } else {\n
+          throw error;\n
+        }\n
       }\n
-    }\n
 \n
-    function rejecter(err) {\n
-      reject(err);\n
-      cancel();\n
-    }\n
+      return substorage.buildQuery(sub_options)\n
+\n
+        // Include docs if needed\n
+        .push(function (result) {\n
+          var include_query_list = [result],\n
+            len,\n
+            i;\n
+\n
+          function safeGet(j) {\n
+            var id = result[j].id;\n
+            return substorage.get(id)\n
+              .push(function (doc) {\n
+                // XXX Can delete user data!\n
+                doc._id = id;\n
+                return doc;\n
+              }, function (error) {\n
+                // Document may have been dropped after listing\n
+                if ((error instanceof jIO.util.jIOError) &&\n
+                    (error.status_code === 404)) {\n
+                  return;\n
+                }\n
+                throw error;\n
+              });\n
+          }\n
+\n
+          if (is_manual_include_needed) {\n
+            len = result.length;\n
+            for (i = 0; i < len; i += 1) {\n
+              include_query_list.push(safeGet(i));\n
+            }\n
+            result = RSVP.all(include_query_list);\n
+          }\n
+          return result;\n
+        })\n
+        .push(function (result) {\n
+          var original_result,\n
+            len,\n
+            i;\n
+          if (is_manual_include_needed) {\n
+            original_result = result[0];\n
+            len = original_result.length;\n
+            for (i = 0; i < len; i += 1) {\n
+              original_result[i].doc = result[i + 1];\n
+            }\n
+            result = original_result;\n
+          }\n
+          return result;\n
+\n
+        })\n
+\n
+        // Manual query if needed\n
+        .push(function (result) {\n
+          var data_rows = [],\n
+            len,\n
+            i;\n
+          if (is_manual_query_needed) {\n
+            len = result.length;\n
+            for (i = 0; i < len; i += 1) {\n
+              result[i].doc.__id = result[i].id;\n
+              data_rows.push(result[i].doc);\n
+            }\n
+            if (options.select_list) {\n
+              options.select_list.push("__id");\n
+            }\n
+            result = jIO.QueryFactory.create(options.query || "",\n
+                                             context._key_schema).\n
+              exec(data_rows, options);\n
+          }\n
+          return result;\n
+        })\n
+\n
+        // reconstruct filtered rows, preserving the order from docs\n
+        .push(function (result) {\n
+          var new_result = [],\n
+            element,\n
+            len,\n
+            i;\n
+          if (is_manual_query_needed) {\n
+            len = result.length;\n
+            for (i = 0; i < len; i += 1) {\n
+              element = {\n
+                id: result[i].__id,\n
+                value: options.select_list ? result[i] : {},\n
+                doc: {}\n
+              };\n
+              if (options.select_list) {\n
+                // Does not work if user manually request __id\n
+                delete element.value.__id;\n
+              }\n
+              if (options.include_docs) {\n
+                // XXX To implement\n
+                throw new Error("QueryStorage does not support include docs");\n
+              }\n
+              new_result.push(element);\n
+            }\n
+            result = new_result;\n
+          }\n
+          return result;\n
+        });\n
 \n
-    for (i = 0; i < promises.length; i += 1) {\n
-      promises[i].then(resolver, rejecter);\n
     }\n
-  }, cancel);\n
-};\n
+  };\n
 \n
-/**\n
- * Comparison operator, test if one of the sub queries matches the\n
- * item value\n
- *\n
- * @method OR\n
- * @param  {Object} item The item to match\n
- * @return {Boolean} true if one match, false otherwise\n
- */\n
-ComplexQuery.prototype.OR =  function (item) {\n
-  var j, promises = [];\n
-  for (j = 0; j < this.query_list.length; j += 1) {\n
-    promises.push(this.query_list[j].match(item));\n
+  jIO.addStorage(\'query\', QueryStorage);\n
+\n
+}(jIO, RSVP));\n
+;/*jslint nomen: true*/\n
+/*global RSVP, Blob*/\n
+(function (jIO, RSVP, Blob) {\n
+  "use strict";\n
+\n
+  /**\n
+   * The jIO FileSystemBridgeStorage extension\n
+   *\n
+   * @class FileSystemBridgeStorage\n
+   * @constructor\n
+   */\n
+  function FileSystemBridgeStorage(spec) {\n
+    this._sub_storage = jIO.createJIO(spec.sub_storage);\n
   }\n
+  var DOCUMENT_EXTENSION = ".json",\n
+    DOCUMENT_KEY = "/.jio_documents/",\n
+    ROOT = "/";\n
 \n
-  function cancel() {\n
-    var i;\n
-    for (i = 0; i < promises.length; i += 1) {\n
-      if (typeof promises.cancel === \'function\') {\n
-        promises.cancel();\n
-      }\n
-    }\n
+  function endsWith(str, suffix) {\n
+    return str.indexOf(suffix, str.length - suffix.length) !== -1;\n
   }\n
 \n
-  return new RSVP.Promise(function (resolve, reject) {\n
-    var i, count = 0;\n
-    function resolver(value) {\n
-      if (value) {\n
-        resolve(true);\n
-      }\n
-      count += 1;\n
-      if (count === promises.length) {\n
-        resolve(false);\n
-      }\n
-    }\n
+  FileSystemBridgeStorage.prototype.get = function (id) {\n
+    var context = this;\n
+    return new RSVP.Queue()\n
 \n
-    function rejecter(err) {\n
-      reject(err);\n
-      cancel();\n
-    }\n
+      // First, try to get explicit reference to the document\n
 \n
-    for (i = 0; i < promises.length; i += 1) {\n
-      promises[i].then(resolver, rejecter);\n
-    }\n
-  }, cancel);\n
-};\n
+      .push(function () {\n
+        // First get the document itself if it exists\n
+        return context._sub_storage.getAttachment(\n
+          DOCUMENT_KEY,\n
+          id + DOCUMENT_EXTENSION,\n
+          {format: "json"}\n
+        );\n
+      })\n
+      .push(undefined, function (error) {\n
+        if ((error instanceof jIO.util.jIOError) &&\n
+            (error.status_code === 404)) {\n
+\n
+          // Second, try to get default attachment\n
+          return context._sub_storage.allAttachments(ROOT)\n
+            .push(function (attachment_dict) {\n
+              if (attachment_dict.hasOwnProperty(id)) {\n
+                return {};\n
+              }\n
+              throw new jIO.util.jIOError("Cannot find document " + id,\n
+                                          404);\n
+            });\n
+        }\n
+        throw error;\n
+      });\n
+  };\n
 \n
-/**\n
- * Comparison operator, test if the sub query does not match the\n
- * item value\n
- *\n
- * @method NOT\n
- * @param  {Object} item The item to match\n
- * @return {Boolean} true if one match, false otherwise\n
- */\n
-ComplexQuery.prototype.NOT = function (item) {\n
-  return sequence([function () {\n
-    return this.query_list[0].match(item);\n
-  }, function (answer) {\n
-    return !answer;\n
-  }]);\n
-};\n
+  FileSystemBridgeStorage.prototype.allAttachments = function (id) {\n
+    var context = this;\n
+    return context._sub_storage.allAttachments(ROOT)\n
+      .push(function (attachment_dict) {\n
+        if (attachment_dict.hasOwnProperty(id)) {\n
+          return {\n
+            enclosure: {}\n
+          };\n
+        }\n
+        // Second get the document itself if it exists\n
+        return context._sub_storage.getAttachment(\n
+          DOCUMENT_KEY,\n
+          id + DOCUMENT_EXTENSION\n
+        )\n
+          .push(function () {\n
+            return {};\n
+          }, function (error) {\n
+            if ((error instanceof jIO.util.jIOError) &&\n
+                (error.status_code === 404)) {\n
+              throw new jIO.util.jIOError("Cannot find document " + id,\n
+                                          404);\n
+            }\n
+            throw error;\n
+          });\n
+      });\n
 \n
-query_class_dict.complex = ComplexQuery;\n
+  };\n
 \n
-exports.ComplexQuery = ComplexQuery;\n
+  FileSystemBridgeStorage.prototype.put = function (doc_id, param) {\n
+    var context = this;\n
+    // XXX Handle conflict!\n
+\n
+    return context._sub_storage.putAttachment(\n
+      DOCUMENT_KEY,\n
+      doc_id + DOCUMENT_EXTENSION,\n
+      new Blob([JSON.stringify(param)], {type: "application/json"})\n
+    )\n
+      .push(undefined, function (error) {\n
+        if ((error instanceof jIO.util.jIOError) &&\n
+            (error.status_code === 404)) {\n
+          return context._sub_storage.put(DOCUMENT_KEY, {})\n
+            .push(function () {\n
+              return context._sub_storage.putAttachment(\n
+                DOCUMENT_KEY,\n
+                doc_id + DOCUMENT_EXTENSION,\n
+                new Blob([JSON.stringify(param)],\n
+                         {type: "application/json"})\n
+              );\n
+            });\n
+        }\n
+        throw error;\n
+      })\n
+      .push(function () {\n
+        return doc_id;\n
+      });\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */\n
-/*global exports, ComplexQuery, SimpleQuery, Query, parseStringToObject,\n
-  query_class_dict */\n
+  };\n
 \n
-/**\n
- * Provides static methods to create Query object\n
- *\n
- * @class QueryFactory\n
- */\n
-function QueryFactory() {\n
-  return;\n
-}\n
+  FileSystemBridgeStorage.prototype.remove = function (doc_id) {\n
+    var context = this,\n
+      got_error = false;\n
+    return new RSVP.Queue()\n
 \n
-/**\n
- * Creates Query object from a search text string or a serialized version\n
- * of a Query.\n
- *\n
- * @method create\n
- * @static\n
- * @param  {Object,String} object The search text or the serialized version\n
- *         of a Query\n
- * @return {Query} A Query object\n
- */\n
-QueryFactory.create = function (object, key_schema) {\n
-  if (object === "") {\n
-    return new Query();\n
-  }\n
-  if (typeof object === "string") {\n
-    object = parseStringToObject(object);\n
-  }\n
-  if (typeof (object || {}).type === "string" &&\n
-      query_class_dict[object.type]) {\n
-    return new query_class_dict[object.type](object, key_schema);\n
-  }\n
-  throw new TypeError("QueryFactory.create(): " +\n
-                      "Argument 1 is not a search text or a parsable object");\n
-};\n
+      // First, try to remove enclosure\n
+      .push(function () {\n
+        return context._sub_storage.removeAttachment(\n
+          ROOT,\n
+          doc_id\n
+        );\n
+      })\n
 \n
-exports.QueryFactory = QueryFactory;\n
+      .push(undefined, function (error) {\n
+        if ((error instanceof jIO.util.jIOError) &&\n
+            (error.status_code === 404)) {\n
+          got_error = true;\n
+          return;\n
+        }\n
+        throw error;\n
+      })\n
+\n
+      // Second, try to remove explicit doc\n
+      .push(function () {\n
+        return context._sub_storage.removeAttachment(\n
+          DOCUMENT_KEY,\n
+          doc_id + DOCUMENT_EXTENSION\n
+        );\n
+      })\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */\n
-/*global Query, exports */\n
+      .push(undefined, function (error) {\n
+        if ((!got_error) && (error instanceof jIO.util.jIOError) &&\n
+            (error.status_code === 404)) {\n
+          return doc_id;\n
+        }\n
+        throw error;\n
+      });\n
 \n
-function objectToSearchText(query) {\n
-  var str_list = [];\n
-  if (query.type === "complex") {\n
-    str_list.push("(");\n
-    (query.query_list || []).forEach(function (sub_query) {\n
-      str_list.push(objectToSearchText(sub_query));\n
-      str_list.push(query.operator);\n
-    });\n
-    str_list.length -= 1;\n
-    str_list.push(")");\n
-    return str_list.join(" ");\n
-  }\n
-  if (query.type === "simple") {\n
-    return (query.key ? query.key + ": " : "") +\n
-      (query.operator || "") + \' "\' + query.value + \'"\';\n
-  }\n
-  throw new TypeError("This object is not a query");\n
-}\n
-Query.objectToSearchText = objectToSearchText;\n
+  };\n
+\n
+  FileSystemBridgeStorage.prototype.hasCapacity = function (capacity) {\n
+    return (capacity === "list");\n
+  };\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */\n
-/*global Query, inherits, query_class_dict, exports,\n
-  searchTextToRegExp, RSVP */\n
+  FileSystemBridgeStorage.prototype.buildQuery = function () {\n
+    var result_dict = {},\n
+      context = this;\n
+    return new RSVP.Queue()\n
+\n
+      // First, get list of explicit documents\n
+\n
+      .push(function () {\n
+        return context._sub_storage.allAttachments(DOCUMENT_KEY);\n
+      })\n
+      .push(function (result) {\n
+        var key;\n
+        for (key in result) {\n
+          if (result.hasOwnProperty(key)) {\n
+            if (endsWith(key, DOCUMENT_EXTENSION)) {\n
+              result_dict[key.substring(\n
+                0,\n
+                key.length - DOCUMENT_EXTENSION.length\n
+              )] = null;\n
+            }\n
+          }\n
+        }\n
+      }, function (error) {\n
+        if ((error instanceof jIO.util.jIOError) &&\n
+            (error.status_code === 404)) {\n
+          return;\n
+        }\n
+        throw error;\n
+      })\n
+\n
+      // Second, get list of enclosure\n
+\n
+      .push(function () {\n
+        return context._sub_storage.allAttachments(ROOT);\n
+      })\n
+      .push(function (result) {\n
+        var key;\n
+        for (key in result) {\n
+          if (result.hasOwnProperty(key)) {\n
+            result_dict[key] = null;\n
+          }\n
+        }\n
+      })\n
+\n
+      // Finally, build the result\n
+\n
+      .push(function () {\n
+        var result = [],\n
+          key;\n
+        for (key in result_dict) {\n
+          if (result_dict.hasOwnProperty(key)) {\n
+            result.push({\n
+              id: key,\n
+              value: {}\n
+            });\n
+          }\n
+        }\n
+        return result;\n
+      });\n
 \n
-var checkKeySchema = function (key_schema) {\n
-  var prop;\n
+  };\n
 \n
-  if (key_schema !== undefined) {\n
-    if (typeof key_schema !== \'object\') {\n
-      throw new TypeError("SimpleQuery().create(): " +\n
-                          "key_schema is not of type \'object\'");\n
+  FileSystemBridgeStorage.prototype.getAttachment = function (id, name) {\n
+    if (name !== "enclosure") {\n
+      throw new jIO.util.jIOError("Only support \'enclosure\' attachment",\n
+                                  400);\n
     }\n
-    // key_set is mandatory\n
-    if (key_schema.key_set === undefined) {\n
-      throw new TypeError("SimpleQuery().create(): " +\n
-                          "key_schema has no \'key_set\' property");\n
+\n
+    return this._sub_storage.getAttachment(ROOT, id);\n
+  };\n
+\n
+  FileSystemBridgeStorage.prototype.putAttachment = function (id, name, blob) {\n
+    if (name !== "enclosure") {\n
+      throw new jIO.util.jIOError("Only support \'enclosure\' attachment",\n
+                                  400);\n
     }\n
-    for (prop in key_schema) {\n
-      if (key_schema.hasOwnProperty(prop)) {\n
-        switch (prop) {\n
-        case \'key_set\':\n
-        case \'cast_lookup\':\n
-        case \'match_lookup\':\n
-          break;\n
-        default:\n
-          throw new TypeError("SimpleQuery().create(): " +\n
-                             "key_schema has unknown property \'" + prop + "\'");\n
-        }\n
-      }\n
+\n
+    return this._sub_storage.putAttachment(\n
+      ROOT,\n
+      id,\n
+      blob\n
+    );\n
+  };\n
+\n
+  FileSystemBridgeStorage.prototype.removeAttachment = function (id, name) {\n
+    if (name !== "enclosure") {\n
+      throw new jIO.util.jIOError("Only support \'enclosure\' attachment",\n
+                                  400);\n
     }\n
-  }\n
-};\n
 \n
+    return this._sub_storage.removeAttachment(ROOT, id);\n
+  };\n
 \n
-/**\n
- * The SimpleQuery inherits from Query, and compares one metadata value\n
- *\n
- * @class SimpleQuery\n
- * @extends Query\n
- * @param  {Object} [spec={}] The specifications\n
- * @param  {String} [spec.operator="="] The compare method to use\n
- * @param  {String} spec.key The metadata key\n
- * @param  {String} spec.value The value of the metadata to compare\n
- */\n
-function SimpleQuery(spec, key_schema) {\n
-  Query.call(this);\n
+  FileSystemBridgeStorage.prototype.repair = function () {\n
+    return this._sub_storage.repair.apply(this._sub_storage, arguments);\n
+  };\n
 \n
-  checkKeySchema(key_schema);\n
+  jIO.addStorage(\'drivetojiomapping\', FileSystemBridgeStorage);\n
 \n
-  this._key_schema = key_schema || {};\n
+}(jIO, RSVP, Blob));\n
+;/*jslint nomen: true*/\n
+/*global Blob, atob, btoa, RSVP*/\n
+(function (jIO, Blob, atob, btoa, RSVP) {\n
+  "use strict";\n
 \n
   /**\n
-   * Operator to use to compare object values\n
+   * The jIO DocumentStorage extension\n
    *\n
-   * @attribute operator\n
-   * @type String\n
-   * @optional\n
+   * @class DocumentStorage\n
+   * @constructor\n
    */\n
-  this.operator = spec.operator;\n
+  function DocumentStorage(spec) {\n
+    this._sub_storage = jIO.createJIO(spec.sub_storage);\n
+    this._document_id = spec.document_id;\n
+    this._repair_attachment = spec.repair_attachment || false;\n
+  }\n
 \n
-  /**\n
-   * Key of the object which refers to the value to compare\n
-   *\n
-   * @attribute key\n
-   * @type String\n
-   */\n
-  this.key = spec.key;\n
+  var DOCUMENT_EXTENSION = ".json",\n
+    DOCUMENT_REGEXP = new RegExp("^jio_document/([\\\\w=]+)" +\n
+                                 DOCUMENT_EXTENSION + "$"),\n
+    ATTACHMENT_REGEXP = new RegExp("^jio_attachment/([\\\\w=]+)/([\\\\w=]+)$");\n
 \n
-  /**\n
-   * Value is used to do the comparison with the object value\n
-   *\n
-   * @attribute value\n
-   * @type String\n
-   */\n
-  this.value = spec.value;\n
+  function getSubAttachmentIdFromParam(id, name) {\n
+    if (name === undefined) {\n
+      return \'jio_document/\' + btoa(id) + DOCUMENT_EXTENSION;\n
+    }\n
+    return \'jio_attachment/\' + btoa(id) + "/" + btoa(name);\n
+  }\n
 \n
-}\n
-inherits(SimpleQuery, Query);\n
+  DocumentStorage.prototype.get = function (id) {\n
+    return this._sub_storage.getAttachment(\n
+      this._document_id,\n
+      getSubAttachmentIdFromParam(id),\n
+      {format: "json"}\n
+    );\n
+  };\n
+\n
+  DocumentStorage.prototype.allAttachments = function (id) {\n
+    return this._sub_storage.allAttachments(this._document_id)\n
+      .push(function (result) {\n
+        var attachments = {},\n
+          exec,\n
+          key;\n
+        for (key in result) {\n
+          if (result.hasOwnProperty(key)) {\n
+            if (ATTACHMENT_REGEXP.test(key)) {\n
+              exec = ATTACHMENT_REGEXP.exec(key);\n
+              try {\n
+                if (atob(exec[1]) === 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
+        }\n
+        return attachments;\n
+      });\n
+  };\n
+\n
+  DocumentStorage.prototype.put = function (doc_id, param) {\n
+    return this._sub_storage.putAttachment(\n
+      this._document_id,\n
+      getSubAttachmentIdFromParam(doc_id),\n
+      new Blob([JSON.stringify(param)], {type: "application/json"})\n
+    )\n
+      .push(function () {\n
+        return doc_id;\n
+      });\n
+\n
+  };\n
+\n
+  DocumentStorage.prototype.remove = function (id) {\n
+    var context = this;\n
+    return this.allAttachments(id)\n
+      .push(function (result) {\n
+        var key,\n
+          promise_list = [];\n
+        for (key in result) {\n
+          if (result.hasOwnProperty(key)) {\n
+            promise_list.push(context.removeAttachment(id, key));\n
+          }\n
+        }\n
+        return RSVP.all(promise_list);\n
+      })\n
+      .push(function () {\n
+        return context._sub_storage.removeAttachment(\n
+          context._document_id,\n
+          getSubAttachmentIdFromParam(id)\n
+        );\n
+      })\n
+      .push(function () {\n
+        return id;\n
+      });\n
+  };\n
+\n
+  DocumentStorage.prototype.repair = function () {\n
+    var context = this;\n
+    return this._sub_storage.repair.apply(this._sub_storage, arguments)\n
+      .push(function (result) {\n
+        if (context._repair_attachment) {\n
+          return context._sub_storage.allAttachments(context._document_id)\n
+            .push(function (result_dict) {\n
+              var promise_list = [],\n
+                id_dict = {},\n
+                attachment_dict = {},\n
+                id,\n
+                attachment,\n
+                exec,\n
+                key;\n
+              for (key in result_dict) {\n
+                if (result_dict.hasOwnProperty(key)) {\n
+                  id = undefined;\n
+                  attachment = undefined;\n
+                  if (DOCUMENT_REGEXP.test(key)) {\n
+                    try {\n
+                      id = atob(DOCUMENT_REGEXP.exec(key)[1]);\n
+                    } catch (error) {\n
+                      // Check if unable to decode base64 data\n
+                      if (!error instanceof ReferenceError) {\n
+                        throw error;\n
+                      }\n
+                    }\n
+                    if (id !== undefined) {\n
+                      id_dict[id] = null;\n
+                    }\n
+                  } else if (ATTACHMENT_REGEXP.test(key)) {\n
+                    exec = ATTACHMENT_REGEXP.exec(key);\n
+                    try {\n
+                      id = atob(exec[1]);\n
+                      attachment = atob(exec[2]);\n
+                    } catch (error) {\n
+                      // Check if unable to decode base64 data\n
+                      if (!error instanceof ReferenceError) {\n
+                        throw error;\n
+                      }\n
+                    }\n
+                    if (attachment !== undefined) {\n
+                      if (!id_dict.hasOwnProperty(id)) {\n
+                        if (!attachment_dict.hasOwnProperty(id)) {\n
+                          attachment_dict[id] = {};\n
+                        }\n
+                        attachment_dict[id][attachment] = null;\n
+                      }\n
+                    }\n
+                  }\n
+                }\n
+              }\n
+              for (id in attachment_dict) {\n
+                if (attachment_dict.hasOwnProperty(id)) {\n
+                  if (!id_dict.hasOwnProperty(id)) {\n
+                    for (attachment in attachment_dict[id]) {\n
+                      if (attachment_dict[id].hasOwnProperty(attachment)) {\n
+                        promise_list.push(context.removeAttachment(\n
+                          id,\n
+                          attachment\n
+                        ));\n
+                      }\n
+                    }\n
+                  }\n
+                }\n
+              }\n
+              return RSVP.all(promise_list);\n
+            });\n
+        }\n
+        return result;\n
+      });\n
+  };\n
+\n
+  DocumentStorage.prototype.hasCapacity = function (capacity) {\n
+    return (capacity === "list");\n
+  };\n
+\n
+  DocumentStorage.prototype.buildQuery = function () {\n
+    return this._sub_storage.allAttachments(this._document_id)\n
+      .push(function (attachment_dict) {\n
+        var result = [],\n
+          key;\n
+        for (key in attachment_dict) {\n
+          if (attachment_dict.hasOwnProperty(key)) {\n
+            if (DOCUMENT_REGEXP.test(key)) {\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
+        return result;\n
+      });\n
+  };\n
+\n
+  DocumentStorage.prototype.getAttachment = function (id, name) {\n
+    return this._sub_storage.getAttachment(\n
+      this._document_id,\n
+      getSubAttachmentIdFromParam(id, name)\n
+    );\n
+  };\n
+\n
+  DocumentStorage.prototype.putAttachment = function (id, name, blob) {\n
+    return this._sub_storage.putAttachment(\n
+      this._document_id,\n
+      getSubAttachmentIdFromParam(id, name),\n
+      blob\n
+    );\n
+  };\n
+\n
+  DocumentStorage.prototype.removeAttachment = function (id, name) {\n
+    return this._sub_storage.removeAttachment(\n
+      this._document_id,\n
+      getSubAttachmentIdFromParam(id, name)\n
+    );\n
+  };\n
+\n
+  jIO.addStorage(\'document\', DocumentStorage);\n
+\n
+}(jIO, Blob, atob, btoa, RSVP));\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
-SimpleQuery.prototype.type = "simple";\n
+/*jslint nomen: true */\n
+/*global indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange*/\n
 \n
-var checkKey = function (key) {\n
-  var prop;\n
+(function (indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange) {\n
+  "use strict";\n
 \n
-  if (key.read_from === undefined) {\n
-    throw new TypeError("Custom key is missing the read_from property");\n
-  }\n
+  // Read only as changing it can lead to data corruption\n
+  var UNITE = 2000000;\n
 \n
-  for (prop in key) {\n
-    if (key.hasOwnProperty(prop)) {\n
-      switch (prop) {\n
-      case \'read_from\':\n
-      case \'cast_to\':\n
-      case \'equal_match\':\n
-        break;\n
-      default:\n
-        throw new TypeError("Custom key has unknown property \'" +\n
-                            prop + "\'");\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
 \n
+  IndexedDBStorage.prototype.hasCapacity = function (name) {\n
+    return ((name === "list") || (name === "include"));\n
+  };\n
 \n
-/**\n
- * #crossLink "Query/match:method"\n
- */\n
-SimpleQuery.prototype.match = function (item) {\n
-  var object_value = null,\n
-    equal_match = null,\n
-    cast_to = null,\n
-    matchMethod = null,\n
-    operator = this.operator,\n
-    value = null,\n
-    key = this.key;\n
-\n
-  /*jslint regexp: true */\n
-  if (!(/^(?:!?=|<=?|>=?)$/i.test(operator))) {\n
-    // `operator` is not correct, we have to change it to "like" or "="\n
-    if (/%/.test(this.value)) {\n
-      // `value` contains a non escaped `%`\n
-      operator = "like";\n
-    } else {\n
-      // `value` does not contain non escaped `%`\n
-      operator = "=";\n
-    }\n
+  function buildKeyPath(key_list) {\n
+    return key_list.join("_");\n
   }\n
 \n
-  matchMethod = this[operator];\n
+  function handleUpgradeNeeded(evt) {\n
+    var db = evt.target.result,\n
+      store;\n
 \n
-  if (this._key_schema.key_set && this._key_schema.key_set[key] !== undefined) {\n
-    key = this._key_schema.key_set[key];\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
-  if (typeof key === \'object\') {\n
-    checkKey(key);\n
-    object_value = item[key.read_from];\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
-    equal_match = key.equal_match;\n
+      request.onabort = function () {\n
+        request.result.close();\n
+        reject("Aborting connection to: " + db_name);\n
+      };\n
 \n
-    // equal_match can be a string\n
-    if (typeof equal_match === \'string\') {\n
-      // XXX raise error if equal_match not in match_lookup\n
-      equal_match = this._key_schema.match_lookup[equal_match];\n
-    }\n
+      request.ontimeout = function () {\n
+        request.result.close();\n
+        reject("Connection to: " + db_name + " timeout");\n
+      };\n
 \n
-    // equal_match overrides the default \'=\' operator\n
-    if (equal_match !== undefined) {\n
-      matchMethod = (operator === "=" || operator === "like" ?\n
-                     equal_match : matchMethod);\n
-    }\n
+      request.onblocked = function () {\n
+        request.result.close();\n
+        reject("Connection to: " + db_name + " was blocked");\n
+      };\n
 \n
-    value = this.value;\n
-    cast_to = key.cast_to;\n
-    if (cast_to) {\n
-      // cast_to can be a string\n
-      if (typeof cast_to === \'string\') {\n
-        // XXX raise error if cast_to not in cast_lookup\n
-        cast_to = this._key_schema.cast_lookup[cast_to];\n
-      }\n
+      // Create DB if necessary //\n
+      request.onupgradeneeded = handleUpgradeNeeded;\n
 \n
-      try {\n
-        value = cast_to(value);\n
-      } catch (e) {\n
-        value = undefined;\n
-      }\n
+      request.onversionchange = function () {\n
+        request.result.close();\n
+        reject(db_name + " was upgraded");\n
+      };\n
 \n
-      try {\n
-        object_value = cast_to(object_value);\n
-      } catch (e) {\n
-        object_value = undefined;\n
-      }\n
+      request.onsuccess = function () {\n
+        resolve(request.result);\n
+      };\n
     }\n
-  } else {\n
-    object_value = item[key];\n
-    value = this.value;\n
+    // XXX Canceller???\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        return new RSVP.Promise(resolver);\n
+      });\n
   }\n
-  if (object_value === undefined || value === undefined) {\n
-    return RSVP.resolve(false);\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
-  return matchMethod(object_value, value);\n
-};\n
 \n
-/**\n
- * #crossLink "Query/toString:method"\n
- */\n
-SimpleQuery.prototype.toString = function () {\n
-  return (this.key ? this.key + ":" : "") +\n
-    (this.operator ? " " + this.operator : "") + \' "\' + this.value + \'"\';\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
-/**\n
- * #crossLink "Query/serialized:method"\n
- */\n
-SimpleQuery.prototype.serialized = function () {\n
-  var object = {\n
-    "type": "simple",\n
-    "key": this.key,\n
-    "value": this.value\n
-  };\n
-  if (this.operator !== undefined) {\n
-    object.operator = this.operator;\n
-  }\n
-  return object;\n
-};\n
-SimpleQuery.prototype.toJSON = SimpleQuery.prototype.serialized;\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
-/**\n
- * Comparison operator, test if this query value matches the item value\n
- *\n
- * @method =\n
- * @param  {String} object_value The value to compare\n
- * @param  {String} comparison_value The comparison value\n
- * @return {Boolean} true if match, false otherwise\n
- */\n
-SimpleQuery.prototype["="] = function (object_value, comparison_value) {\n
-  var value, i;\n
-  if (!Array.isArray(object_value)) {\n
-    object_value = [object_value];\n
-  }\n
-  for (i = 0; i < object_value.length; i += 1) {\n
-    value = object_value[i];\n
-    if (typeof value === \'object\' && value.hasOwnProperty(\'content\')) {\n
-      value = value.content;\n
-    }\n
-    if (typeof value.cmp === "function") {\n
-      return RSVP.resolve(value.cmp(comparison_value) === 0);\n
-    }\n
-    if (\n
-      searchTextToRegExp(comparison_value.toString(), false).\n
-        test(value.toString())\n
-    ) {\n
-      return RSVP.resolve(true);\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
-  return RSVP.resolve(false);\n
-};\n
 \n
-/**\n
- * Comparison operator, test if this query value matches the item value\n
- *\n
- * @method like\n
- * @param  {String} object_value The value to compare\n
- * @param  {String} comparison_value The comparison value\n
- * @return {Boolean} true if match, false otherwise\n
- */\n
-SimpleQuery.prototype.like = function (object_value, comparison_value) {\n
-  var value, i;\n
-  if (!Array.isArray(object_value)) {\n
-    object_value = [object_value];\n
-  }\n
-  for (i = 0; i < object_value.length; i += 1) {\n
-    value = object_value[i];\n
-    if (typeof value === \'object\' && value.hasOwnProperty(\'content\')) {\n
-      value = value.content;\n
-    }\n
-    if (typeof value.cmp === "function") {\n
-      return RSVP.resolve(value.cmp(comparison_value) === 0);\n
-    }\n
-    if (\n
-      searchTextToRegExp(comparison_value.toString()).test(value.toString())\n
-    ) {\n
-      return RSVP.resolve(true);\n
-    }\n
-  }\n
-  return RSVP.resolve(false);\n
-};\n
+  IndexedDBStorage.prototype.buildQuery = function (options) {\n
+    var result_list = [];\n
 \n
-/**\n
- * Comparison operator, test if this query value does not match the item value\n
- *\n
- * @method !=\n
- * @param  {String} object_value The value to compare\n
- * @param  {String} comparison_value The comparison value\n
- * @return {Boolean} true if not match, false otherwise\n
- */\n
-SimpleQuery.prototype["!="] = function (object_value, comparison_value) {\n
-  var value, i;\n
-  if (!Array.isArray(object_value)) {\n
-    object_value = [object_value];\n
-  }\n
-  for (i = 0; i < object_value.length; i += 1) {\n
-    value = object_value[i];\n
-    if (typeof value === \'object\' && value.hasOwnProperty(\'content\')) {\n
-      value = value.content;\n
-    }\n
-    if (typeof value.cmp === "function") {\n
-      return RSVP.resolve(value.cmp(comparison_value) !== 0);\n
-    }\n
-    if (\n
-      searchTextToRegExp(comparison_value.toString(), false).\n
-        test(value.toString())\n
-    ) {\n
-      return RSVP.resolve(false);\n
+    function pushIncludedMetadata(cursor) {\n
+      result_list.push({\n
+        "id": cursor.key,\n
+        "value": {},\n
+        "doc": cursor.value.doc\n
+      });\n
     }\n
-  }\n
-  return RSVP.resolve(true);\n
-};\n
-\n
-/**\n
- * Comparison operator, test if this query value is lower than the item value\n
- *\n
- * @method <\n
- * @param  {Number, String} object_value The value to compare\n
- * @param  {Number, String} comparison_value The comparison value\n
- * @return {Boolean} true if lower, false otherwise\n
- */\n
-SimpleQuery.prototype["<"] = function (object_value, comparison_value) {\n
-  var value;\n
-  if (!Array.isArray(object_value)) {\n
-    object_value = [object_value];\n
-  }\n
-  value = object_value[0];\n
-  if (typeof value === \'object\' && value.hasOwnProperty(\'content\')) {\n
-    value = value.content;\n
-  }\n
-  if (typeof value.cmp === "function") {\n
-    return RSVP.resolve(value.cmp(comparison_value) < 0);\n
-  }\n
-  return RSVP.resolve(value < comparison_value);\n
-};\n
 \n
-/**\n
- * Comparison operator, test if this query value is equal or lower than the\n
- * item value\n
- *\n
- * @method <=\n
- * @param  {Number, String} object_value The value to compare\n
- * @param  {Number, String} comparison_value The comparison value\n
- * @return {Boolean} true if equal or lower, false otherwise\n
- */\n
-SimpleQuery.prototype["<="] = function (object_value, comparison_value) {\n
-  var value;\n
-  if (!Array.isArray(object_value)) {\n
-    object_value = [object_value];\n
-  }\n
-  value = object_value[0];\n
-  if (typeof value === \'object\' && value.hasOwnProperty(\'content\')) {\n
-    value = value.content;\n
-  }\n
-  if (typeof value.cmp === "function") {\n
-    return RSVP.resolve(value.cmp(comparison_value) <= 0);\n
-  }\n
-  return RSVP.resolve(value <= comparison_value);\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
+        if (options.include_docs === true) {\n
+          return handleCursor(tx.objectStore("metadata").index("_id")\n
+                              .openCursor(), pushIncludedMetadata);\n
+        }\n
+        return handleCursor(tx.objectStore("metadata").index("_id")\n
+                            .openKeyCursor(), pushMetadata);\n
+      })\n
+      .push(function () {\n
+        return result_list;\n
+      });\n
 \n
-/**\n
- * Comparison operator, test if this query value is greater than the item\n
- * value\n
- *\n
- * @method >\n
- * @param  {Number, String} object_value The value to compare\n
- * @param  {Number, String} comparison_value The comparison value\n
- * @return {Boolean} true if greater, false otherwise\n
- */\n
-SimpleQuery.prototype[">"] = function (object_value, comparison_value) {\n
-  var value;\n
-  if (!Array.isArray(object_value)) {\n
-    object_value = [object_value];\n
-  }\n
-  value = object_value[0];\n
-  if (typeof value === \'object\' && value.hasOwnProperty(\'content\')) {\n
-    value = value.content;\n
-  }\n
-  if (typeof value.cmp === "function") {\n
-    return RSVP.resolve(value.cmp(comparison_value) > 0);\n
-  }\n
-  return RSVP.resolve(value > comparison_value);\n
-};\n
+  };\n
 \n
-/**\n
- * Comparison operator, test if this query value is equal or greater than the\n
- * item value\n
- *\n
- * @method >=\n
- * @param  {Number, String} object_value The value to compare\n
- * @param  {Number, String} comparison_value The comparison value\n
- * @return {Boolean} true if equal or greater, false otherwise\n
- */\n
-SimpleQuery.prototype[">="] = function (object_value, comparison_value) {\n
-  var value;\n
-  if (!Array.isArray(object_value)) {\n
-    object_value = [object_value];\n
-  }\n
-  value = object_value[0];\n
-  if (typeof value === \'object\' && value.hasOwnProperty(\'content\')) {\n
-    value = value.content;\n
-  }\n
-  if (typeof value.cmp === "function") {\n
-    return RSVP.resolve(value.cmp(comparison_value) >= 0);\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
-  return RSVP.resolve(value >= comparison_value);\n
-};\n
 \n
-query_class_dict.simple = SimpleQuery;\n
-\n
-exports.SimpleQuery = SimpleQuery;\n
+  IndexedDBStorage.prototype.get = function (id) {\n
+    return openIndexedDB(this)\n
+      .push(function (db) {\n
+        var transaction = openTransaction(db, ["metadata"],\n
+                                          "readonly");\n
+        return handleGet(transaction.objectStore("metadata").get(id));\n
+      })\n
+      .push(function (result) {\n
+        return result.doc;\n
+      });\n
+  };\n
 \n
-/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */\n
-/*global Query, RSVP, deepClone */\n
+  IndexedDBStorage.prototype.allAttachments = function (id) {\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(id)),\n
+          handleCursor(transaction.objectStore("attachment").index("_id")\n
+                       .openCursor(IDBKeyRange.only(id)), addEntry)\n
+        ]);\n
+      })\n
+      .push(function () {\n
+        return attachment_dict;\n
+      });\n
+  };\n
 \n
-/**\n
- * Escapes regexp special chars from a string.\n
- *\n
- * @param  {String} string The string to escape\n
- * @return {String} The escaped string\n
- */\n
-function stringEscapeRegexpCharacters(string) {\n
-  if (typeof string === "string") {\n
-    return string.replace(/([\\\\\\.\\$\\[\\]\\(\\)\\{\\}\\^\\?\\*\\+\\-])/g, "\\\\$1");\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
-  throw new TypeError("Query.stringEscapeRegexpCharacters(): " +\n
-                      "Argument no 1 is not of type \'string\'");\n
-}\n
 \n
-Query.stringEscapeRegexpCharacters = stringEscapeRegexpCharacters;\n
+  IndexedDBStorage.prototype.put = function (id, metadata) {\n
+    return openIndexedDB(this)\n
+      .push(function (db) {\n
+        var transaction = openTransaction(db, ["metadata"], "readwrite");\n
+        return handleRequest(transaction.objectStore("metadata").put({\n
+          "_id": id,\n
+          "doc": metadata\n
+        }));\n
+      });\n
+  };\n
 \n
-/**\n
- * Convert metadata values to array of strings. ex:\n
- *\n
- *     "a" -> ["a"],\n
- *     {"content": "a"} -> ["a"]\n
- *\n
- * @param  {Any} value The metadata value\n
- * @return {Array} The value in string array format\n
- */\n
-function metadataValueToStringArray(value) {\n
-  var i, new_value = [];\n
-  if (value === undefined) {\n
-    return undefined;\n
-  }\n
-  if (!Array.isArray(value)) {\n
-    value = [value];\n
-  }\n
-  for (i = 0; i < value.length; i += 1) {\n
-    if (typeof value[i] === \'object\') {\n
-      new_value[i] = value[i].content;\n
-    } else {\n
-      new_value[i] = value[i];\n
-    }\n
-  }\n
-  return new_value;\n
-}\n
+  function deleteEntry(cursor) {\n
+    cursor["delete"]();\n
+  }\n
+\n
+  IndexedDBStorage.prototype.remove = function (id) {\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"](id)),\n
+          // XXX Why not possible to delete with KeyCursor?\n
+          handleCursor(transaction.objectStore("attachment").index("_id")\n
+                       .openCursor(IDBKeyRange.only(id)), deleteEntry),\n
+          handleCursor(transaction.objectStore("blob").index("_id")\n
+                       .openCursor(IDBKeyRange.only(id)), deleteEntry)\n
+        ]);\n
+      });\n
+  };\n
 \n
-/**\n
- * A sort function to sort items by key\n
- *\n
- * @param  {String} key The key to sort on\n
- * @param  {String} [way="ascending"] \'ascending\' or \'descending\'\n
- * @return {Function} The sort function\n
- */\n
-function sortFunction(key, way) {\n
-  if (way === \'descending\') {\n
-    return function (a, b) {\n
-      // this comparison is 5 times faster than json comparison\n
-      var i, l;\n
-      a = metadataValueToStringArray(a[key]) || [];\n
-      b = metadataValueToStringArray(b[key]) || [];\n
-      l = a.length > b.length ? a.length : b.length;\n
-      for (i = 0; i < l; i += 1) {\n
-        if (a[i] === undefined) {\n
-          return 1;\n
-        }\n
-        if (b[i] === undefined) {\n
-          return -1;\n
+  IndexedDBStorage.prototype.getAttachment = function (id, name, options) {\n
+    var transaction,\n
+      type,\n
+      start,\n
+      end;\n
+    if (options === undefined) {\n
+      options = {};\n
+    }\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([id, name])));\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
+        type = attachment.info.content_type;\n
+        start = options.start || 0;\n
+        end = options.end || total_length;\n
+        if (end > total_length) {\n
+          end = total_length;\n
         }\n
-        if (a[i] > b[i]) {\n
-          return -1;\n
+\n
+        if (start < 0 || end < 0) {\n
+          throw new jIO.util.jIOError("_start and _end must be positive",\n
+                                      400);\n
         }\n
-        if (a[i] < b[i]) {\n
-          return 1;\n
+        if (start > end) {\n
+          throw new jIO.util.jIOError("_start is greater than _end",\n
+                                      400);\n
         }\n
-      }\n
-      return 0;\n
-    };\n
-  }\n
-  if (way === \'ascending\') {\n
-    return function (a, b) {\n
-      // this comparison is 5 times faster than json comparison\n
-      var i, l;\n
-      a = metadataValueToStringArray(a[key]) || [];\n
-      b = metadataValueToStringArray(b[key]) || [];\n
-      l = a.length > b.length ? a.length : b.length;\n
-      for (i = 0; i < l; i += 1) {\n
-        if (a[i] === undefined) {\n
-          return -1;\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
-        if (b[i] === undefined) {\n
-          return 1;\n
+\n
+        for (i = start_index; i <= end_index; i += 1) {\n
+          promise_list.push(\n
+            handleGet(store.get(buildKeyPath([id,\n
+                                name, i])))\n
+          );\n
         }\n
-        if (a[i] > b[i]) {\n
-          return 1;\n
+        return RSVP.all(promise_list);\n
+      })\n
+      .push(function (result_list) {\n
+        var array_buffer_list = [],\n
+          blob,\n
+          i,\n
+          index,\n
+          len = result_list.length;\n
+        for (i = 0; i < len; i += 1) {\n
+          array_buffer_list.push(result_list[i].blob);\n
         }\n
-        if (a[i] < b[i]) {\n
-          return -1;\n
+        if ((options.start === undefined) && (options.end === undefined)) {\n
+          return new Blob(array_buffer_list, {type: type});\n
         }\n
-      }\n
-      return 0;\n
-    };\n
-  }\n
-  throw new TypeError("Query.sortFunction(): " +\n
-                      "Argument 2 must be \'ascending\' or \'descending\'");\n
-}\n
-\n
-/**\n
- * Inherits the prototype methods from one constructor into another. The\n
- * prototype of `constructor` will be set to a new object created from\n
- * `superConstructor`.\n
- *\n
- * @param  {Function} constructor The constructor which inherits the super one\n
- * @param  {Function} superConstructor The super constructor\n
- */\n
-function inherits(constructor, superConstructor) {\n
-  constructor.super_ = superConstructor;\n
-  constructor.prototype = Object.create(superConstructor.prototype, {\n
-    "constructor": {\n
-      "configurable": true,\n
-      "enumerable": false,\n
-      "writable": true,\n
-      "value": constructor\n
-    }\n
-  });\n
-}\n
-\n
-/**\n
- * Does nothing\n
- */\n
-function emptyFunction() {\n
-  return;\n
-}\n
-\n
-/**\n
- * Filter a list of items, modifying them to select only wanted keys. If\n
- * `clone` is true, then the method will act on a cloned list.\n
- *\n
- * @param  {Array} select_option Key list to keep\n
- * @param  {Array} list The item list to filter\n
- * @param  {Boolean} [clone=false] If true, modifies a clone of the list\n
- * @return {Array} The filtered list\n
- */\n
-function select(select_option, list, clone) {\n
-  var i, j, new_item;\n
-  if (!Array.isArray(select_option)) {\n
-    throw new TypeError("jioquery.select(): " +\n
-                        "Argument 1 is not of type Array");\n
-  }\n
-  if (!Array.isArray(list)) {\n
-    throw new TypeError("jioquery.select(): " +\n
-                        "Argument 2 is not of type Array");\n
-  }\n
-  if (clone === true) {\n
-    list = deepClone(list);\n
-  }\n
-  for (i = 0; i < list.length; i += 1) {\n
-    new_item = {};\n
-    for (j = 0; j < select_option.length; j += 1) {\n
-      if (list[i].hasOwnProperty([select_option[j]])) {\n
-        new_item[select_option[j]] = list[i][select_option[j]];\n
-      }\n
-    }\n
-    for (j in new_item) {\n
-      if (new_item.hasOwnProperty(j)) {\n
-        list[i] = new_item;\n
-        break;\n
-      }\n
-    }\n
-  }\n
-  return list;\n
-}\n
-\n
-Query.select = select;\n
+        index = Math.floor(start / UNITE) * UNITE;\n
+        blob = new Blob(array_buffer_list, {type: "application/octet-stream"});\n
+        return blob.slice(start - index, end - index,\n
+                          "application/octet-stream");\n
+      });\n
+  };\n
 \n
-/**\n
- * Sort a list of items, according to keys and directions. If `clone` is true,\n
- * then the method will act on a cloned list.\n
- *\n
- * @param  {Array} sort_on_option List of couples [key, direction]\n
- * @param  {Array} list The item list to sort\n
- * @param  {Boolean} [clone=false] If true, modifies a clone of the list\n
- * @return {Array} The filtered list\n
- */\n
-function sortOn(sort_on_option, list, clone) {\n
-  var sort_index;\n
-  if (!Array.isArray(sort_on_option)) {\n
-    throw new TypeError("jioquery.sortOn(): " +\n
-                        "Argument 1 is not of type \'array\'");\n
-  }\n
-  if (clone) {\n
-    list = deepClone(list);\n
-  }\n
-  for (sort_index = sort_on_option.length - 1; sort_index >= 0;\n
-       sort_index -= 1) {\n
-    list.sort(sortFunction(\n
-      sort_on_option[sort_index][0],\n
-      sort_on_option[sort_index][1]\n
-    ));\n
+  function removeAttachment(transaction, id, name) {\n
+    return RSVP.all([\n
+      // XXX How to get the right attachment\n
+      handleRequest(transaction.objectStore("attachment")["delete"](\n
+        buildKeyPath([id, name])\n
+      )),\n
+      handleCursor(transaction.objectStore("blob").index("_id_attachment")\n
+                   .openCursor(IDBKeyRange.only(\n
+          [id, name]\n
+        )),\n
+          deleteEntry\n
+        )\n
+    ]);\n
   }\n
-  return list;\n
-}\n
 \n
-Query.sortOn = sortOn;\n
+  IndexedDBStorage.prototype.putAttachment = function (id, name, blob) {\n
+    var blob_part = [],\n
+      transaction,\n
+      db;\n
 \n
-/**\n
- * Limit a list of items, according to index and length. If `clone` is true,\n
- * then the method will act on a cloned list.\n
- *\n
- * @param  {Array} limit_option A couple [from, length]\n
- * @param  {Array} list The item list to limit\n
- * @param  {Boolean} [clone=false] If true, modifies a clone of the list\n
- * @return {Array} The filtered list\n
- */\n
-function limit(limit_option, list, clone) {\n
-  if (!Array.isArray(limit_option)) {\n
-    throw new TypeError("jioquery.limit(): " +\n
-                        "Argument 1 is not of type \'array\'");\n
-  }\n
-  if (!Array.isArray(list)) {\n
-    throw new TypeError("jioquery.limit(): " +\n
-                        "Argument 2 is not of type \'array\'");\n
-  }\n
-  if (clone) {\n
-    list = deepClone(list);\n
-  }\n
-  list.splice(0, limit_option[0]);\n
-  if (limit_option[1]) {\n
-    list.splice(limit_option[1]);\n
-  }\n
-  return list;\n
-}\n
+    return openIndexedDB(this)\n
+      .push(function (database) {\n
+        db = database;\n
 \n
-Query.limit = limit;\n
+        // Split the blob first\n
+        return jIO.util.readBlobAsArrayBuffer(blob);\n
+      })\n
+      .push(function (event) {\n
+        var array_buffer = event.target.result,\n
+          total_size = blob.size,\n
+          handled_size = 0;\n
 \n
-/**\n
- * Convert a search text to a regexp.\n
- *\n
- * @param  {String} string The string to convert\n
- * @param  {Boolean} [use_wildcard_character=true] Use wildcard "%" and "_"\n
- * @return {RegExp} The search text regexp\n
- */\n
-function searchTextToRegExp(string, use_wildcard_characters) {\n
-  if (typeof string !== \'string\') {\n
-    throw new TypeError("jioquery.searchTextToRegExp(): " +\n
-                        "Argument 1 is not of type \'string\'");\n
-  }\n
-  if (use_wildcard_characters === false) {\n
-    return new RegExp("^" + stringEscapeRegexpCharacters(string) + "$");\n
-  }\n
-  return new RegExp("^" + stringEscapeRegexpCharacters(string).replace(\n
-    /%/g,\n
-    ".*"\n
-  ).replace(\n
-    /_/g,\n
-    "."\n
-  ) + "$");\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
-Query.searchTextToRegExp = searchTextToRegExp;\n
+        // Remove previous attachment\n
+        transaction = openTransaction(db, ["attachment", "blob"], "readwrite");\n
+        return removeAttachment(transaction, id, name);\n
+      })\n
+      .push(function () {\n
+\n
+        var promise_list = [\n
+            handleRequest(transaction.objectStore("attachment").put({\n
+              "_key_path": buildKeyPath([id, name]),\n
+              "_id": id,\n
+              "_attachment": name,\n
+              "info": {\n
+                "content_type": blob.type,\n
+                "length": 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([id, name,\n
+                                         i]),\n
+              "_id" : id,\n
+              "_attachment" : name,\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
-/**\n
- * sequence(thens): Promise\n
- *\n
- * Executes a sequence of *then* callbacks. It acts like\n
- * `smth().then(callback).then(callback)...`. The first callback is called with\n
- * no parameter.\n
- *\n
- * Elements of `thens` array can be a function or an array contaning at most\n
- * three *then* callbacks: *onFulfilled*, *onRejected*, *onNotified*.\n
- *\n
- * When `cancel()` is executed, each then promises are cancelled at the same\n
- * time.\n
- *\n
- * @param  {Array} thens An array of *then* callbacks\n
- * @return {Promise} A new promise\n
- */\n
-function sequence(thens) {\n
-  var promises = [];\n
-  return new RSVP.Promise(function (resolve, reject, notify) {\n
-    var i;\n
-    promises[0] = new RSVP.Promise(function (resolve) {\n
-      resolve();\n
-    });\n
-    for (i = 0; i < thens.length; i += 1) {\n
-      if (Array.isArray(thens[i])) {\n
-        promises[i + 1] = promises[i].\n
-          then(thens[i][0], thens[i][1], thens[i][2]);\n
-      } else {\n
-        promises[i + 1] = promises[i].then(thens[i]);\n
-      }\n
-    }\n
-    promises[i].then(resolve, reject, notify);\n
-  }, function () {\n
-    var i;\n
-    for (i = 0; i < promises.length; i += 1) {\n
-      promises[i].cancel();\n
-    }\n
-  });\n
-}\n
+  IndexedDBStorage.prototype.removeAttachment = function (id, name) {\n
+    return openIndexedDB(this)\n
+      .push(function (db) {\n
+        var transaction = openTransaction(db, ["attachment", "blob"],\n
+                                          "readwrite");\n
+        return removeAttachment(transaction, id, name);\n
+      });\n
+  };\n
 \n
-}));
+  jIO.addStorage("indexeddb", IndexedDBStorage);\n
+}(indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange));
 
 ]]></string> </value>
         </item>
@@ -5599,7 +10659,7 @@ function sequence(thens) {\n
         </item>
         <item>
             <key> <string>size</string> </key>
-            <value> <int>155851</int> </value>
+            <value> <int>371128</int> </value>
         </item>
         <item>
             <key> <string>title</string> </key>
diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/jio_sha256.amd.js.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/jio_sha256.amd.js.xml
deleted file mode 100644
index c0ce672792da91201db6b36e72b1216027c8af21..0000000000000000000000000000000000000000
--- a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/jio_sha256.amd.js.xml
+++ /dev/null
@@ -1,138 +0,0 @@
-<?xml version="1.0"?>
-<ZopeData>
-  <record id="1" aka="AAAAAAAAAAE=">
-    <pickle>
-      <global name="File" module="OFS.Image"/>
-    </pickle>
-    <pickle>
-      <dictionary>
-        <item>
-            <key> <string>_Cacheable__manager_id</string> </key>
-            <value> <string>http_cache</string> </value>
-        </item>
-        <item>
-            <key> <string>_EtagSupport__etag</string> </key>
-            <value> <string>ts16413671.77</string> </value>
-        </item>
-        <item>
-            <key> <string>__name__</string> </key>
-            <value> <string>jio_sha256.amd.js</string> </value>
-        </item>
-        <item>
-            <key> <string>content_type</string> </key>
-            <value> <string>application/javascript</string> </value>
-        </item>
-        <item>
-            <key> <string>data</string> </key>
-            <value> <string encoding="cdata"><![CDATA[
-
-(function (dependencies, module) {\n
-    if (typeof define === \'function\' && define.amd) {\n
-        return define(dependencies, module);\n
-    }\n
-    if (typeof exports === \'object\') {\n
-        return module(exports);\n
-    }\n
-    module(window);\n
-}([\'exports\'], function (window) {\n
-/* A JavaScript implementation of the Secure Hash Algorithm, SHA-256\n
- * Version 0.3 Copyright Angel Marin 2003-2004 - http://anmar.eu.org/\n
- * Distributed under the BSD License\n
- * Some bits taken from Paul Johnston\'s SHA-1 implementation\n
- */\n
-(function () {\n
-    var chrsz = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode  */\n
-    function safe_add (x, y) {\n
-        var lsw = (x & 0xFFFF) + (y & 0xFFFF);\n
-        var msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n
-        return (msw << 16) | (lsw & 0xFFFF);\n
-    }\n
-    function S (X, n) {return ( X >>> n ) | (X << (32 - n));}\n
-    function R (X, n) {return ( X >>> n );}\n
-    function Ch(x, y, z) {return ((x & y) ^ ((~x) & z));}\n
-    function Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));}\n
-    function Sigma0256(x) {return (S(x, 2) ^ S(x, 13) ^ S(x, 22));}\n
-    function Sigma1256(x) {return (S(x, 6) ^ S(x, 11) ^ S(x, 25));}\n
-    function Gamma0256(x) {return (S(x, 7) ^ S(x, 18) ^ R(x, 3));}\n
-    function Gamma1256(x) {return (S(x, 17) ^ S(x, 19) ^ R(x, 10));}\n
-    function newArray (n) {\n
-        var a = [];\n
-        for (;n>0;n--) {\n
-            a.push(undefined);\n
-        }\n
-        return a;\n
-    }\n
-    function core_sha256 (m, l) {\n
-        var K = [0x428A2F98,0x71374491,0xB5C0FBCF,0xE9B5DBA5,0x3956C25B,0x59F111F1,0x923F82A4,0xAB1C5ED5,0xD807AA98,0x12835B01,0x243185BE,0x550C7DC3,0x72BE5D74,0x80DEB1FE,0x9BDC06A7,0xC19BF174,0xE49B69C1,0xEFBE4786,0xFC19DC6,0x240CA1CC,0x2DE92C6F,0x4A7484AA,0x5CB0A9DC,0x76F988DA,0x983E5152,0xA831C66D,0xB00327C8,0xBF597FC7,0xC6E00BF3,0xD5A79147,0x6CA6351,0x14292967,0x27B70A85,0x2E1B2138,0x4D2C6DFC,0x53380D13,0x650A7354,0x766A0ABB,0x81C2C92E,0x92722C85,0xA2BFE8A1,0xA81A664B,0xC24B8B70,0xC76C51A3,0xD192E819,0xD6990624,0xF40E3585,0x106AA070,0x19A4C116,0x1E376C08,0x2748774C,0x34B0BCB5,0x391C0CB3,0x4ED8AA4A,0x5B9CCA4F,0x682E6FF3,0x748F82EE,0x78A5636F,0x84C87814,0x8CC70208,0x90BEFFFA,0xA4506CEB,0xBEF9A3F7,0xC67178F2];\n
-        var HASH = [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19];\n
-        var W = newArray(64);\n
-        var a, b, c, d, e, f, g, h, i, j;\n
-        var T1, T2;\n
-        /* append padding */\n
-        m[l >> 5] |= 0x80 << (24 - l % 32);\n
-        m[((l + 64 >> 9) << 4) + 15] = l;\n
-        for ( var i = 0; i<m.length; i+=16 ) {\n
-            a = HASH[0]; b = HASH[1]; c = HASH[2]; d = HASH[3];\n
-            e = HASH[4]; f = HASH[5]; g = HASH[6]; h = HASH[7];\n
-            for ( var j = 0; j<64; j++) {\n
-                if (j < 16) {\n
-                    W[j] = m[j + i];\n
-                } else {\n
-                    W[j] = safe_add(safe_add(safe_add(Gamma1256(\n
-                        W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]);\n
-                }\n
-                T1 = safe_add(safe_add(safe_add(\n
-                    safe_add(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]);\n
-                T2 = safe_add(Sigma0256(a), Maj(a, b, c));\n
-                h = g; g = f; f = e; e = safe_add(d, T1);\n
-                d = c; c = b; b = a; a = safe_add(T1, T2);\n
-            }\n
-            HASH[0] = safe_add(a, HASH[0]); HASH[1] = safe_add(b, HASH[1]);\n
-            HASH[2] = safe_add(c, HASH[2]); HASH[3] = safe_add(d, HASH[3]);\n
-            HASH[4] = safe_add(e, HASH[4]); HASH[5] = safe_add(f, HASH[5]);\n
-            HASH[6] = safe_add(g, HASH[6]); HASH[7] = safe_add(h, HASH[7]);\n
-        }\n
-        return HASH;\n
-    }\n
-    function str2binb (str) {\n
-        var bin = Array();\n
-        var mask = (1 << chrsz) - 1;\n
-        for(var i = 0; i < str.length * chrsz; i += chrsz)\n
-            bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32);\n
-        return bin;\n
-    }\n
-    function binb2hex (binarray) {\n
-        var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */\n
-        var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";\n
-        var str = "";\n
-        for (var i = 0; i < binarray.length * 4; i++) {\n
-            str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +\n
-                hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8  )) & 0xF);\n
-        }\n
-        return str;\n
-    }\n
-    function hex_sha256(s){\n
-        return binb2hex(core_sha256(str2binb(s),s.length * chrsz));\n
-    }\n
-    window.hex_sha256 = hex_sha256;\n
-}());\n
-}));
-
-]]></string> </value>
-        </item>
-        <item>
-            <key> <string>precondition</string> </key>
-            <value> <string></string> </value>
-        </item>
-        <item>
-            <key> <string>size</string> </key>
-            <value> <int>4494</int> </value>
-        </item>
-        <item>
-            <key> <string>title</string> </key>
-            <value> <string></string> </value>
-        </item>
-      </dictionary>
-    </pickle>
-  </record>
-</ZopeData>