From 1dda23789e70b8dcb99edd6725c589c3545790e8 Mon Sep 17 00:00:00 2001 From: Alan Kligman Date: Sat, 25 May 2013 11:15:00 -0400 Subject: [PATCH] Modified when.js to avoid deferring callbacks using setTimeout (this breaks IndexedDB). Added promise-based API wrapper for IndexedDB. --- dist/idbfs.min.js | 3 - examples/deferred-test.html | 268 ++++++ examples/test5.html | 12 + lib/when.js | 1527 ++++++++++++++++++----------------- src/event.js | 48 ++ src/fs2.js | 57 ++ src/indexeddb.js | 218 +++++ 7 files changed, 1410 insertions(+), 723 deletions(-) delete mode 100644 dist/idbfs.min.js create mode 100644 examples/deferred-test.html create mode 100644 examples/test5.html create mode 100644 src/event.js create mode 100644 src/fs2.js create mode 100644 src/indexeddb.js diff --git a/dist/idbfs.min.js b/dist/idbfs.min.js deleted file mode 100644 index db81f45..0000000 --- a/dist/idbfs.min.js +++ /dev/null @@ -1,3 +0,0 @@ -/*! idbfs 2013-03-11 */ -(function(n,t){"object"==typeof exports?module.exports=t():"function"==typeof define&&define.amd?define(t):n.IDBFS||(n.IDBFS=t())})(this,function(){var n,t,e;if(function(r){function o(n,t){return b.call(n,t)}function i(n,t){var e,r,o,i,a,s,u,c,l,f,p=t&&t.split("/"),h=m.map,g=h&&h["*"]||{};if(n&&"."===n.charAt(0))if(t){for(p=p.slice(0,p.length-1),n=p.concat(n.split("/")),c=0;n.length>c;c+=1)if(f=n[c],"."===f)n.splice(c,1),c-=1;else if(".."===f){if(1===c&&(".."===n[2]||".."===n[0]))break;c>0&&(n.splice(c-1,2),c-=2)}n=n.join("/")}else 0===n.indexOf("./")&&(n=n.substring(2));if((p||g)&&h){for(e=n.split("/"),c=e.length;c>0;c-=1){if(r=e.slice(0,c).join("/"),p)for(l=p.length;l>0;l-=1)if(o=h[p.slice(0,l).join("/")],o&&(o=o[r])){i=o,a=c;break}if(i)break;!s&&g&&g[r]&&(s=g[r],u=c)}!i&&s&&(i=s,a=u),i&&(e.splice(0,a,i),n=e.join("/"))}return n}function a(n,t){return function(){return h.apply(r,x.call(arguments,0).concat([n,t]))}}function s(n){return function(t){return i(t,n)}}function u(n){return function(t){v[n]=t}}function c(n){if(o(y,n)){var t=y[n];delete y[n],_[n]=!0,p.apply(r,t)}if(!o(v,n)&&!o(_,n))throw Error("No "+n);return v[n]}function l(n){var t,e=n?n.indexOf("!"):-1;return e>-1&&(t=n.substring(0,e),n=n.substring(e+1,n.length)),[t,n]}function f(n){return function(){return m&&m.config&&m.config[n]||{}}}var p,h,g,d,v={},y={},m={},_={},b=Object.prototype.hasOwnProperty,x=[].slice;g=function(n,t){var e,r=l(n),o=r[0];return n=r[1],o&&(o=i(o,t),e=c(o)),o?n=e&&e.normalize?e.normalize(n,s(t)):i(n,t):(n=i(n,t),r=l(n),o=r[0],n=r[1],o&&(e=c(o))),{f:o?o+"!"+n:n,n:n,pr:o,p:e}},d={require:function(n){return a(n)},exports:function(n){var t=v[n];return t!==void 0?t:v[n]={}},module:function(n){return{id:n,uri:"",exports:v[n],config:f(n)}}},p=function(n,t,e,i){var s,l,f,p,h,m,b=[];if(i=i||n,"function"==typeof e){for(t=!t.length&&e.length?["require","exports","module"]:t,h=0;t.length>h;h+=1)if(p=g(t[h],i),l=p.f,"require"===l)b[h]=d.require(n);else if("exports"===l)b[h]=d.exports(n),m=!0;else if("module"===l)s=b[h]=d.module(n);else if(o(v,l)||o(y,l)||o(_,l))b[h]=c(l);else{if(!p.p)throw Error(n+" missing "+l);p.p.load(p.n,a(i,!0),u(l),{}),b[h]=v[l]}f=e.apply(v[n],b),n&&(s&&s.exports!==r&&s.exports!==v[n]?v[n]=s.exports:f===r&&m||(v[n]=f))}else n&&(v[n]=e)},n=t=h=function(n,t,e,o,i){return"string"==typeof n?d[n]?d[n](t):c(g(n,t).f):(n.splice||(m=n,t.splice?(n=t,t=e,e=null):n=r),t=t||function(){},"function"==typeof e&&(e=o,o=i),o?p(r,n,t,e):setTimeout(function(){p(r,n,t,e)},4),h)},h.config=function(n){return m=n,m.deps&&h(m.deps,m.callback),h},e=function(n,t,e){t.splice||(e=t,t=[]),o(v,n)||o(y,n)||(y[n]=[n,t,e])},e.amd={jQuery:!0}}(),e("build/almond",function(){}),function(n){n("when",[],function(){function n(){}function t(n){return Array(n)}function e(){}function r(n){var t=new e;return t.then=function(t){i(arguments);var e;try{return t&&(e=t(n)),c(e===_?n:e)}catch(r){return o(r)}},y(t)}function o(n){var t=new e;return t.then=function(t,e){i(arguments);var r;try{return e?(r=e(n),c(r===_?n:r)):o(n)}catch(a){return o(a)}},y(t)}function i(n){for(var t,e=n.length;e;)if(t=n[--e],null!=t&&"function"!=typeof t)throw Error("callback is not a function")}function a(){function n(n,t,e){return h(n,t,e)}function t(n){d(r(n))}function s(n){d(o(n))}function u(n){g(n)}var c,l,f,p,h,g,d;return f=[],p=[],h=function(n,t,e){i(arguments);var r=a();return f.push(function(e){e.then(n,t).then(r.resolve,r.reject,r.progress)}),e&&p.push(e),r.promise},g=function(n){for(var t,e=0;t=p[e++];)t(n)},d=function(n){var t,e=0;for(h=n.then,d=g=function(){throw Error("already completed")},p=_;t=f[e++];)t(n);f=[]},c={},l=new e,l.then=c.then=n,c.promise=y(l),c.resolver=y({resolve:c.resolve=t,reject:c.reject=s,progress:c.progress=u}),c}function s(n){return n&&"function"==typeof n.then}function u(n,t,e,r){var o=c(n);return o.then(t,e,r)}function c(n){var t,r;return n instanceof e?t=n:(r=a(),s(n)?(n.then(r.resolve,r.reject,r.progress),t=r.promise):(r.resolve(n),t=r.promise)),t}function l(t,e,r,o,i){function s(n){v(n)}function c(n){y(n)}function l(n){m(n)}function f(){v=y=m=n}var p,h,g,d,v,y,m,_,b;if(_=t.length>>>0,p=Math.max(0,Math.min(e,_)),h=[],d=a(),g=u(d,r,o,i),p)for(v=function(n){h.push(n),--p||(f(),d.resolve(h))},y=function(n){f(),d.reject(n)},m=d.progress,b=0;_>b;++b)b in t&&u(t[b],s,c,l);else d.resolve(h);return g}function f(n,e,r,o){var i,a;return i=t(n.length),a=d(n,p,i),u(a,e,r,o)}function p(n,t,e){return n[e]=t,n}function h(n,t,e,r){function o(n){return t(n[0])}return l(n,1,o,e,r)}function g(n,e){var r,o;for(o=n.length,r=t(o);o>=0;--o)o in n&&(r[o]=u(n[o],e));return d(r,p,r)}function d(n,t,e){var r,o;return r=n.length,o=[function(n,e,o){return u(n,function(n){return u(e,function(e){return t(n,e,o,r)})})}],arguments.length>=3&&o.push(e),c(m.apply(n,o))}function v(n,t,e){var r=arguments.length>2;return u(n,function(n){return r&&(n=e),t.resolve(n),n},function(n){return t.reject(n),o(n)},t.progress)}var y,m,_;return y=Object.freeze||function(n){return n},m=[].reduce||function(n){var t,e,r,o,i;if(i=0,t=Object(this),o=t.length>>>0,e=arguments,1>=e.length)for(;;){if(i in t){r=t[i++];break}if(++i>=o)throw new TypeError}else r=e[1];for(;o>i;++i)i in t&&(r=n(r,t[i],i,t));return r},u.defer=a,u.isPromise=s,u.some=l,u.all=f,u.any=h,u.reduce=d,u.map=g,u.chain=v,u})}("function"==typeof e?e:function(n){"undefined"!=typeof module?module.exports=n():this.when=n()}),function(n,t){function r(n){return n&&n.__wrapped__?n:this instanceof r?(this.__wrapped__=n,t):new r(n)}function o(n,t,e){t||(t=0);var r=n.length,o=r-t>=(e||Ot),i=o?{}:n;if(o)for(var a,s=t-1;r>++s;)a=n[s]+"",(Vt.call(i,a)?i[a]:i[a]=[]).push(n[s]);return function(n){if(o){var e=n+"";return Vt.call(i,e)&&F(i[e],n)>-1}return F(i,n,t)>-1}}function i(n,e){var r=n.index,o=e.index;if(n=n.criteria,e=e.criteria,n!==e){if(n>e||n===t)return 1;if(e>n||e===t)return-1}return o>r?-1:1}function a(n,t,e){function r(){var s=arguments,u=i?this:t;if(o||(n=t[a]),e.length&&(s=s.length?e.concat(Xt.call(s)):e),this instanceof r){f.prototype=n.prototype,u=new f;var c=n.apply(u,s);return c&&ze[typeof c]?c:u}return n.apply(u,s)}var o=g(n),i=!e,a=n;return i&&(e=t),r}function s(n,e){return n?"function"!=typeof n?function(t){return t[n]}:e!==t?function(t,r,o){return n.call(e,t,r,o)}:n:pt}function u(){for(var n=-1,t=arguments.length,e={bottom:"",hasDontEnumBug:St,isKeysFast:Be,noArgsEnum:_e,noCharByIndex:we,shadowed:Kt,top:"",useHas:!0,useStrict:Ae,arrayBranch:{},objectBranch:{}};t>++n;){var r=arguments[n];for(var o in r){var a=r[o];/beforeLoop|inLoop/.test(o)?("string"==typeof a&&(a={array:a,object:a}),e.arrayBranch[o]=a.array,e.objectBranch[o]=a.object):e[o]=a}}var u=e.args,c=/^[^,]+/.exec(u)[0],l=e.init;e.firstArg=c,e.init=null==l?c:l,"collection"==c&&e.arrayBranch.inLoop||(e.arrayBranch=null);var f=Function("arrayLikeClasses, ArrayProto, bind, compareAscending, concat, createCallback, forIn, hasOwnProperty, indexOf, isArguments, isArray, isFunction, isPlainObject, objectClass, objectTypes, nativeKeys, propertyIsEnumerable, slice, stringClass, toString, undefined","var callee = function("+u+") {\n"+Ne(e)+"\n};\n"+"return callee");return f(Le,Bt,nt,i,Qt,s,Xe,Vt,F,h,Ue,g,Ke,ge,ze,oe,Jt,Xt,ve,Yt)}function c(n){return"\\"+Oe[n]}function l(n){return Qe[n]}function f(){}function p(n){return Ve[n]}function h(n){return Yt.call(n)==ue}function g(n){return"function"==typeof n}function d(n){var t=!1;if(!n||"object"!=typeof n||h(n))return t;var e=n.constructor;return je&&"function"!=typeof n.toString&&"string"==typeof(n+"")||g(e)&&!(e instanceof e)?t:It?(Xe(n,function(n,e,r){return t=!Vt.call(r,e),!1}),t===!1):(Xe(n,function(n,e){t=e}),t===!1||Vt.call(n,t))}function v(n,t,e,r,o){if(null==n)return n;e&&(t=!1);var i=ze[typeof n];if(i){var a=Yt.call(n);if(!Ie[a]||be&&h(n))return n;var s=a==ce;i=s||(a==ge?Ke(n):i)}if(!i||!t)return i?s?Xt.call(n):Je({},n):n;var u=n.constructor;switch(a){case le:return new u(1==n);case fe:return new u(+n);case he:case ve:return new u(n);case de:return u(n.source,Tt.exec(n))}r||(r=[]),o||(o=[]);for(var c=r.length;c--;)if(r[c]==n)return o[c];var l=s?u(c=n.length):{};if(r.push(n),o.push(l),s)for(var f=-1;c>++f;)l[f]=v(n[f],t,null,r,o);else Ye(n,function(n,e){l[e]=v(n,t,null,r,o)});return l}function y(n,t){return n?Vt.call(n,t):!1}function m(n){return n===!0||n===!1||Yt.call(n)==le}function _(n){return Yt.call(n)==fe}function b(n){return n?1===n.nodeType:!1}function x(n,t,e,r){if(null==n||null==t)return n===t;if(n===t)return 0!==n||1/n==1/t;(ze[typeof n]||ze[typeof t])&&(n=n.__wrapped__||n,t=t.__wrapped__||t);var o=Yt.call(n);if(o!=Yt.call(t))return!1;switch(o){case le:case fe:return+n==+t;case he:return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case de:case ve:return n==t+""}var i=Le[o];if(be&&!i&&(i=h(n))&&!h(t))return!1;if(!i&&(o!=ge||je&&("function"!=typeof n.toString&&"string"==typeof(n+"")||"function"!=typeof t.toString&&"string"==typeof(t+""))))return!1;e||(e=[]),r||(r=[]);for(var a=e.length;a--;)if(e[a]==n)return r[a]==t;var s=-1,u=!0,c=0;if(e.push(n),r.push(t),i){if(c=n.length,u=c==t.length)for(;c--&&(u=x(n[c],t[c],e,r)););return u}var l=n.constructor,f=t.constructor;if(l!=f&&!(g(l)&&l instanceof l&&g(f)&&f instanceof f))return!1;for(var p in n)if(Vt.call(n,p)&&(c++,!Vt.call(t,p)||!x(n[p],t[p],e,r)))return!1;for(p in t)if(Vt.call(t,p)&&!c--)return!1;if(St)for(;7>++s;)if(p=Kt[s],Vt.call(n,p)&&(!Vt.call(t,p)||!x(n[p],t[p],e,r)))return!1;return!0}function w(n){return re(n)&&Yt.call(n)==he}function j(n){return n?ze[typeof n]:!1}function E(n){return Yt.call(n)==he&&n!=+n}function k(n){return null===n}function B(n){return Yt.call(n)==he}function A(n){return Yt.call(n)==de}function S(n){return Yt.call(n)==ve}function L(n){return n===t}function I(n,t,e,r){var o=n,i=n?n.length:0,a=3>arguments.length;if(i!==+i){var s=tr(n);i=s.length}else we&&Yt.call(n)==ve&&(o=n.split(""));return pr(n,function(n,u,c){u=s?s[--i]:--i,e=a?(a=!1,o[u]):t.call(r,e,o[u],u,c)}),e}function z(n){var t=n?n.length:0;return t===+t?t:tr(n).length}function O(n){if(!n)return[];var t=n.length;return t===+t?(xe?Yt.call(n)==ve:"string"==typeof n)?n.split(""):Xt.call(n):ar(n)}function N(n){for(var t=-1,e=n?n.length:0,r=[];e>++t;)n[t]&&r.push(n[t]);return r}function D(n){var t=[];if(!n)return t;for(var e=-1,r=n.length,i=Qt.apply(Bt,arguments),a=o(i,r);r>++e;){var s=n[e];a(s)||t.push(s)}return t}function C(n,e,r){return n?null==e||r?n[0]:Xt.call(n,0,e):t}function P(n,t){for(var e,r=-1,o=n?n.length:0,i=[];o>++r;)e=n[r],Ue(e)?Gt.apply(i,t?e:P(e)):i.push(e);return i}function F(n,t,e){var r=-1,o=n?n.length:0;if("number"==typeof e)r=(0>e?ie(0,o+e):e||0)-1;else if(e)return r=V(n,t),n[r]===t?r:-1;for(;o>++r;)if(n[r]===t)return r;return-1}function H(n,t,e){return n?Xt.call(n,0,-(null==t||e?1:t)):[]}function T(n){var t=arguments.length,e=[],r=-1,i=n?n.length:0,a=[];n:for(;i>++r;){var s=n[r];if(0>F(a,s)){for(var u=1;t>u;u++)if(!(e[u]||(e[u]=o(arguments[u])))(s))continue n;a.push(s)}}return a}function q(n,t,e){if(n){var r=n.length;return null==t||e?n[r-1]:Xt.call(n,-t||r)}}function R(n,t,e){var r=n?n.length:0;for("number"==typeof e&&(r=(0>e?ie(0,r+e):ae(e,r-1))+1);r--;)if(n[r]===t)return r;return-1}function $(n,t,e){var r,o=-1/0,i=-1,a=n?n.length:0,u=o;for(t=s(t,e);a>++i;)r=t(n[i],i,n),r>o&&(o=r,u=n[i]);return u}function M(n,t,e){var r,o=1/0,i=-1,a=n?n.length:0,u=o;for(t=s(t,e);a>++i;)r=t(n[i],i,n),o>r&&(o=r,u=n[i]);return u}function U(n,t){for(var e=-1,r=n?n.length:0,o={};r>++e;)t?o[n[e]]=t[e]:o[n[e][0]]=n[e][1];return o}function K(n,t,e){n=+n||0,e=+e||1,null==t&&(t=n,n=0);for(var r=-1,o=ie(0,Math.ceil((t-n)/e)),i=Array(o);o>++r;)i[r]=n,n+=e;return i}function W(n,t,e){return n?Xt.call(n,null==t||e?1:t):[]}function Q(n){for(var t=-1,e=n?n.length:0,r=Array(e);e>++t;){var o=ne(se()*(t+1));r[t]=r[o],r[o]=n[t]}return r}function V(n,t,e,r){var o=0,i=n?n.length:o;for(e=s(e,r),t=e(t);i>o;){var a=o+i>>>1;t>e(n[a])?o=a+1:i=a}return o}function G(){for(var n=-1,t=Qt.apply(Bt,arguments),e=t.length,r=[];e>++n;)0>F(r,t[n])&&r.push(t[n]);return r}function J(n,t,e,r){var o=-1,i=n?n.length:0,a=[],u=[];for("function"==typeof t&&(r=e,e=t,t=!1),e=s(e,r);i>++o;){var c=e(n[o],o,n);(t?!o||u[u.length-1]!==c:0>F(u,c))&&(u.push(c),a.push(n[o]))}return a}function X(n){for(var t=-1,e=n?n.length:0,r=o(arguments,1,20),i=[];e>++t;){var a=n[t];r(a)||i.push(a)}return i}function Y(n){for(var t=-1,e=n?$(vr(arguments,"length")):0,r=Array(e);e>++t;)r[t]=vr(arguments,t);return r}function Z(n,e){return 1>n?e():function(){return 1>--n?e.apply(this,arguments):t}}function nt(n,t){return ke||Zt&&arguments.length>2?Zt.call.apply(Zt,arguments):a(n,t,Xt.call(arguments,2))}function tt(){var n=arguments;return function(){for(var t=arguments,e=n.length;e--;)t=[n[e].apply(this,t)];return t[0]}}function et(n,t,e){function r(){s=null,e||(i=n.apply(a,o))}var o,i,a,s;return function(){var u=e&&!s;return o=arguments,a=this,ye(s),s=me(r,t),u&&(i=n.apply(a,o)),i}}function rt(n,e){var r=Xt.call(arguments,2);return me(function(){return n.apply(t,r)},e)}function ot(n){var e=Xt.call(arguments,1);return me(function(){return n.apply(t,e)},1)}function it(n,t){return a(t,n,Xt.call(arguments,2))}function at(n,t){var e={};return function(){var r=t?t.apply(this,arguments):arguments[0];return Vt.call(e,r)?e[r]:e[r]=n.apply(this,arguments)}}function st(n){var t,e=!1;return function(){return e?t:(e=!0,t=n.apply(this,arguments),n=null,t)}}function ut(n){return a(n,Xt.call(arguments,1))}function ct(n,t){function e(){s=new Date,a=null,o=n.apply(i,r)}var r,o,i,a,s=0;return function(){var u=new Date,c=t-(u-s);return r=arguments,i=this,0>=c?(s=u,o=n.apply(i,r)):a||(a=me(e,c)),o}}function lt(n,t){return function(){var e=[n];return arguments.length&&Gt.apply(e,arguments),t.apply(this,e)}}function ft(n){return null==n?"":(n+"").replace(Mt,l)}function pt(n){return n}function ht(n){pr(Ze(n),function(t){var e=r[t]=n[t];r.prototype[t]=function(){var n=[this.__wrapped__];arguments.length&&Gt.apply(n,arguments);var t=e.apply(r,n);return this.__chain__&&(t=new r(t),t.__chain__=!0),t}})}function gt(){return n._=Nt,this}function dt(n,t){return null==n&&null==t&&(t=1),n=+n||0,null==t&&(t=n,n=0),n+ne(se()*((+t||0)-n+1))}function vt(n,t){var e=n?n[t]:null;return g(e)?n[t]():e}function yt(n,t,e){n+="",e||(e={});var o,i,a=0,s=r.templateSettings,u="__p += '",l=e.variable||s.variable,f=l,p=RegExp((e.escape||s.escape||$t).source+"|"+(e.interpolate||s.interpolate||$t).source+"|"+(e.evaluate||s.evaluate||$t).source+"|$","g");if(n.replace(p,function(t,e,r,i,s){u+=n.slice(a,s).replace(Ut,c),u+=e?"' +\n__e("+e+") +\n'":i?"';\n"+i+";\n__p += '":r?"' +\n((__t = ("+r+")) == null ? '' : __t) +\n'":"",o||(o=i||Dt.test(e||r)),a=s+t.length}),u+="';\n",!f)if(l="obj",o)u="with ("+l+") {\n"+u+"\n}\n";else{var h=RegExp("(\\(\\s*)"+l+"\\."+l+"\\b","g");u=u.replace(qt,"$&"+l+".").replace(h,"$1__d")}u=(o?u.replace(Pt,""):u).replace(Ft,"$1").replace(Ht,"$1;"),u="function("+l+") {\n"+(f?"":l+" || ("+l+" = {});\n")+"var __t, __p = '', __e = _.escape"+(o?", __j = Array.prototype.join;\nfunction print() { __p += __j.call(arguments, '') }\n":(f?"":", __d = "+l+"."+l+" || "+l)+";\n")+u+"return __p\n}";var g=Se?"\n//@ sourceURL=/lodash/template/source["+Wt++ +"]":"";try{i=Function("_","return "+u+g)(r)}catch(d){throw d.source=u,d}return t?i(t):(i.source=u,i)}function mt(n,t,e){n=+n||0;for(var r=-1,o=Array(n);n>++r;)o[r]=t.call(e,r);return o}function _t(n){return null==n?"":(n+"").replace(Ct,p)}function bt(n){var t=zt++;return n?n+t:t}function xt(n){return n=new r(n),n.__chain__=!0,n}function wt(n,t){return t(n),n}function jt(){return this.__chain__=!0,this}function Et(){return this.__wrapped__}var kt="object"==typeof exports&&exports&&("object"==typeof global&&global&&global==global.global&&(n=global),exports),Bt=Array.prototype,At=(Boolean.prototype,Object.prototype);Number.prototype,String.prototype;var St,Lt,It,zt=0,Ot=30,Nt=n._,Dt=/[-?+=!~*%&^<>|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/,Ct=/&(?:amp|lt|gt|quot|#x27);/g,Pt=/\b__p \+= '';/g,Ft=/\b(__p \+=) '' \+/g,Ht=/(__e\(.*?\)|\b__t\)) \+\n'';/g,Tt=/\w*$/,qt=/(?:__e|__t = )\(\s*(?![\d\s"']|this\.)/g,Rt=RegExp("^"+(At.valueOf+"").replace(/[.*+?^=!:${}()|[\]\/\\]/g,"\\$&").replace(/valueOf|for [^\]]+/g,".+?")+"$"),$t=/($^)/,Mt=/[&<>"']/g,Ut=/['\n\r\t\u2028\u2029\\]/g,Kt=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],Wt=0,Qt=Bt.concat,Vt=At.hasOwnProperty,Gt=Bt.push,Jt=At.propertyIsEnumerable,Xt=Bt.slice,Yt=At.toString,Zt=Rt.test(Zt=Xt.bind)&&Zt,ne=Math.floor,te=Rt.test(te=Object.getPrototypeOf)&&te,ee=Rt.test(ee=Array.isArray)&&ee,re=n.isFinite,oe=Rt.test(oe=Object.keys)&&oe,ie=Math.max,ae=Math.min,se=Math.random,ue="[object Arguments]",ce="[object Array]",le="[object Boolean]",fe="[object Date]",pe="[object Function]",he="[object Number]",ge="[object Object]",de="[object RegExp]",ve="[object String]",ye=n.clearTimeout,me=n.setTimeout,_e=!0;(function(){function n(){this.x=1}var t={0:1,length:1},e=[];n.prototype={valueOf:1,y:1};for(var r in new n)e.push(r);for(r in arguments)_e=!r;St=4>(e+"").length,It="x"!=e[0],e.splice.call(t,0,1),Lt=t[0]})(1);var be=!h(arguments),xe="x"!=Xt.call("x")[0],we="xx"!="x"[0]+Object("x")[0];try{var je=Yt.call(n.document||0)==ge}catch(Ee){}var ke=Zt&&/\n|Opera/.test(Zt+Yt.call(n.opera)),Be=oe&&/^.+$|true/.test(oe+!!n.attachEvent),Ae=!ke;try{var Se=(Function("//@")(),!n.attachEvent)}catch(Ee){}var Le={};Le[le]=Le[fe]=Le[pe]=Le[he]=Le[ge]=Le[de]=!1,Le[ue]=Le[ce]=Le[ve]=!0;var Ie={};Ie[ue]=Ie[pe]=!1,Ie[ce]=Ie[le]=Ie[fe]=Ie[he]=Ie[ge]=Ie[de]=Ie[ve]=!0;var ze={"boolean":!1,"function":!0,object:!0,number:!1,string:!1,undefined:!1,unknown:!0},Oe={"\\":"\\","'":"'","\n":"n","\r":"r"," ":"t","\u2028":"u2028","\u2029":"u2029"};r.templateSettings={escape:/<%-([\s\S]+?)%>/g,evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,variable:""};var Ne=yt("<% if (useStrict) { %>'use strict';\n<% } %>var index, value, iteratee = <%= firstArg %>, result<% if (init) { %> = <%= init %><% } %>;\nif (!<%= firstArg %>) return result;\n<%= top %>;\n<% if (arrayBranch) { %>var length = iteratee.length; index = -1; <% if (objectBranch) { %>\nif (length === +length) {<% } %> <% if (noCharByIndex) { %>\n if (toString.call(iteratee) == stringClass) {\n iteratee = iteratee.split('')\n } <% } %>\n <%= arrayBranch.beforeLoop %>;\n while (++index < length) {\n value = iteratee[index];\n <%= arrayBranch.inLoop %>\n } <% if (objectBranch) { %>\n}<% } %><% } %><% if (objectBranch) { %> <% if (arrayBranch) { %>\nelse { <% } else if (noArgsEnum) { %>\n var length = iteratee.length; index = -1;\n if (length && isArguments(iteratee)) {\n while (++index < length) {\n value = iteratee[index += ''];\n <%= objectBranch.inLoop %>\n }\n } else { <% } %> <% if (!hasDontEnumBug) { %>\n var skipProto = typeof iteratee == 'function' && \n propertyIsEnumerable.call(iteratee, 'prototype');\n <% } %> <% if (isKeysFast && useHas) { %>\n var ownIndex = -1,\n ownProps = objectTypes[typeof iteratee] ? nativeKeys(iteratee) : [],\n length = ownProps.length;\n\n <%= objectBranch.beforeLoop %>;\n while (++ownIndex < length) {\n index = ownProps[ownIndex];\n <% if (!hasDontEnumBug) { %>if (!(skipProto && index == 'prototype')) {\n <% } %> value = iteratee[index];\n <%= objectBranch.inLoop %>\n <% if (!hasDontEnumBug) { %>}\n<% } %> } <% } else { %>\n <%= objectBranch.beforeLoop %>;\n for (index in iteratee) {<% if (!hasDontEnumBug || useHas) { %>\n if (<% if (!hasDontEnumBug) { %>!(skipProto && index == 'prototype')<% } if (!hasDontEnumBug && useHas) { %> && <% } if (useHas) { %>hasOwnProperty.call(iteratee, index)<% } %>) { <% } %>\n value = iteratee[index];\n <%= objectBranch.inLoop %>; <% if (!hasDontEnumBug || useHas) { %>\n }<% } %>\n } <% } %> <% if (hasDontEnumBug) { %>\n\n var ctor = iteratee.constructor;\n <% for (var k = 0; k < 7; k++) { %>\n index = '<%= shadowed[k] %>';\n if (<% if (shadowed[k] == 'constructor') { %>!(ctor && ctor.prototype === iteratee) && <% } %>hasOwnProperty.call(iteratee, index)) {\n value = iteratee[index];\n <%= objectBranch.inLoop %>\n } <% } %> <% } %> <% if (arrayBranch || noArgsEnum) { %>\n}<% } %><% } %>\n<%= bottom %>;\nreturn result"),De={args:"collection, callback, thisArg",init:"collection",top:"callback = createCallback(callback, thisArg)",inLoop:"if (callback(value, index, collection) === false) return result"},Ce={init:"{}",top:"callback = createCallback(callback, thisArg)",inLoop:"var prop = callback(value, index, collection);\n(hasOwnProperty.call(result, prop) ? result[prop]++ : result[prop] = 1)"},Pe={init:"true",inLoop:"if (!callback(value, index, collection)) return !result"},Fe={useHas:!1,useStrict:!1,args:"object",init:"object",top:"for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {\n if (iteratee = arguments[argsIndex]) {",inLoop:"result[index] = value",bottom:" }\n}"},He={init:"[]",inLoop:"callback(value, index, collection) && result.push(value)"},Te={top:"callback = createCallback(callback, thisArg)"},qe={inLoop:{object:De.inLoop}},Re={init:!1,beforeLoop:{array:"result = Array(length)",object:"result = "+(Be?"Array(length)":"[]")},inLoop:{array:"result[index] = callback(value, index, collection)",object:"result"+(Be?"[ownIndex] = ":".push")+"(callback(value, index, collection))"}},$e={useHas:!1,args:"object, callback, thisArg",init:"{}",top:"var isFunc = typeof callback == 'function';\nif (isFunc) callback = createCallback(callback, thisArg);\nelse var props = concat.apply(ArrayProto, arguments)",inLoop:"if (isFunc\n ? !callback(value, index, object)\n : indexOf(props, index) < 0\n) result[index] = value"},Me=u({args:"object",init:"{}",inLoop:"result[value] = index"});be&&(h=function(n){return n?Vt.call(n,"callee"):!1});var Ue=ee||function(n){return Yt.call(n)==ce};g(/x/)&&(g=function(n){return Yt.call(n)==pe});var Ke=te?function(n){if(!n||"object"!=typeof n)return!1;var t=n.valueOf,e="function"==typeof t&&(e=te(t))&&te(e);return e?n==e||te(n)==e&&!h(n):d(n)}:d,We=u({args:"object",init:"[]",inLoop:"result.push(index)"}),Qe={"&":"&","<":"<",">":">",'"':""","'":"'"},Ve=Me(Qe),Ge=u(Fe,{inLoop:"if (result[index] == null) "+Fe.inLoop}),Je=u(Fe),Xe=u(De,Te,qe,{useHas:!1}),Ye=u(De,Te,qe),Ze=u({useHas:!1,args:"object",init:"[]",inLoop:"if (isFunction(value)) result.push(index)",bottom:"result.sort()"}),nr=u({args:"value",init:"true",top:"var className = toString.call(value),\n length = value.length;\nif (arrayLikeClasses[className]"+(be?" || isArguments(value)":"")+" ||\n"+" (className == objectClass && length === +length &&\n"+" isFunction(value.splice))"+") return !length",inLoop:{object:"return false"}}),tr=oe?function(n){var t=typeof n;return"function"==t&&Jt.call(n,"prototype")?We(n):n&&ze[t]?oe(n):[]}:We,er=u(Fe,{args:"object, source, indicator",top:"var isArr, args = arguments, argsIndex = 0;\nif (indicator == compareAscending) {\n var argsLength = 2, stackA = args[3], stackB = args[4]\n} else {\n var argsLength = args.length, stackA = [], stackB = []\n}\nwhile (++argsIndex < argsLength) {\n if (iteratee = args[argsIndex]) {",inLoop:"if ((source = value) && ((isArr = isArray(source)) || isPlainObject(source))) {\n var found = false, stackLength = stackA.length;\n while (stackLength--) {\n if (found = stackA[stackLength] == source) break\n }\n if (found) {\n result[index] = stackB[stackLength]\n } else {\n stackA.push(source);\n stackB.push(value = (value = result[index]) && isArr\n ? (isArray(value) ? value : [])\n : (isPlainObject(value) ? value : {})\n );\n result[index] = callee(value, source, compareAscending, stackA, stackB)\n }\n} else if (source != null) {\n result[index] = source\n}"}),rr=u($e),or=u({args:"object",init:"[]",inLoop:"result"+(Be?"[ownIndex] = ":".push")+"([index, value])"}),ir=u($e,{top:"if (typeof callback != 'function') {\n var prop,\n props = concat.apply(ArrayProto, arguments),\n length = props.length;\n for (index = 1; index < length; index++) {\n prop = props[index];\n if (prop in object) result[prop] = object[prop]\n }\n} else {\n callback = createCallback(callback, thisArg)",inLoop:"if (callback(value, index, object)) result[index] = value",bottom:"}"}),ar=u({args:"object",init:"[]",inLoop:"result.push(value)"}),sr=u({args:"collection, target",init:"false",noCharByIndex:!1,beforeLoop:{array:"if (toString.call(collection) == stringClass) return collection.indexOf(target) > -1"},inLoop:"if (value === target) return true"}),ur=u(De,Ce),cr=u(De,Pe),lr=u(De,He),fr=u(De,Te,{init:!1,inLoop:"if (callback(value, index, collection)) return value"}),pr=u(De,Te),hr=u(De,Ce,{inLoop:"var prop = callback(value, index, collection);\n(hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(value)"}),gr=u(Re,{args:"collection, methodName",top:"var args = slice.call(arguments, 2),\n isFunc = typeof methodName == 'function'",inLoop:{array:"result[index] = (isFunc ? methodName : value[methodName]).apply(value, args)",object:"result"+(Be?"[ownIndex] = ":".push")+"((isFunc ? methodName : value[methodName]).apply(value, args))"}}),dr=u(De,Re),vr=u(Re,{args:"collection, property",inLoop:{array:"result[index] = value[property]",object:"result"+(Be?"[ownIndex] = ":".push")+"(value[property])"}}),yr=u({args:"collection, callback, accumulator, thisArg",init:"accumulator",top:"var noaccum = arguments.length < 3;\ncallback = createCallback(callback, thisArg)",beforeLoop:{array:"if (noaccum) result = iteratee[++index]"},inLoop:{array:"result = callback(result, value, index, collection)",object:"result = noaccum\n ? (noaccum = false, value)\n : callback(result, value, index, collection)"}}),mr=u(De,He,{inLoop:"!"+He.inLoop}),_r=u(De,Pe,{init:"false",inLoop:Pe.inLoop.replace("!","")}),br=u(De,Ce,Re,{inLoop:{array:"result[index] = {\n criteria: callback(value, index, collection),\n index: index,\n value: value\n}",object:"result"+(Be?"[ownIndex] = ":".push")+"({\n"+" criteria: callback(value, index, collection),\n"+" index: index,\n"+" value: value\n"+"})"},bottom:"result.sort(compareAscending);\nlength = result.length;\nwhile (length--) {\n result[length] = result[length].value\n}"}),xr=u(He,{args:"collection, properties",top:"var props = [];\nforIn(properties, function(value, prop) { props.push(prop) });\nvar propsLength = props.length",inLoop:"for (var prop, pass = true, propIndex = 0; propIndex < propsLength; propIndex++) {\n prop = props[propIndex];\n if (!(pass = value[prop] === properties[prop])) break\n}\npass && result.push(value)"}),wr=u({useHas:!1,useStrict:!1,args:"object",top:"var funcs = arguments,\n length = funcs.length;\nif (length > 1) {\n for (var index = 1; index < length; index++) {\n result[funcs[index]] = bind(result[funcs[index]], result)\n }\n return result\n}",inLoop:"if (isFunction(value)) {\n result[index] = bind(value, result)\n}"});r.VERSION="0.8.1",r.after=Z,r.bind=nt,r.bindAll=wr,r.chain=xt,r.clone=v,r.compact=N,r.compose=tt,r.contains=sr,r.countBy=ur,r.debounce=et,r.defaults=Ge,r.defer=ot,r.delay=rt,r.difference=D,r.escape=ft,r.every=cr,r.extend=Je,r.filter=lr,r.find=fr,r.first=C,r.flatten=P,r.forEach=pr,r.forIn=Xe,r.forOwn=Ye,r.functions=Ze,r.groupBy=hr,r.has=y,r.identity=pt,r.indexOf=F,r.initial=H,r.intersection=T,r.invert=Me,r.invoke=gr,r.isArguments=h,r.isArray=Ue,r.isBoolean=m,r.isDate=_,r.isElement=b,r.isEmpty=nr,r.isEqual=x,r.isFinite=w,r.isFunction=g,r.isNaN=E,r.isNull=k,r.isNumber=B,r.isObject=j,r.isPlainObject=Ke,r.isRegExp=A,r.isString=S,r.isUndefined=L,r.keys=tr,r.last=q,r.lastIndexOf=R,r.lateBind=it,r.map=dr,r.max=$,r.memoize=at,r.merge=er,r.min=M,r.mixin=ht,r.noConflict=gt,r.object=U,r.omit=rr,r.once=st,r.pairs=or,r.partial=ut,r.pick=ir,r.pluck=vr,r.random=dt,r.range=K,r.reduce=yr,r.reduceRight=I,r.reject=mr,r.rest=W,r.result=vt,r.shuffle=Q,r.size=z,r.some=_r,r.sortBy=br,r.sortedIndex=V,r.tap=wt,r.template=yt,r.throttle=ct,r.times=mt,r.toArray=O,r.unescape=_t,r.union=G,r.uniq=J,r.uniqueId=bt,r.values=ar,r.where=xr,r.without=X,r.wrap=lt,r.zip=Y,r.all=cr,r.any=_r,r.collect=dr,r.detect=fr,r.drop=W,r.each=pr,r.foldl=yr,r.foldr=I,r.head=C,r.include=sr,r.inject=yr,r.methods=Ze,r.select=lr,r.tail=W,r.take=C,r.unique=J,r._iteratorTemplate=Ne,r._shimKeys=We,ht(r),r.prototype.chain=jt,r.prototype.value=Et,pr(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=Bt[n];r.prototype[n]=function(){var n=this.__wrapped__;return t.apply(n,arguments),Lt&&0===n.length&&delete n[0],this.__chain__&&(n=new r(n),n.__chain__=!0),n}}),pr(["concat","join","slice"],function(n){var t=Bt[n];r.prototype[n]=function(){var n=this.__wrapped__,e=t.apply(n,arguments);return this.__chain__&&(e=new r(e),e.__chain__=!0),e}}),"function"==typeof e&&"object"==typeof e.amd&&e.amd?(n._=r,e("lodash",[],function(){return r})):kt?"object"==typeof module&&module&&module.exports==kt?(module.exports=r)._=r:kt._=r:n._=r}(this),e("src/path",["require"],function(){function n(n,t){for(var e=0,r=n.length-1;r>=0;r--){var o=n[r];"."===o?n.splice(r,1):".."===o?(n.splice(r,1),e++):e&&(n.splice(r,1),e--)}if(t)for(;e--;e)n.unshift("..");return n}function t(t){var e="/"===t.charAt(0);return t=n(t.split("/").filter(function(n){return!!n}),!e).join("/"),t||e||(t="."),(e?"/":"")+t}function e(n){var t=a(n),e=t[0],r=t[1];return e||r?(r&&(r=r.substr(0,r.length-1)),e+r):"."}function r(n,t){var e=a(n)[2];return t&&e.substr(-1*t.length)===t&&(e=e.substr(0,e.length-t.length)),""===e?"/":e}function o(n){return"/"===n.charAt(0)}var i=/^(\/?)([\s\S]+\/(?!$)|\/)?((?:\.{1,2}$|[\s\S]+?)?(\.[^.\/]*)?)$/,a=function(n){var t=i.exec(n);return[t[1]||"",t[2]||"",t[3]||"",t[4]||""]};return{normalize:t,dirname:e,basename:r,isAbsolute:o}}),"function"!=typeof e)var e=t("amdefine")(module);e("src/guid",["require"],function(){function n(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(n){var t=0|16*Math.random(),e="x"==n?t:8|3&t;return e.toString(16)}).toUpperCase()}return n}),e("src/error",["require"],function(){function n(){Error.apply(this,arguments)}function t(){Error.apply(this,arguments)}function e(){Error.apply(this,arguments)}function r(){Error.apply(this,arguments)}function o(){Error.apply(this,arguments)}function i(){Error.apply(this,arguments)}function a(){Error.apply(this,arguments)}function s(){Error.apply(this,arguments)}function u(){Error.apply(this,arguments)}function c(){Error.apply(this,arguments)}return n.prototype=Error(),n.prototype.name="EPathExists",n.prototype.constructor=n,t.prototype=Error(),t.prototype.name="EIsDirectory",t.prototype.constructor=t,e.prototype=Error(),e.prototype.name="ENoEntry",e.prototype.constructor=e,r.prototype=Error(),r.prototype.name="EBusy",r.prototype.constructor=r,o.prototype=Error(),o.prototype.name="ENotEmpty",o.prototype.constructor=o,i.prototype=Error(),i.prototype.name="NotADirectoryError",i.prototype.constructor=i,a.prototype=Error(),a.prototype.name="EBadFileDescriptor",a.prototype.constructor=a,s.prototype=Error(),s.prototype.name="ENotImplemented",s.prototype.constructor=s,u.prototype=Error(),u.prototype.name="ENotMounted",u.prototype.constructor=u,c.prototype=Error(),c.prototype.name="EFileExists",c.prototype.constructor=c,{EPathExists:n,EIsDirectory:t,ENoEntry:e,EBusy:r,ENotEmpty:o,ENotDirectory:i,EBadFileDescriptor:a,ENotImplemented:s,ENotMounted:u,EFileExists:c}});var r=r||function(n,t){var e={},r=e.lib={},o=r.Base=function(){function n(){}return{extend:function(t){n.prototype=this;var e=new n;return t&&e.mixIn(t),e.$super=this,e},create:function(){var n=this.extend();return n.init.apply(n,arguments),n},init:function(){},mixIn:function(n){for(var t in n)n.hasOwnProperty(t)&&(this[t]=n[t]);n.hasOwnProperty("toString")&&(this.toString=n.toString)},clone:function(){return this.$super.extend(this)}}}(),i=r.WordArray=o.extend({init:function(n,e){n=this.words=n||[],this.sigBytes=e!=t?e:4*n.length -},toString:function(n){return(n||s).stringify(this)},concat:function(n){var t=this.words,e=n.words,r=this.sigBytes,n=n.sigBytes;if(this.clamp(),r%4)for(var o=0;n>o;o++)t[r+o>>>2]|=(255&e[o>>>2]>>>24-8*(o%4))<<24-8*((r+o)%4);else if(e.length>65535)for(o=0;n>o;o+=4)t[r+o>>>2]=e[o>>>2];else t.push.apply(t,e);return this.sigBytes+=n,this},clamp:function(){var t=this.words,e=this.sigBytes;t[e>>>2]&=4294967295<<32-8*(e%4),t.length=n.ceil(e/4)},clone:function(){var n=o.clone.call(this);return n.words=this.words.slice(0),n},random:function(t){for(var e=[],r=0;t>r;r+=4)e.push(0|4294967296*n.random());return i.create(e,t)}}),a=e.enc={},s=a.Hex={stringify:function(n){for(var t=n.words,n=n.sigBytes,e=[],r=0;n>r;r++){var o=255&t[r>>>2]>>>24-8*(r%4);e.push((o>>>4).toString(16)),e.push((15&o).toString(16))}return e.join("")},parse:function(n){for(var t=n.length,e=[],r=0;t>r;r+=2)e[r>>>3]|=parseInt(n.substr(r,2),16)<<24-4*(r%8);return i.create(e,t/2)}},u=a.Latin1={stringify:function(n){for(var t=n.words,n=n.sigBytes,e=[],r=0;n>r;r++)e.push(String.fromCharCode(255&t[r>>>2]>>>24-8*(r%4)));return e.join("")},parse:function(n){for(var t=n.length,e=[],r=0;t>r;r++)e[r>>>2]|=(255&n.charCodeAt(r))<<24-8*(r%4);return i.create(e,t)}},c=a.Utf8={stringify:function(n){try{return decodeURIComponent(escape(u.stringify(n)))}catch(t){throw Error("Malformed UTF-8 data")}},parse:function(n){return u.parse(unescape(encodeURIComponent(n)))}},l=r.BufferedBlockAlgorithm=o.extend({reset:function(){this._data=i.create(),this._nDataBytes=0},_append:function(n){"string"==typeof n&&(n=c.parse(n)),this._data.concat(n),this._nDataBytes+=n.sigBytes},_process:function(t){var e=this._data,r=e.words,o=e.sigBytes,a=this.blockSize,s=o/(4*a),s=t?n.ceil(s):n.max((0|s)-this._minBufferSize,0),t=s*a,o=n.min(4*t,o);if(t){for(var u=0;t>u;u+=a)this._doProcessBlock(r,u);u=r.splice(0,t),e.sigBytes-=o}return i.create(u,o)},clone:function(){var n=o.clone.call(this);return n._data=this._data.clone(),n},_minBufferSize:0});r.Hasher=l.extend({init:function(){this.reset()},reset:function(){l.reset.call(this),this._doReset()},update:function(n){return this._append(n),this._process(),this},finalize:function(n){return n&&this._append(n),this._doFinalize(),this._hash},clone:function(){var n=l.clone.call(this);return n._hash=this._hash.clone(),n},blockSize:16,_createHelper:function(n){return function(t,e){return n.create(e).finalize(t)}},_createHmacHelper:function(n){return function(t,e){return f.HMAC.create(n,e).finalize(t)}}});var f=e.algo={};return e}(Math);(function(n){var t=r,e=t.lib,o=e.WordArray,e=e.Hasher,i=t.algo,a=[],s=[];(function(){function t(t){for(var e=n.sqrt(t),r=2;e>=r;r++)if(!(t%r))return!1;return!0}function e(n){return 0|4294967296*(n-(0|n))}for(var r=2,o=0;64>o;)t(r)&&(8>o&&(a[o]=e(n.pow(r,.5))),s[o]=e(n.pow(r,1/3)),o++),r++})();var u=[],i=i.SHA256=e.extend({_doReset:function(){this._hash=o.create(a.slice(0))},_doProcessBlock:function(n,t){for(var e=this._hash.words,r=e[0],o=e[1],i=e[2],a=e[3],c=e[4],l=e[5],f=e[6],p=e[7],h=0;64>h;h++){if(16>h)u[h]=0|n[t+h];else{var g=u[h-15],d=u[h-2];u[h]=((g<<25|g>>>7)^(g<<14|g>>>18)^g>>>3)+u[h-7]+((d<<15|d>>>17)^(d<<13|d>>>19)^d>>>10)+u[h-16]}g=p+((c<<26|c>>>6)^(c<<21|c>>>11)^(c<<7|c>>>25))+(c&l^~c&f)+s[h]+u[h],d=((r<<30|r>>>2)^(r<<19|r>>>13)^(r<<10|r>>>22))+(r&o^r&i^o&i),p=f,f=l,l=c,c=0|a+g,a=i,i=o,o=r,r=0|g+d}e[0]=0|e[0]+r,e[1]=0|e[1]+o,e[2]=0|e[2]+i,e[3]=0|e[3]+a,e[4]=0|e[4]+c,e[5]=0|e[5]+l,e[6]=0|e[6]+f,e[7]=0|e[7]+p},_doFinalize:function(){var n=this._data,t=n.words,e=8*this._nDataBytes,r=8*n.sigBytes;t[r>>>5]|=128<<24-r%32,t[(r+64>>>9<<4)+15]=e,n.sigBytes=4*t.length,this._process()}});t.SHA256=e._createHelper(i),t.HmacSHA256=e._createHmacHelper(i)})(Math),e("crypto-js/rollups/sha256",function(){}),e("src/fs",["require","when","lodash","src/path","src/guid","src/error","crypto-js/rollups/sha256"],function(n){function t(n){var t;if(void 0===n)return[];if("string"==typeof n){for(n=n.split(","),t=0,l=n.length;l>t;++t)n[t]=n[t].trim().toUpperCase();return n}}function e(n){"function"==typeof n&&n.apply(void 0,Array.prototype.slice.call(arguments,1))}function o(n){return b.SHA256(n).toString(b.enc.hex)}function i(n,t,e,r,o,i,a,s,u,c,l){var f=Date.now();return{size:r||0,atime:i||f,ctime:a||f,mtime:s||f,mode:n||k,flags:u||"",xattrs:c||{},data:t||b.SHA256(m()).toString(b.enc.hex),type:e||L,links:l||0,version:o||0,id:b.SHA256(m()).toString(b.enc.hex)}}function a(n,t,e,r,o,i){return{size:n,handle:t,atime:e,ctime:r,mtime:o,links:i}}function s(n,t,e,r){var o=n.transaction(e,r),i=d.defer();return o.oncomplete=i.resolve,o.then=i.then,t(o),o}function u(n){this._db=n,this._pending=0,this._mounted=!0,this._descriptors={},this._deferred=d.defer(),this._deferred.resolve(),this.Context=c.bind(void 0,this),this.Transaction=s.bind(void 0,n,this._request)}function c(n,t){this._fs=n,this._cwd=t||"/"}function f(n,t,e,r,o,i){this._fs=n,this._pending=0,this._valid=!0,this._position=0,this._flags=r,this._mode=o,this._size=i,this._handle=t,this._file=e,this._deferred=d.defer(),this._deferred.resolve();var a=this;this.Transaction=s.bind(void 0,n._db,function(t){n._request(t),a._request(t)})}function p(n,t){this._openfile=n,this._flags=t}function h(n,r,o){r=t(r);var i=v(r).contains(P),a=x.open(n);a.onupgradeneeded=function(n){var t=n.target.result;t.objectStoreNames.contains(w)&&t.deleteObjectStore(w),t.createObjectStore(w),i=!0},a.onsuccess=function(n){var t=n.target.result,r=new u(t),a=new r.Context;if(i){var s=t.transaction([w],E),c=s.objectStore(w),l=c.clear();l.onsuccess=function(){r.mkdir("/",function(n){n?e(o,n):e(o,null,a)},s)},l.onerror=function(n){console.log(n)}}else e(o,null,a)},a.onerror=function(n){console.log(n)}}function g(n,t){n._db.close(),n._deferred.then(t)}var d=n("when"),v=n("lodash"),y=n("src/path"),m=n("src/guid"),_=n("src/error");n("crypto-js/rollups/sha256");var b=r,x=window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB,w="files",j="readonly",E="readwrite",k="application/file",B="application/directory",A="application/octet-stream",S="application/json",L=A;u.prototype._request=function(n){var t=this;0===t._pending&&(t._deferred=d.defer()),++t._pending,n.then(function(){--t._pending,0===t._pending&&t._deferred.resolve()})};var I="CREATE",z="RO",O="RW";u.prototype.open=function(n,r,a,s,u){function c(n,t,r,o){var i=new f(l,n,t,r,o),a=new p(i);l._descriptors[a]=i,e(s,null,a)}var l=this;n=y.normalize(n);var h=u||new l.Transaction([w],E),g=h.objectStore(w);r=t(r),a=a.toUpperCase();var d=y.basename(n),b=y.dirname(n),x=o(b),j=g.get(x);j.onsuccess=function(t){var u,l,f=t.target.result,p=f.data;if(d===n)u=f,l=x,O===a&&B===u.mode&&e(s,new _.EIsDirectory),c(l,u,r,a);else if(v(p).has(d)){l=p[d];var h=g.get(l);h.onsuccess=function(n){u=n.target.result,O===a&&B===u.mode&&e(s,new _.EIsDirectory),c(l,u,r,a)},h.onerror=function(n){e(s,n)}}else if(v(r).contains(I)){l=p[d]=o(m()),++f.size,++f.version,u=new i,++u.links;var y=g.put(u,p[d]);y.onsuccess=function(){var n=g.put(f,x);n.onsuccess=function(){c(l,u,r,a)},n.onerror=function(n){e(s,n)}},y.onerror=function(n){e(s,n)}}},j.onerror=function(n){e(s,n)}},u.prototype.close=function(n,t){var e=this,r=e._descriptors[n];r.valid=!1,r._deferred.then(t)},u.prototype.mkdir=function(n,t,r){var a=this;n=y.normalize(n);var s=r||new a.Transaction([w],E),u=s.objectStore(w),c=o(n),l=y.dirname(n),f=o(l),p=u.get(c);p.onsuccess=function(r){var o=r.target.result;if(o)e(t,new _.EPathExists);else{o=new i(B,{".":c,"..":f},S,2),++o.links;var a=u.put(o,c);a.onsuccess=function(){if(c!==f){var r=u.get(f);r.onsuccess=function(r){var o=r.target.result;o.data[y.basename(n)]=c,++o.size,++o.version;var i=u.put(o,f);i.onsuccess=function(){e(t)},i.onerror=function(n){e(t,n)}},r.onerror=function(n){e(t,n)}}else e(t)},a.onerror=function(n){e(t,n)}}},p.onerror=function(n){e(t,n)}},u.prototype.rmdir=function(n,t,r){var i=this;n=y.normalize(n);var a=r||new i.Transaction([w],E),s=a.objectStore(w),u=o(n),c=s.get(u);c.onsuccess=function(r){var i=r.target.result;if(i){var a=i.data;if(Object.keys(a).length>2)e(t,new _.ENotEmpty);else{var c=s.delete(u);c.onsuccess=function(){var r=y.dirname(n),i=o(r),a=s.get(i);a.onsuccess=function(n){var r=n.target.result;delete r.data[u],--r.size,++r.version;var o=s.put(r,i);o.onsuccess=function(){e(t)},o.onerror=function(n){e(t,n)}},a.onerror=function(n){e(t,n)}},c.onerror=function(n){e(t,n)}}}else e(t,new _.ENoEntry)},c.onerror=function(n){e(t,n)}},u.prototype.stat=function(n,t,r){var i=this;n=y.normalize(n);var s,u,c=r||new i.Transaction([w],j),l=c.objectStore(w),f=y.dirname(n),p=o(f),h=l.get(p);h.onsuccess=function(r){var o=r.target.result,i=o.data,c=y.basename(n);if(c===n)u=o,f=p,s=new a(u.size,f,u.atime,u.ctime,u.mtime,u.links),e(t,null,s);else if(v(i).has(c)){var f=i[c],h=l.get(f);h.onsuccess=function(n){var r=n.target.result;s=new a(r.size,f,r.atime,r.ctime,r.mtime,r.links),e(t,null,s)},h.onerror=function(n){e(t,n)}}else e(t,new _.ENoEntry)},h.onerror=function(n){e(t,n)}},u.prototype.link=function(n,t,r,i){var a=this;n=y.normalize(n),t=y.normalize(t);var s=y.dirname(n),u=y.dirname(t),c=i||new a.Transaction([w],E),l=c.objectStore(w),f=o(s),p=o(u),h=l.get(f);h.onsuccess=function(o){var i=o.target.result;if(i){var a=i.data,s=a[y.basename(n)];if(s){var u=l.get(p);u.onsuccess=function(n){var o=n.target.result;if(o){var i=l.get(s);i.onsuccess=function(n){var i=n.target.result;++i.links,++i.version;var a=l.put(i,s);a.onsuccess=function(){var n=o.data,i=y.basename(t);if(v(n).has(i))e(r,new _.EPathExists);else{n[i]=s,++o.size,++o.version;var a=l.put(o,p);a.onsuccess=function(){e(r)},a.onerror=function(n){e(r,n)}}},a.onerror=function(n){e(r,n)}},i.onerror=function(n){e(r,n)}}else e(r,new _.ENoEntry)},u.onerror=function(n){e(r,n)}}else e(r,new _.ENoEntry)}else e(r,new _.ENoEntry)},h.onerror=function(n){e(r,n)}},u.prototype.unlink=function(n,t,r){var i=this;n=y.normalize(n);var a=r||new i.Transaction([w],E),s=a.objectStore(w),u=y.dirname(n),c=o(u),l=s.get(c);l.onsuccess=function(r){var o=r.target.result,i=o.data,a=y.basename(n);if(v(i).has(a)){var u=i[a];delete i[a],--o.size,++o.version;var l=s.put(o,c);l.onsuccess=function(){var n=s.get(u);n.onsuccess=function(n){function r(){e(t)}var o=n.target.result;if(--o.links,0===o.links){var i=s.delete(u);i.onsuccess=r,i.onerror=function(n){e(t,n)}}else{++o.version;var a=s.put(o,u);a.onsuccess=r,a.onerror=function(n){e(t,n)}}},n.onerror=function(n){e(t,n)}},l.onerror=function(n){e(t,n)}}else e(t,new _.ENoEntry)},l.onerror=function(n){e(t,n)}},u.prototype.setxattr=function(){},u.prototype.getxattr=function(){},c.prototype.chdir=function(n){this._cwd=y.isAbsolute(n)?y.normalize(n):y.normalize(this._cwd+"/"+n)},c.prototype.getcwd=function(){return this._cwd},c.prototype.open=function(n,t,e,r){this._fs.open(y.normalize(this._cwd+"/"+n),t,e,r)},c.prototype.close=function(n,t){this._fs.close(n,t)},c.prototype.mkdir=function(n,t){this._fs.mkdir(y.normalize(this._cwd+"/"+n),t)},c.prototype.rmdir=function(n,t){this._fs.rmdir(y.normalize(this._cwd+"/"+n),t)},c.prototype.stat=function(n,t){this._fs.stat(y.normalize(this._cwd+"/"+n),t)},c.prototype.link=function(n,t,e){this._fs.link(y.normalize(this._cwd+"/"+n),y.normalize(this._cwd+"/"+t),e)},c.prototype.unlink=function(n,t){this._fs.unlink(y.normalize(this._cwd+"/"+n),t)},c.prototype.setxattr=function(n,t,e,r){this._fs.setxattr(y.normalize(this._cwd+"/"+n),t,e,r)},c.prototype.getxattr=function(n,t,e){this._fs.getxattr(y.normalize(this._cwd+"/"+n),t,e)},f.prototype._request=function(n){var t=this;0===t._pending&&(t._deferred=d.defer()),++t._pending,n.then(function(){--t._pending,0===t._pending&&t._deferred.resolve()})},f.prototype.seek=function(n,t,r,o){var i=this;i._fs,t=t.toUpperCase();var a=o||new i.Transaction([w],j),s=a.objectStore(w);if(C===t){var u=s.get(fd._handle);u.onsuccess=function(t){var o=t.target.result,a=o.size;n+=a,i._position=n,e(r,null,n)},u.onerror=function(n){e(r,n)}}else D===t?(i._position+=n,e(r,null,i._position)):N===t&&(i._position=n,e(r,null,n))},f.prototype.read=function(n,t,r){var o=this;if(o._fs,B===o._file.mode)return e(t,new _.EIsDir);var i=r||new o.Transaction([w],j),a=i.objectStore(w),s=a.get(o._file.data);s.onsuccess=function(r){var i=r.target.result;if(i){var a=o._position+n.length>i.length?i.length-o._position:n.length,s=i.subarray(o._position,o._position+a);n.set(s),o._position+=a,e(t,null,a,n)}else e(t,null,0,n)},s.onerror=function(n){e(t,n)}},f.prototype.readdir=function(n,t,r,o){var i=this;if(i._fs,!B===i._file.mode)return e(r,new _.ENotIsDir);var a=o||new i.Transaction([w],j),s=a.objectStore(w),u=s.get(i._handle);u.onsuccess=function(o){var a=o.target.result,s=v(a.data).keys().sort();t=t||a.size,t=i._position+t>a.size?a.size-i._position:t;for(var u=i._position,c=i._position+t;c>u;++u)n.push(s[u]);i._position+=t,e(r,null,t,n)},u.onerror=function(n){e(r,n)}},f.prototype.write=function(n,t,r){var o=this;if(o._fs,z===o._mode)return e(t,new _.EBadFileDescriptor),void 0;var i=r||o.Transaction([w],E),a=i.objectStore(w),s=a.get(o._file.data);s.onsuccess=function(r){var i=r.target.result,s=n.length,u=i?i.length:0,c=u>o._position+s?u:o._position+s,l=new Uint8Array(c);i&&l.set(i),l.set(n,o._position),o._position+=s;var f=a.put(l,o._file.data);f.onsuccess=function(){var n=a.get(o._handle);n.onsuccess=function(n){var r=n.target.result;r.size=c,r.mtime=Date.now();var i=a.put(r,o._handle);i.onsuccess=function(){e(t,null,c)},i.onerror=function(n){e(t,n)}},n.onerror=function(n){e(t,n)}},f.onerror=function(n){e(t,n)}},s.onerror=function(n){e(t,n)}};var N="SET",D="CURRENT",C="END";p.prototype.seek=function(n,t,e){this._openfile.seek(n,t,e)},p.prototype.read=function(n,t){this._openfile.read(n,t)},p.prototype.readdir=function(n,t,e){this._openfile.readdir(n,t,e)},p.prototype.write=function(n,t){this._openfile.write(n,t)};var P="FORMAT",F={mount:h,umount:g};return F});var o=t("src/fs");return o}); \ No newline at end of file diff --git a/examples/deferred-test.html b/examples/deferred-test.html new file mode 100644 index 0000000..c360959 --- /dev/null +++ b/examples/deferred-test.html @@ -0,0 +1,268 @@ + + + + + + +
+ + + + + \ No newline at end of file diff --git a/examples/test5.html b/examples/test5.html new file mode 100644 index 0000000..8298fff --- /dev/null +++ b/examples/test5.html @@ -0,0 +1,12 @@ +var fs = new FileSystem('packages'); + +var fd = fs.open('/myfile.txt', fs.RW); + +var buffer = new Uint8Array(16); + +fd.write(buffer).then(function(nbytes) { + fd.seek(-nbytes); + return fd.read(buffer); +}).then(function(nbytes) { + +}); \ No newline at end of file diff --git a/lib/when.js b/lib/when.js index 726d38e..8e9a582 100644 --- a/lib/when.js +++ b/lib/when.js @@ -1,738 +1,825 @@ -/** @license MIT License (c) copyright B Cavalier & J Hann */ +/** @license MIT License (c) copyright 2011-2013 original author or authors */ /** - * when * A lightweight CommonJS Promises/A and when() implementation - * * when is part of the cujo.js family of libraries (http://cujojs.com/) * * Licensed under the MIT License at: * http://www.opensource.org/licenses/mit-license.php * - * @version 1.0.4 + * @author Brian Cavalier + * @author John Hann + * @version 2.1.0 */ - -(function(define) { -define(function() { - var freeze, reduceArray, undef; - - /** - * No-Op function used in method replacement - * @private - */ - function noop() {} - - /** - * Allocate a new Array of size n - * @private - * @param n {number} size of new Array - * @returns {Array} - */ - function allocateArray(n) { - return new Array(n); - } - - /** - * Use freeze if it exists - * @function - * @private - */ - freeze = Object.freeze || function(o) { return o; }; - - // ES5 reduce implementation if native not available - // See: http://es5.github.com/#x15.4.4.21 as there are many - // specifics and edge cases. - reduceArray = [].reduce || - function(reduceFunc /*, initialValue */) { - // ES5 dictates that reduce.length === 1 - - // This implementation deviates from ES5 spec in the following ways: - // 1. It does not check if reduceFunc is a Callable - - var arr, args, reduced, len, i; - - i = 0; - arr = Object(this); - len = arr.length >>> 0; - args = arguments; - - // If no initialValue, use first item of array (we know length !== 0 here) - // and adjust i to start at second item - if(args.length <= 1) { - // Skip to the first real element in the array - for(;;) { - if(i in arr) { - reduced = arr[i++]; - break; - } - - // If we reached the end of the array without finding any real - // elements, it's a TypeError - if(++i >= len) { - throw new TypeError(); - } - } - } else { - // If initialValue provided, use it - reduced = args[1]; - } - - // Do the actual reduce - for(;i < len; ++i) { - // Skip holes - if(i in arr) - reduced = reduceFunc(reduced, arr[i], i, arr); - } - - return reduced; - }; - - /** - * Trusted Promise constructor. A Promise created from this constructor is - * a trusted when.js promise. Any other duck-typed promise is considered - * untrusted. - */ - function Promise() {} - - /** - * Create an already-resolved promise for the supplied value - * @private - * - * @param value anything - * @return {Promise} - */ - function resolved(value) { - - var p = new Promise(); - - p.then = function(callback) { - checkCallbacks(arguments); - - var nextValue; - try { - if(callback) nextValue = callback(value); - return promise(nextValue === undef ? value : nextValue); - } catch(e) { - return rejected(e); - } - }; - - return freeze(p); - } - - /** - * Create an already-rejected {@link Promise} with the supplied - * rejection reason. - * @private - * - * @param reason rejection reason - * @return {Promise} - */ - function rejected(reason) { - - var p = new Promise(); - - p.then = function(callback, errback) { - checkCallbacks(arguments); - - var nextValue; - try { - if(errback) { - nextValue = errback(reason); - return promise(nextValue === undef ? reason : nextValue) - } - - return rejected(reason); - - } catch(e) { - return rejected(e); - } - }; - - return freeze(p); - } - - /** - * Helper that checks arrayOfCallbacks to ensure that each element is either - * a function, or null or undefined. - * - * @param arrayOfCallbacks {Array} array to check - * @throws {Error} if any element of arrayOfCallbacks is something other than - * a Functions, null, or undefined. - */ - function checkCallbacks(arrayOfCallbacks) { - var arg, i = arrayOfCallbacks.length; - while(i) { - arg = arrayOfCallbacks[--i]; - if (arg != null && typeof arg != 'function') throw new Error('callback is not a function'); - } - } - - /** - * Creates a new, CommonJS compliant, Deferred with fully isolated - * resolver and promise parts, either or both of which may be given out - * safely to consumers. - * The Deferred itself has the full API: resolve, reject, progress, and - * then. The resolver has resolve, reject, and progress. The promise - * only has then. - * - * @memberOf when - * @function - * - * @returns {Deferred} - */ - function defer() { - var deferred, promise, listeners, progressHandlers, _then, _progress, complete; - - listeners = []; - progressHandlers = []; - - /** - * Pre-resolution then() that adds the supplied callback, errback, and progback - * functions to the registered listeners - * - * @private - * - * @param [callback] {Function} resolution handler - * @param [errback] {Function} rejection handler - * @param [progback] {Function} progress handler - * - * @throws {Error} if any argument is not null, undefined, or a Function - */ - _then = function unresolvedThen(callback, errback, progback) { - // Check parameters and fail immediately if any supplied parameter - // is not null/undefined and is also not a function. - // That is, any non-null/undefined parameter must be a function. - checkCallbacks(arguments); - - var deferred = defer(); - - listeners.push(function(promise) { - promise.then(callback, errback) - .then(deferred.resolve, deferred.reject, deferred.progress); - }); - - progback && progressHandlers.push(progback); - - return deferred.promise; - }; - - /** - * Registers a handler for this {@link Deferred}'s {@link Promise}. Even though all arguments - * are optional, each argument that *is* supplied must be null, undefined, or a Function. - * Any other value will cause an Error to be thrown. - * - * @memberOf Promise - * - * @param [callback] {Function} resolution handler - * @param [errback] {Function} rejection handler - * @param [progback] {Function} progress handler - * - * @throws {Error} if any argument is not null, undefined, or a Function - */ - function then(callback, errback, progback) { - return _then(callback, errback, progback); - } - - /** - * Resolves this {@link Deferred}'s {@link Promise} with val as the - * resolution value. - * - * @memberOf Resolver - * - * @param val anything - */ - function resolve(val) { - complete(resolved(val)); - } - - /** - * Rejects this {@link Deferred}'s {@link Promise} with err as the - * reason. - * - * @memberOf Resolver - * - * @param err anything - */ - function reject(err) { - complete(rejected(err)); - } - - /** - * @private - * @param update - */ - _progress = function(update) { - var progress, i = 0; - while (progress = progressHandlers[i++]) progress(update); - }; - - /** - * Emits a progress update to all progress observers registered with - * this {@link Deferred}'s {@link Promise} - * - * @memberOf Resolver - * - * @param update anything - */ - function progress(update) { - _progress(update); - } - - /** - * Transition from pre-resolution state to post-resolution state, notifying - * all listeners of the resolution or rejection - * - * @private - * - * @param completed {Promise} the completed value of this deferred - */ - complete = function(completed) { - var listener, i = 0; - - // Replace _then with one that directly notifies with the result. - _then = completed.then; - - // Replace complete so that this Deferred can only be completed - // once. Also Replace _progress, so that subsequent attempts to issue - // progress throw. - complete = _progress = function alreadyCompleted() { - // TODO: Consider silently returning here so that parties who - // have a reference to the resolver cannot tell that the promise - // has been resolved using try/catch - throw new Error("already completed"); - }; - - // Free progressHandlers array since we'll never issue progress events - // for this promise again now that it's completed - progressHandlers = undef; - - // Notify listeners - // Traverse all listeners registered directly with this Deferred - - while (listener = listeners[i++]) { - listener(completed); - } - - listeners = []; - }; - - /** - * The full Deferred object, with both {@link Promise} and {@link Resolver} - * parts - * @class Deferred - * @name Deferred - * @augments Resolver - * @augments Promise - */ - deferred = {}; - - // Promise and Resolver parts - // Freeze Promise and Resolver APIs - - /** - * The Promise API - * @namespace Promise - * @name Promise - */ - promise = new Promise(); - promise.then = deferred.then = then; - - /** - * The {@link Promise} for this {@link Deferred} - * @memberOf Deferred - * @name promise - * @type {Promise} - */ - deferred.promise = freeze(promise); - - /** - * The {@link Resolver} for this {@link Deferred} - * @namespace Resolver - * @name Resolver - * @memberOf Deferred - * @name resolver - * @type {Resolver} - */ - deferred.resolver = freeze({ - resolve: (deferred.resolve = resolve), - reject: (deferred.reject = reject), - progress: (deferred.progress = progress) - }); - - return deferred; - } - - /** - * Determines if promiseOrValue is a promise or not. Uses the feature - * test from http://wiki.commonjs.org/wiki/Promises/A to determine if - * promiseOrValue is a promise. - * - * @param promiseOrValue anything - * - * @returns {Boolean} true if promiseOrValue is a {@link Promise} - */ - function isPromise(promiseOrValue) { - return promiseOrValue && typeof promiseOrValue.then === 'function'; - } - - /** - * Register an observer for a promise or immediate value. - * - * @function - * @name when - * @namespace - * - * @param promiseOrValue anything - * @param {Function} [callback] callback to be called when promiseOrValue is - * successfully resolved. If promiseOrValue is an immediate value, callback - * will be invoked immediately. - * @param {Function} [errback] callback to be called when promiseOrValue is - * rejected. - * @param {Function} [progressHandler] callback to be called when progress updates - * are issued for promiseOrValue. - * - * @returns {Promise} a new {@link Promise} that will complete with the return - * value of callback or errback or the completion value of promiseOrValue if - * callback and/or errback is not supplied. - */ - function when(promiseOrValue, callback, errback, progressHandler) { - // Get a promise for the input promiseOrValue - // See promise() - var trustedPromise = promise(promiseOrValue); - - // Register promise handlers - return trustedPromise.then(callback, errback, progressHandler); - } - - /** - * Returns promiseOrValue if promiseOrValue is a {@link Promise}, a new Promise if - * promiseOrValue is a foreign promise, or a new, already-resolved {@link Promise} - * whose resolution value is promiseOrValue if promiseOrValue is an immediate value. - * - * Note that this function is not safe to export since it will return its - * input when promiseOrValue is a {@link Promise} - * - * @private - * - * @param promiseOrValue anything - * - * @returns Guaranteed to return a trusted Promise. If promiseOrValue is a when.js {@link Promise} - * returns promiseOrValue, otherwise, returns a new, already-resolved, when.js {@link Promise} - * whose resolution value is: - * * the resolution value of promiseOrValue if it's a foreign promise, or - * * promiseOrValue if it's a value - */ - function promise(promiseOrValue) { - var promise, deferred; - - if(promiseOrValue instanceof Promise) { - // It's a when.js promise, so we trust it - promise = promiseOrValue; - - } else { - // It's not a when.js promise. Check to see if it's a foreign promise - // or a value. - - deferred = defer(); - if(isPromise(promiseOrValue)) { - // It's a compliant promise, but we don't know where it came from, - // so we don't trust its implementation entirely. Introduce a trusted - // middleman when.js promise - - // IMPORTANT: This is the only place when.js should ever call .then() on - // an untrusted promise. - promiseOrValue.then(deferred.resolve, deferred.reject, deferred.progress); - promise = deferred.promise; - - } else { - // It's a value, not a promise. Create an already-resolved promise - // for it. - deferred.resolve(promiseOrValue); - promise = deferred.promise; - } - } - - return promise; - } - - /** - * Return a promise that will resolve when howMany of the supplied promisesOrValues - * have resolved. The resolution value of the returned promise will be an array of - * length howMany containing the resolutions values of the triggering promisesOrValues. - * - * @memberOf when - * - * @param promisesOrValues {Array} array of anything, may contain a mix - * of {@link Promise}s and values - * @param howMany - * @param [callback] - * @param [errback] - * @param [progressHandler] - * - * @returns {Promise} - */ - function some(promisesOrValues, howMany, callback, errback, progressHandler) { - var toResolve, results, ret, deferred, resolver, rejecter, handleProgress, len, i; - - len = promisesOrValues.length >>> 0; - - toResolve = Math.max(0, Math.min(howMany, len)); - results = []; - deferred = defer(); - ret = when(deferred, callback, errback, progressHandler); - - // Wrapper so that resolver can be replaced - function resolve(val) { - resolver(val); - } - - // Wrapper so that rejecter can be replaced - function reject(err) { - rejecter(err); - } - - // Wrapper so that progress can be replaced - function progress(update) { - handleProgress(update); - } - - function complete() { - resolver = rejecter = handleProgress = noop; - } - - // No items in the input, resolve immediately - if (!toResolve) { - deferred.resolve(results); - - } else { - // Resolver for promises. Captures the value and resolves - // the returned promise when toResolve reaches zero. - // Overwrites resolver var with a noop once promise has - // be resolved to cover case where n < promises.length - resolver = function(val) { - // This orders the values based on promise resolution order - // Another strategy would be to use the original position of - // the corresponding promise. - results.push(val); - - if (!--toResolve) { - complete(); - deferred.resolve(results); - } - }; - - // Rejecter for promises. Rejects returned promise - // immediately, and overwrites rejecter var with a noop - // once promise to cover case where n < promises.length. - // TODO: Consider rejecting only when N (or promises.length - N?) - // promises have been rejected instead of only one? - rejecter = function(err) { - complete(); - deferred.reject(err); - }; - - handleProgress = deferred.progress; - - // TODO: Replace while with forEach - for(i = 0; i < len; ++i) { - if(i in promisesOrValues) { - when(promisesOrValues[i], resolve, reject, progress); - } - } - } - - return ret; - } - - /** - * Return a promise that will resolve only once all the supplied promisesOrValues - * have resolved. The resolution value of the returned promise will be an array - * containing the resolution values of each of the promisesOrValues. - * - * @memberOf when - * - * @param promisesOrValues {Array} array of anything, may contain a mix - * of {@link Promise}s and values - * @param [callback] {Function} - * @param [errback] {Function} - * @param [progressHandler] {Function} - * - * @returns {Promise} - */ - function all(promisesOrValues, callback, errback, progressHandler) { - var results, promise; - - results = allocateArray(promisesOrValues.length); - promise = reduce(promisesOrValues, reduceIntoArray, results); - - return when(promise, callback, errback, progressHandler); - } - - function reduceIntoArray(current, val, i) { - current[i] = val; - return current; - } - - /** - * Return a promise that will resolve when any one of the supplied promisesOrValues - * has resolved. The resolution value of the returned promise will be the resolution - * value of the triggering promiseOrValue. - * - * @memberOf when - * - * @param promisesOrValues {Array} array of anything, may contain a mix - * of {@link Promise}s and values - * @param [callback] {Function} - * @param [errback] {Function} - * @param [progressHandler] {Function} - * - * @returns {Promise} - */ - function any(promisesOrValues, callback, errback, progressHandler) { - - function unwrapSingleResult(val) { - return callback(val[0]); - } - - return some(promisesOrValues, 1, unwrapSingleResult, errback, progressHandler); - } - - /** - * Traditional map function, similar to `Array.prototype.map()`, but allows - * input to contain {@link Promise}s and/or values, and mapFunc may return - * either a value or a {@link Promise} - * - * @memberOf when - * - * @param promisesOrValues {Array} array of anything, may contain a mix - * of {@link Promise}s and values - * @param mapFunc {Function} mapping function mapFunc(value) which may return - * either a {@link Promise} or value - * - * @returns {Promise} a {@link Promise} that will resolve to an array containing - * the mapped output values. - */ - function map(promisesOrValues, mapFunc) { - - var results, i; - - // Since we know the resulting length, we can preallocate the results - // array to avoid array expansions. - i = promisesOrValues.length; - results = allocateArray(i); - - // Since mapFunc may be async, get all invocations of it into flight - // asap, and then use reduce() to collect all the results - for(;i >= 0; --i) { - if(i in promisesOrValues) - results[i] = when(promisesOrValues[i], mapFunc); - } - - // Could use all() here, but that would result in another array - // being allocated, i.e. map() would end up allocating 2 arrays - // of size len instead of just 1. Since all() uses reduce() - // anyway, avoid the additional allocation by calling reduce - // directly. - return reduce(results, reduceIntoArray, results); - } - - /** - * Traditional reduce function, similar to `Array.prototype.reduce()`, but - * input may contain {@link Promise}s and/or values, but reduceFunc - * may return either a value or a {@link Promise}, *and* initialValue may - * be a {@link Promise} for the starting value. - * - * @memberOf when - * - * @param promisesOrValues {Array} array of anything, may contain a mix - * of {@link Promise}s and values - * @param reduceFunc {Function} reduce function reduce(currentValue, nextValue, index, total), - * where total is the total number of items being reduced, and will be the same - * in each call to reduceFunc. - * @param initialValue starting value, or a {@link Promise} for the starting value - * - * @returns {Promise} that will resolve to the final reduced value - */ - function reduce(promisesOrValues, reduceFunc, initialValue) { - - var total, args; - - total = promisesOrValues.length; - - // Skip promisesOrValues, since it will be used as 'this' in the call - // to the actual reduce engine below. - - // Wrap the supplied reduceFunc with one that handles promises and then - // delegates to the supplied. - - args = [ - function (current, val, i) { - return when(current, function (c) { - return when(val, function (value) { - return reduceFunc(c, value, i, total); - }); - }); - } - ]; - - if (arguments.length >= 3) args.push(initialValue); - - return promise(reduceArray.apply(promisesOrValues, args)); - } - - /** - * Ensure that resolution of promiseOrValue will complete resolver with the completion - * value of promiseOrValue, or instead with resolveValue if it is provided. - * - * @memberOf when - * - * @param promiseOrValue - * @param resolver {Resolver} - * @param [resolveValue] anything - * - * @returns {Promise} - */ - function chain(promiseOrValue, resolver, resolveValue) { - var useResolveValue = arguments.length > 2; - - return when(promiseOrValue, - function(val) { - if(useResolveValue) val = resolveValue; - resolver.resolve(val); - return val; - }, - function(e) { - resolver.reject(e); +(function(define, global) { 'use strict'; +define(function () { + + // Public API + + when.defer = defer; // Create a deferred + when.resolve = resolve; // Create a resolved promise + when.reject = reject; // Create a rejected promise + + when.join = join; // Join 2 or more promises + + when.all = all; // Resolve a list of promises + when.map = map; // Array.map() for promises + when.reduce = reduce; // Array.reduce() for promises + when.settle = settle; // Settle a list of promises + + when.any = any; // One-winner race + when.some = some; // Multi-winner race + + when.isPromise = isPromise; // Determine if a thing is a promise + + when.promise = promise; // EXPERIMENTAL: May change. Use at your own risk + + /** + * Register an observer for a promise or immediate value. + * + * @param {*} promiseOrValue + * @param {function?} [onFulfilled] callback to be called when promiseOrValue is + * successfully fulfilled. If promiseOrValue is an immediate value, callback + * will be invoked immediately. + * @param {function?} [onRejected] callback to be called when promiseOrValue is + * rejected. + * @param {function?} [onProgress] callback to be called when progress updates + * are issued for promiseOrValue. + * @returns {Promise} a new {@link Promise} that will complete with the return + * value of callback or errback or the completion value of promiseOrValue if + * callback and/or errback is not supplied. + */ + function when(promiseOrValue, onFulfilled, onRejected, onProgress) { + // Get a trusted promise for the input promiseOrValue, and then + // register promise handlers + return resolve(promiseOrValue).then(onFulfilled, onRejected, onProgress); + } + + /** + * Trusted Promise constructor. A Promise created from this constructor is + * a trusted when.js promise. Any other duck-typed promise is considered + * untrusted. + * @constructor + * @name Promise + */ + function Promise(then, inspect) { + this.then = then; + this.inspect = inspect; + } + + Promise.prototype = { + /** + * Register a rejection handler. Shortcut for .then(undefined, onRejected) + * @param {function?} onRejected + * @return {Promise} + */ + otherwise: function(onRejected) { + return this.then(undef, onRejected); + }, + + /** + * Ensures that onFulfilledOrRejected will be called regardless of whether + * this promise is fulfilled or rejected. onFulfilledOrRejected WILL NOT + * receive the promises' value or reason. Any returned value will be disregarded. + * onFulfilledOrRejected may throw or return a rejected promise to signal + * an additional error. + * @param {function} onFulfilledOrRejected handler to be called regardless of + * fulfillment or rejection + * @returns {Promise} + */ + ensure: function(onFulfilledOrRejected) { + return this.then(injectHandler, injectHandler).yield(this); + + function injectHandler() { + return resolve(onFulfilledOrRejected()); + } + }, + + /** + * Shortcut for .then(function() { return value; }) + * @param {*} value + * @return {Promise} a promise that: + * - is fulfilled if value is not a promise, or + * - if value is a promise, will fulfill with its value, or reject + * with its reason. + */ + 'yield': function(value) { + return this.then(function() { + return value; + }); + }, + + /** + * Assumes that this promise will fulfill with an array, and arranges + * for the onFulfilled to be called with the array as its argument list + * i.e. onFulfilled.apply(undefined, array). + * @param {function} onFulfilled function to receive spread arguments + * @return {Promise} + */ + spread: function(onFulfilled) { + return this.then(function(array) { + // array may contain promises, so resolve its contents. + return all(array, function(array) { + return onFulfilled.apply(undef, array); + }); + }); + }, + + /** + * Shortcut for .then(onFulfilledOrRejected, onFulfilledOrRejected) + * @deprecated + */ + always: function(onFulfilledOrRejected, onProgress) { + return this.then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress); + } + }; + + /** + * Returns a resolved promise. The returned promise will be + * - fulfilled with promiseOrValue if it is a value, or + * - if promiseOrValue is a promise + * - fulfilled with promiseOrValue's value after it is fulfilled + * - rejected with promiseOrValue's reason after it is rejected + * @param {*} value + * @return {Promise} + */ + function resolve(value) { + return promise(function(resolve) { + resolve(value); + }); + } + + /** + * Returns a rejected promise for the supplied promiseOrValue. The returned + * promise will be rejected with: + * - promiseOrValue, if it is a value, or + * - if promiseOrValue is a promise + * - promiseOrValue's value after it is fulfilled + * - promiseOrValue's reason after it is rejected + * @param {*} promiseOrValue the rejected value of the returned {@link Promise} + * @return {Promise} rejected {@link Promise} + */ + function reject(promiseOrValue) { + return when(promiseOrValue, rejected); + } + + /** + * Creates a new Deferred with fully isolated resolver and promise parts, + * either or both of which may be given out safely to consumers. + * The resolver has resolve, reject, and progress. The promise + * only has then. + * + * @return {{ + * promise: Promise, + * resolve: function:Promise, + * reject: function:Promise, + * notify: function:Promise + * resolver: { + * resolve: function:Promise, + * reject: function:Promise, + * notify: function:Promise + * }}} + */ + function defer() { + var deferred, pending, resolved; + + // Optimize object shape + deferred = { + promise: undef, resolve: undef, reject: undef, notify: undef, + resolver: { resolve: undef, reject: undef, notify: undef } + }; + + deferred.promise = pending = promise(makeDeferred); + + return deferred; + + function makeDeferred(resolvePending, rejectPending, notifyPending) { + deferred.resolve = deferred.resolver.resolve = function(value) { + if(resolved) { + return resolve(value); + } + resolved = true; + resolvePending(value); + return pending; + }; + + deferred.reject = deferred.resolver.reject = function(reason) { + if(resolved) { + return resolve(rejected(reason)); + } + resolved = true; + rejectPending(reason); + return pending; + }; + + deferred.notify = deferred.resolver.notify = function(update) { + notifyPending(update); + return update; + }; + } + } + + /** + * Creates a new promise whose fate is determined by resolver. + * @private (for now) + * @param {function} resolver function(resolve, reject, notify) + * @returns {Promise} promise whose fate is determine by resolver + */ + function promise(resolver) { + var value, handlers = []; + + // Call the provider resolver to seal the promise's fate + try { + resolver(promiseResolve, promiseReject, promiseNotify); + } catch(e) { + promiseReject(e); + } + + // Return the promise + return new Promise(then, inspect); + + /** + * Register handlers for this promise. + * @param [onFulfilled] {Function} fulfillment handler + * @param [onRejected] {Function} rejection handler + * @param [onProgress] {Function} progress handler + * @return {Promise} new Promise + */ + function then(onFulfilled, onRejected, onProgress) { + return promise(function(resolve, reject, notify) { + handlers + // Call handlers later, after resolution + ? handlers.push(function(value) { + value.then(onFulfilled, onRejected, onProgress) + .then(resolve, reject, notify); + }) + // Call handlers soon, but not in the current stack + : enqueue(function() { + value.then(onFulfilled, onRejected, onProgress) + .then(resolve, reject, notify); + }); + }); + } + + function inspect() { + return value ? value.inspect() : toPendingState(); + } + + /** + * Transition from pre-resolution state to post-resolution state, notifying + * all listeners of the ultimate fulfillment or rejection + * @param {*|Promise} val resolution value + */ + function promiseResolve(val) { + if(!handlers) { + return; + } + + value = coerce(val); + scheduleHandlers(handlers, value); + + handlers = undef; + } + + /** + * Reject this promise with the supplied reason, which will be used verbatim. + * @param {*} reason reason for the rejection + */ + function promiseReject(reason) { + promiseResolve(rejected(reason)); + } + + /** + * Issue a progress event, notifying all progress listeners + * @param {*} update progress event payload to pass to all listeners + */ + function promiseNotify(update) { + if(handlers) { + scheduleHandlers(handlers, progressing(update)); + } + } + } + + /** + * Coerces x to a trusted Promise + * + * @private + * @param {*} x thing to coerce + * @returns {Promise} Guaranteed to return a trusted Promise. If x + * is trusted, returns x, otherwise, returns a new, trusted, already-resolved + * Promise whose resolution value is: + * * the resolution value of x if it's a foreign promise, or + * * x if it's a value + */ + function coerce(x) { + if(x instanceof Promise) { + return x; + } + + if (!(x === Object(x) && 'then' in x)) { + return fulfilled(x); + } + + return promise(function(resolve, reject, notify) { + enqueue(function() { + try { + // We must check and assimilate in the same tick, but not the + // current tick, careful only to access promiseOrValue.then once. + var untrustedThen = x.then; + + if(typeof untrustedThen === 'function') { + fcall(untrustedThen, x, resolve, reject, notify); + } else { + // It's a value, create a fulfilled wrapper + resolve(fulfilled(x)); + } + + } catch(e) { + // Something went wrong, reject + reject(e); + } + }); + }); + } + + /** + * Create an already-fulfilled promise for the supplied value + * @private + * @param {*} value + * @return {Promise} fulfilled promise + */ + function fulfilled(value) { + var self = new Promise(function (onFulfilled) { + try { + return typeof onFulfilled == 'function' + ? coerce(onFulfilled(value)) : self; + } catch (e) { return rejected(e); - }, - resolver.progress - ); - } + } + }, function() { + return toFulfilledState(value); + }); - // - // Public API - // + return self; + } - when.defer = defer; + /** + * Create an already-rejected promise with the supplied rejection reason. + * @private + * @param {*} reason + * @return {Promise} rejected promise + */ + function rejected(reason) { + var self = new Promise(function (_, onRejected) { + try { + return typeof onRejected == 'function' + ? coerce(onRejected(reason)) : self; + } catch (e) { + return rejected(e); + } + }, function() { + return toRejectedState(reason); + }); - when.isPromise = isPromise; - when.some = some; - when.all = all; - when.any = any; + return self; + } - when.reduce = reduce; - when.map = map; + /** + * Create a progress promise with the supplied update. + * @private + * @param {*} update + * @return {Promise} progress promise + */ + function progressing(update) { + var self = new Promise(function (_, __, onProgress) { + try { + return typeof onProgress == 'function' + ? progressing(onProgress(update)) : self; + } catch (e) { + return progressing(e); + } + }); - when.chain = chain; + return self; + } - return when; + /** + * Schedule a task that will process a list of handlers + * in the next queue drain run. + * @private + * @param {Array} handlers queue of handlers to execute + * @param {*} value passed as the only arg to each handler + */ + function scheduleHandlers(handlers, value) { + enqueue(function() { + var handler, i = 0; + while (handler = handlers[i++]) { + handler(value); + } + }); + } + + /** + * Determines if promiseOrValue is a promise or not + * + * @param {*} promiseOrValue anything + * @returns {boolean} true if promiseOrValue is a {@link Promise} + */ + function isPromise(promiseOrValue) { + return promiseOrValue && typeof promiseOrValue.then === 'function'; + } + + /** + * Initiates a competitive race, returning a promise that will resolve when + * howMany of the supplied promisesOrValues have resolved, or will reject when + * it becomes impossible for howMany to resolve, for example, when + * (promisesOrValues.length - howMany) + 1 input promises reject. + * + * @param {Array} promisesOrValues array of anything, may contain a mix + * of promises and values + * @param howMany {number} number of promisesOrValues to resolve + * @param {function?} [onFulfilled] DEPRECATED, use returnedPromise.then() + * @param {function?} [onRejected] DEPRECATED, use returnedPromise.then() + * @param {function?} [onProgress] DEPRECATED, use returnedPromise.then() + * @returns {Promise} promise that will resolve to an array of howMany values that + * resolved first, or will reject with an array of + * (promisesOrValues.length - howMany) + 1 rejection reasons. + */ + function some(promisesOrValues, howMany, onFulfilled, onRejected, onProgress) { + + return when(promisesOrValues, function(promisesOrValues) { + + return promise(resolveSome).then(onFulfilled, onRejected, onProgress); + + function resolveSome(resolve, reject, notify) { + var toResolve, toReject, values, reasons, fulfillOne, rejectOne, len, i; + + len = promisesOrValues.length >>> 0; + + toResolve = Math.max(0, Math.min(howMany, len)); + values = []; + + toReject = (len - toResolve) + 1; + reasons = []; + + // No items in the input, resolve immediately + if (!toResolve) { + resolve(values); + + } else { + rejectOne = function(reason) { + reasons.push(reason); + if(!--toReject) { + fulfillOne = rejectOne = identity; + reject(reasons); + } + }; + + fulfillOne = function(val) { + // This orders the values based on promise resolution order + values.push(val); + if (!--toResolve) { + fulfillOne = rejectOne = identity; + resolve(values); + } + }; + + for(i = 0; i < len; ++i) { + if(i in promisesOrValues) { + when(promisesOrValues[i], fulfiller, rejecter, notify); + } + } + } + + function rejecter(reason) { + rejectOne(reason); + } + + function fulfiller(val) { + fulfillOne(val); + } + } + }); + } + + /** + * Initiates a competitive race, returning a promise that will resolve when + * any one of the supplied promisesOrValues has resolved or will reject when + * *all* promisesOrValues have rejected. + * + * @param {Array|Promise} promisesOrValues array of anything, may contain a mix + * of {@link Promise}s and values + * @param {function?} [onFulfilled] DEPRECATED, use returnedPromise.then() + * @param {function?} [onRejected] DEPRECATED, use returnedPromise.then() + * @param {function?} [onProgress] DEPRECATED, use returnedPromise.then() + * @returns {Promise} promise that will resolve to the value that resolved first, or + * will reject with an array of all rejected inputs. + */ + function any(promisesOrValues, onFulfilled, onRejected, onProgress) { + + function unwrapSingleResult(val) { + return onFulfilled ? onFulfilled(val[0]) : val[0]; + } + + return some(promisesOrValues, 1, unwrapSingleResult, onRejected, onProgress); + } + + /** + * Return a promise that will resolve only once all the supplied promisesOrValues + * have resolved. The resolution value of the returned promise will be an array + * containing the resolution values of each of the promisesOrValues. + * @memberOf when + * + * @param {Array|Promise} promisesOrValues array of anything, may contain a mix + * of {@link Promise}s and values + * @param {function?} [onFulfilled] DEPRECATED, use returnedPromise.then() + * @param {function?} [onRejected] DEPRECATED, use returnedPromise.then() + * @param {function?} [onProgress] DEPRECATED, use returnedPromise.then() + * @returns {Promise} + */ + function all(promisesOrValues, onFulfilled, onRejected, onProgress) { + return _map(promisesOrValues, identity).then(onFulfilled, onRejected, onProgress); + } + + /** + * Joins multiple promises into a single returned promise. + * @return {Promise} a promise that will fulfill when *all* the input promises + * have fulfilled, or will reject when *any one* of the input promises rejects. + */ + function join(/* ...promises */) { + return _map(arguments, identity); + } + + /** + * Settles all input promises such that they are guaranteed not to + * be pending once the returned promise fulfills. The returned promise + * will always fulfill, except in the case where `array` is a promise + * that rejects. + * @param {Array|Promise} array or promise for array of promises to settle + * @returns {Promise} promise that always fulfills with an array of + * outcome snapshots for each input promise. + */ + function settle(array) { + return _map(array, toFulfilledState, toRejectedState); + } + + /** + * Promise-aware array map function, similar to `Array.prototype.map()`, + * but input array may contain promises or values. + * @param {Array|Promise} array array of anything, may contain promises and values + * @param {function} mapFunc map function which may return a promise or value + * @returns {Promise} promise that will fulfill with an array of mapped values + * or reject if any input promise rejects. + */ + function map(array, mapFunc) { + return _map(array, mapFunc); + } + + /** + * Internal map that allows a fallback to handle rejections + * @param {Array|Promise} array array of anything, may contain promises and values + * @param {function} mapFunc map function which may return a promise or value + * @param {function?} fallback function to handle rejected promises + * @returns {Promise} promise that will fulfill with an array of mapped values + * or reject if any input promise rejects. + */ + function _map(array, mapFunc, fallback) { + return when(array, function(array) { + + return promise(resolveMap); + + function resolveMap(resolve, reject, notify) { + var results, len, toResolve, resolveOne, i; + + // Since we know the resulting length, we can preallocate the results + // array to avoid array expansions. + toResolve = len = array.length >>> 0; + results = []; + + if(!toResolve) { + resolve(results); + return; + } + + resolveOne = function(item, i) { + when(item, mapFunc, fallback).then(function(mapped) { + results[i] = mapped; + + if(!--toResolve) { + resolve(results); + } + }, reject, notify); + }; + + // Since mapFunc may be async, get all invocations of it into flight + for(i = 0; i < len; i++) { + if(i in array) { + resolveOne(array[i], i); + } else { + --toResolve; + } + } + } + }); + } + + /** + * Traditional reduce function, similar to `Array.prototype.reduce()`, but + * input may contain promises and/or values, and reduceFunc + * may return either a value or a promise, *and* initialValue may + * be a promise for the starting value. + * + * @param {Array|Promise} promise array or promise for an array of anything, + * may contain a mix of promises and values. + * @param {function} reduceFunc reduce function reduce(currentValue, nextValue, index, total), + * where total is the total number of items being reduced, and will be the same + * in each call to reduceFunc. + * @returns {Promise} that will resolve to the final reduced value + */ + function reduce(promise, reduceFunc /*, initialValue */) { + var args = fcall(slice, arguments, 1); + + return when(promise, function(array) { + var total; + + total = array.length; + + // Wrap the supplied reduceFunc with one that handles promises and then + // delegates to the supplied. + args[0] = function (current, val, i) { + return when(current, function (c) { + return when(val, function (value) { + return reduceFunc(c, value, i, total); + }); + }); + }; + + return reduceArray.apply(array, args); + }); + } + + // Snapshot states + + /** + * Creates a fulfilled state snapshot + * @private + * @param {*} x any value + * @returns {{state:'fulfilled',value:*}} + */ + function toFulfilledState(x) { + return { state: 'fulfilled', value: x }; + } + + /** + * Creates a rejected state snapshot + * @private + * @param {*} x any reason + * @returns {{state:'rejected',reason:*}} + */ + function toRejectedState(x) { + return { state: 'rejected', reason: x }; + } + + /** + * Creates a pending state snapshot + * @private + * @returns {{state:'pending'}} + */ + function toPendingState() { + return { state: 'pending' }; + } + + // + // Utilities, etc. + // + + var reduceArray, slice, fcall, nextTick, handlerQueue, + setTimeout, funcProto, call, arrayProto, undef; + + // + // Shared handler queue processing + // + // Credit to Twisol (https://github.com/Twisol) for suggesting + // this type of extensible queue + trampoline approach for + // next-tick conflation. + + handlerQueue = []; + + /** + * Enqueue a task. If the queue is not currently scheduled to be + * drained, schedule it. + * @param {function} task + */ + function enqueue(task) { + if(handlerQueue.push(task) === 1) { + scheduleDrainQueue(); + } + } + + /** + * Schedule the queue to be drained after the stack has cleared. + */ + function scheduleDrainQueue() { + nextTick(drainQueue); + } + + /** + * Drain the handler queue entirely, being careful to allow the + * queue to be extended while it is being processed, and to continue + * processing until it is truly empty. + */ + function drainQueue() { + var task, i = 0; + + while(task = handlerQueue[i++]) { + task(); + } + + handlerQueue = []; + } + + // + // Capture function and array utils + // + /*global setImmediate,process,vertx*/ + + // capture setTimeout to avoid being caught by fake timers used in time based tests + setTimeout = global.setTimeout; + // Prefer setImmediate, cascade to node, vertx and finally setTimeout + /* + nextTick = typeof setImmediate === 'function' ? setImmediate.bind(global) + : typeof process === 'object' && process.nextTick ? process.nextTick + : typeof vertx === 'object' ? vertx.runOnLoop // vert.x + : function(task) { setTimeout(task, 0); }; // fallback + */ + nextTick = function(task) { task.call(); }; + + // Safe function calls + funcProto = Function.prototype; + call = funcProto.call; + fcall = funcProto.bind + ? call.bind(call) + : function(f, context) { + return f.apply(context, slice.call(arguments, 2)); + }; + + // Safe array ops + arrayProto = []; + slice = arrayProto.slice; + + // ES5 reduce implementation if native not available + // See: http://es5.github.com/#x15.4.4.21 as there are many + // specifics and edge cases. ES5 dictates that reduce.length === 1 + // This implementation deviates from ES5 spec in the following ways: + // 1. It does not check if reduceFunc is a Callable + reduceArray = arrayProto.reduce || + function(reduceFunc /*, initialValue */) { + /*jshint maxcomplexity: 7*/ + var arr, args, reduced, len, i; + + i = 0; + arr = Object(this); + len = arr.length >>> 0; + args = arguments; + + // If no initialValue, use first item of array (we know length !== 0 here) + // and adjust i to start at second item + if(args.length <= 1) { + // Skip to the first real element in the array + for(;;) { + if(i in arr) { + reduced = arr[i++]; + break; + } + + // If we reached the end of the array without finding any real + // elements, it's a TypeError + if(++i >= len) { + throw new TypeError(); + } + } + } else { + // If initialValue provided, use it + reduced = args[1]; + } + + // Do the actual reduce + for(;i < len; ++i) { + if(i in arr) { + reduced = reduceFunc(reduced, arr[i], i, arr); + } + } + + return reduced; + }; + + function identity(x) { + return x; + } + + return when; }); -})(typeof define == 'function' - ? define - : function (factory) { typeof module != 'undefined' - ? (module.exports = factory()) - : (this.when = factory()); - } - // Boilerplate for AMD, Node, and browser global +})( + typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(); }, + this ); diff --git a/src/event.js b/src/event.js new file mode 100644 index 0000000..1c81ae8 --- /dev/null +++ b/src/event.js @@ -0,0 +1,48 @@ +define function(require) { + + var _ = require('lodash'); + + function Event(type, data) { + this.type = type; + this.data = data; + }; + + function EventSource(types) { + var events = this.events = {}; + _(types).forEach(function(type) { + events[type] = []; + }); + + this.on = function on(type, callback) { + if(!_(events).has(type)) { + throw new Error('unsupported event: ' + type); + } + + events[type].push(callback); + }; + + this.off = function off(type, callback) { + if(!_(events).has(type)) { + throw new Error('unsupported event: ' + type); + } + + events[type] = _(events[type]).without(callback); + }; + + this.emit = function emit(event) { + if(!_(events).has(type)) { + throw new Error('unsupported event: ' + type); + } + + _(events[type]).forEach(function(callack) { + callback.call(this, event); + }); + }; + }; + + return { + Event: Event, + EventSource: EventSource + }; + +}; \ No newline at end of file diff --git a/src/fs2.js b/src/fs2.js new file mode 100644 index 0000000..2727ffc --- /dev/null +++ b/src/fs2.js @@ -0,0 +1,57 @@ +define function(require) { + + var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; + + var _ = require('lodash'); + var when = require('when'); + var Path = require('src/path'); + + var METADATA_STORE_NAME = 'metadata'; + var FILE_STORE_NAME = 'files'; + + var IDB_RO = 'readonly'; + var IDB_RW = 'readwrite'; + + function Operation(fs, stores, mode) { + + }; + + var FS_FORMAT = 'format'; + function FileSystem(name, flags) { + var format = _(flags).contains(FS_FORMAT); + + var openRequest = indexedDB.open(name); + }; + FileSystem.prototype.open = function open(transaction, path, flags, mode) { + + }; + FileSystem.prototype.opendir = function opendir(transaction, path) { + + }; + FileSystem.prototype.mkdir = function mkdir(transaction, path) { + + }; + FileSystem.prototype.rmdir = function rmdir(transaction, path) { + + }; + FileSystem.prototype.stat = function stat(transaction, path) { + + }; + FileSystem.prototype.link = function link(transaction, oldpath, newpath) { + + }; + FileSystem.prototype.unlink = function unlink(transaction, path) { + + }; + FileSystem.prototype.getxattr = function getxattr(transaction, path, name) { + + }; + FileSystem.prototype.setxattr = function setxattr(transaction, path, name, value) { + + }; + + return { + FileSystem: FileSystem + }; + +}; \ No newline at end of file diff --git a/src/indexeddb.js b/src/indexeddb.js new file mode 100644 index 0000000..2d5520f --- /dev/null +++ b/src/indexeddb.js @@ -0,0 +1,218 @@ +define function(require) { + + var when = require('when'); + + var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; + + /* + * ObjectStore + */ + + function ObjectStore(idbTransaction, name) { + this.idbObjectStore = idbTransaction.objectStore(name); + }; + ObjectStore.prototype.add = function add(value, key) { + var idbObjectStore = this.idbObjectStore; + var deferred = when.defer(); + + var request = idbObjectStore.add.call(idbObjectStore, value, key); + request.onsuccess = function(event) { + deferred.resolve(event); + }; + request.onerror = function(event) { + deferred.reject(event); + }; + + return deferred.promise; + }; + ObjectStore.prototype.clear = function clear() { + var idbObjectStore = this.idbObjectStore; + var deferred = when.defer(); + + var request = idbObjectStore.clear(); + request.onsuccess = function(event) { + deferred.resolve(event); + }; + request.onerror = function(event) { + deferred.reject(event); + }; + + return deferred.promise; + }; + ObjectStore.prototype.count = function count(key) { + var idbObjectStore = this.idbObjectStore; + var deferred = when.defer(); + + var request = idbObjectStore.count(key); + request.onsuccess = function(event) { + deferred.resolve(event); + }; + request.onerror = function(event) { + deferred.reject(event); + }; + + return deferred.promise; + }; + ObjectStore.prototype.createIndex = function createIndex(name, keyPath, optionalParameters) { + var idbObjectStore = this.idbObjectStore; + return idbObjectStore.createIndex.call(idbObjectStore, name, keyPath, optionalParameters); + }; + ObjectStore.prototype.delete = function delete(key) { + var idbObjectStore = this.idbObjectStore; + var deferred = when.defer(); + + var request = idbObjectStore.delete(key); + request.onsuccess = function(event) { + deferred.resolve(event); + }; + request.onerror = function(event) { + deferred.reject(event); + }; + + return deferred.promise; + }; + ObjectStore.prototype.deleteIndex = function deleteIndex(name) { + var idbObjectStore = this.idbObjectStore; + return idbObjectStore.deleteIndex.call(idbObjectStore, name); + }; + ObjectStore.prototype.get = function get(key) { + var idbObjectStore = this.idbObjectStore; + var deferred = when.defer(); + + var request = idbObjectStore.get(key); + request.onsuccess = function(event) { + deferred.resolve(event); + }; + request.onerror = function(event) { + deferred.reject(event); + }; + + return deferred.promise; + }; + ObjectStore.prototype.index = function index(name) { + var idbObjectStore = this.idbObjectStore; + return idbObjectStore.index.call(idbObjectStore, name); + }; + ObjectStore.prototype.openCursor = function openCursor(range, direction) { + var idbObjectStore = this.idbObjectStore; + var deferred = when.defer(); + + var request = idbObjectStore.openCursor(range, direction); + request.onsuccess = function(event) { + deferred.resolve(event); + }; + request.onerror = function(event) { + deferred.reject(event); + }; + + return deferred.promise; + }; + ObjectStore.prototype.put = function put(value, key) { + var idbObjectStore = this.idbObjectStore; + var deferred = when.defer(); + + var request = idbObjectStore.put(value, key); + request.onsuccess = function(event) { + deferred.resolve(event); + }; + request.onerror = function(event) { + deferred.reject(event); + }; + + return deferred.promise; + }; + + /* + * Transaction + */ + + function Transaction(idbDatabase, storeNames, mode) { + this.idbTransaction = idbDatabase.transaction(storeNames, mode); + }; + Transaction.prototype.objectStore = function objectStore(name) { + var idbTransaction = this.idbTransaction; + return new ObjectStore(idbTransaction, name); + }; + Transaction.prototype.abort = function abort() { + var idbTransaction = this.idbTransaction; + return idbTransaction.abort(); + }; + + /* + * Database + */ + + function Database(idbDatabase) { + this.idbDatabase = idbDatabase; + this.name = idbDatabase.name; + this.version = idbDatabase.version; + }; + Database.prototype.createObjectStore = function createObjectStore(name, optionalParameters) { + var idbDatabase = this.idbDatabase; + return idbDatabase.createObjectStore(name, optionalParameters); + }; + Database.prototype.deleteObjectStore = function deleteObjectStore(name) { + var idbDatabase = this.idbDatabase; + return idbDatabase.deleteObjectStore(name); + }; + Database.prototype.transaction = function transaction(storeNames, mode) { + var idbDatabase = this.idbDatabase; + return new Transaction(idbDatabase, storeNames, mode); + }; + Database.prototype.close = function close() { + var idbDatabase = this.idbDatabase; + return idbDatabase.close(); + }; + Database.prototype.objectStoreNames = function objectStoreNames() { + var idbDatabase = this.idbDatabase; + return idbDatabase.objectStoreNames; + }; + + /* + * Factory + */ + + function open(name, version) { + var deferred = when.defer(); + + var request = indexedDB.open(name); + request.onupgradeneeded = function(event) { + var result = new Database(event.target.result); + event.target._result = result; + deferred.notify(event); + }; + request.onsuccess = function(event) { + var result = new Database(event.target.result); + event.target._result = result; + deferred.resolve(event); + }; + request.onerror = function(event) { + deferred.reject(event); + }; + + return deferred.promise; + }; + + function deleteDatabase(name) { + var deferred = when.defer(); + + var request = indexedDB.deleteDatabase(name); + request.onupgradeneeded = function(event) { + deferred.notify(event); + }; + request.onsuccess = function(event) { + deferred.resolve(event); + }; + request.onerror = function(event) { + deferred.reject(event); + }; + + return deferred.promise; + }; + + return { + open: open, + deleteDatabase: deleteDatabase + }; + +}); \ No newline at end of file