diff --git a/README.md b/README.md
index f606f1c1e8..dabbed671c 100644
--- a/README.md
+++ b/README.md
@@ -92,6 +92,7 @@ List of Definitions
* [jQuery.Transit](http://ricostacruz.com/jquery.transit/) (by [MrBigDog2U](https://github.com/MrBigDog2U))
* [jQuery.Validation](http://bassistance.de/jquery-plugins/jquery-plugin-validation/) (by [Boris Yankov](https://github.com/borisyankov))
* [jQuery.Watermark](http://jquery-watermark.googlecode.com) (by [Anwar Javed](https://github.com/anwarjaved))
+* [JSDeferred](http://cho45.stfuawsc.com/jsdeferred/) (by [Daisuke Mino](https://github.com/minodisk))
* [JSONEditorOnline](https://github.com/josdejong/jsoneditoronline) (by [Vincent Bortone](https://github.com/vbortone/))
* [KeyboardJS](https://github.com/RobertWHurst/KeyboardJS) (by [Vincent Bortone](https://github.com/vbortone/))
* [Knockback](http://kmalakoff.github.com/knockback/) (by [Marcel Binot](https://github.com/docgit))
diff --git a/jsdeferred/jsdeferred-tests.ts b/jsdeferred/jsdeferred-tests.ts
new file mode 100644
index 0000000000..a42119bce2
--- /dev/null
+++ b/jsdeferred/jsdeferred-tests.ts
@@ -0,0 +1,858 @@
+///
+///
+
+declare function alert(a:any):void;
+declare function log(a:any):void;
+
+interface DeferredizedJQueryStatic extends JQueryStatic {
+ get(url:string, data?:any, success?:any, dataType?:any): Deferred;
+}
+declare var $: DeferredizedJQueryStatic;
+
+declare interface http {
+ get(url:string):Deferred;
+}
+
+
+/***** jsDoc case *****/
+
+ // constructor
+(()=> {
+ var d = new Deferred();
+ var d = Deferred();
+})();
+
+// define
+(()=> {
+ Deferred.define();
+ $.get("/hoge").next(function (data) {
+ alert(data);
+ }).
+ parallel([$.get("foo.html"), $.get("bar.html")]).next(function (values) {
+ log($.map(values, function (v) { return v.length }));
+ if (values[1].match(/nextUrl:\s*(\S+)/)) {
+ return $.get(RegExp.$1).next(function (d) {
+ return d;
+ });
+ }
+ }).
+ next(function (d) {
+ log(d.length);
+ });
+})();
+
+// next
+(()=> {
+ var d = new Deferred();
+ d.
+ next(function () {
+ alert(1);
+ }).
+ next(function () {
+ alert(2);
+ });
+ d.call();
+})();
+
+// error
+(()=> {
+ var d = new Deferred();
+ d.
+ next(function () {
+ alert(1);
+ throw "foo";
+ }).
+ next(function () {
+ alert('not shown');
+ }).
+ error(function (e) {
+ alert(e); //=> "foo"
+ });
+ d.call();
+})();
+
+// call
+(()=> {
+ var d = new Deferred();
+ setTimeout(function () {
+ d.call('value');
+ }, 100);
+ return d;
+})();
+
+// fail
+(()=> {
+ var d = new Deferred();
+ var x = new XMLHttpRequest();
+ x.onreadystatechange = function () {
+ if (x.readyState == 4) {
+ if (x.status == 200) d.call(x); else d.fail(x);
+ }
+ };
+ return d;
+})();
+
+// chain
+(()=> {
+ chain(
+ function () {
+ return wait(0.5);
+ },
+ function (w) {
+ throw "foo";
+ },
+ function error(e) {
+ alert(e);
+ },
+ [
+ function () {
+ return wait(1);
+ },
+ function () {
+ return wait(2);
+ }
+ ],
+ function (result) {
+ alert([ result[0], result[1] ]);
+ },
+ {
+ foo: wait(1),
+ bar: wait(1)
+ },
+ function (result) {
+ alert([ result.foo, result.bar ]);
+ },
+ function error(e) {
+ alert(e);
+ }
+ );
+})();
+
+// wait
+(()=> {
+ wait(1).next(function (elapsed) {
+ log(elapsed); //=> may be 990-1100
+ });
+})();
+
+// call
+(()=> {
+ next(function () {
+ function pow(x, n) {
+ function _pow(n, r) {
+ print([n, r]);
+ if (n == 0) return r;
+ return call(_pow, n - 1, x * r);
+ }
+
+ return call(_pow, n, 1);
+ }
+
+ return call(pow, 2, 10);
+ }).
+ next(function (r) {
+ print([r, "end"]);
+ });
+})();
+
+// parallel
+(()=> {
+ parallel([
+ $.get("foo.html"),
+ $.get("bar.html")
+ ]).next(function (values) {
+ values[0] //=> foo.html data
+ values[1] //=> bar.html data
+ });
+
+ parallel({
+ foo: $.get("foo.html"),
+ bar: $.get("bar.html")
+ }).next(function (values) {
+ values.foo //=> foo.html data
+ values.bar //=> bar.html data
+ });
+})();
+
+// loop
+(()=> {
+ //=> loop 1 to 100
+ loop({begin: 1, end: 100, step: 10}, function (n, o) {
+ for (var i = 0; i < o.step; i++) {
+ log(n + i);
+ }
+ });
+
+ //=> loop 10 times with sleeping 1 sec in each loop.
+ loop(10, function (n) {
+ log(n);
+ return wait(1);
+ });
+})();
+
+// repeat
+(()=> {
+ repeat(10, function (i) {
+ i //=> 0,1,2,3,4,5,6,7,8,9
+ });
+})();
+
+// register
+(()=> {
+ // Deferred.register("loop", loop);
+
+ // Global Deferred function
+ loop(10,function (n) {
+ print(n);
+ }).
+ // Registered Deferred.prototype.loop
+ loop(10, function (n) {
+ print(n);
+ });
+})();
+
+// connect
+(()=> {
+ var timeout = Deferred.connect(setTimeout, { target: window, ok: 0 });
+ timeout(1).next(function () {
+ alert('after 1 sec');
+ });
+
+ var timeout = Deferred.connect(window, "setTimeout");
+ timeout(1).next(function () {
+ alert('after 1 sec');
+ });
+})();
+
+// retry
+(()=> {
+ Deferred.retry(3,function () {
+ return http.get('foo.html');
+ }).
+ next(function (res) {
+ res //=> response if succeeded
+ }).
+ error(function (e) {
+ e //=> error if all try failed
+ });
+})();
+
+
+/***** Tutorial case *****/
+
+(()=> {
+ Deferred.define();
+
+ next(function () {
+ alert("Hello!");
+ return wait(5);
+ }).
+ next(function () {
+ alert("World!");
+ });
+
+ Deferred.next(function () {
+ alert("Hello!");
+ return Deferred.wait(5);
+ }).
+ next(function () {
+ alert("World!");
+ });
+
+ // http.get is assumed to be a function that takes a URI as an argument and returns a Deferred instance
+ var results = [];
+ next(function () {
+ return http.get("/foo.json").next(function (data) {
+ results.push(data);
+ });
+ }).
+ next(function () {
+ return http.get("/baz.json").next(function (data) {
+ results.push(data);
+ });
+ }).
+ next(function () {
+ return http.get("/baz.json").next(function (data) {
+ results.push(data);
+ });
+ }).
+ next(function () {
+ alert(results);
+ });
+
+ var wants = ["/foo.json", "/bar.json", "/baz.json"];
+ var results = [];
+ loop(wants.length,function (i) {
+ return http.get(wants[i]).next(function (data) {
+ results.push(data);
+ });
+ }).
+ next(function () {
+ alert(results);
+ });
+
+ parallel([
+ http.get("/foo.json"),
+ http.get("/bar.json"),
+ http.get("/baz.json")
+ ]).
+ next(function (results) {
+ alert(results);
+ });
+
+ next(function () {
+ // something 1
+ }).
+ next(function () {
+ // asynchronous process
+ throw "error!";
+ }).
+ next(function () {
+ // something 2 (not executed as an error occurs in the previous process)
+ });
+
+ next(function () {
+ // something 1
+ }).
+ next(function () {
+ // asynchronous process
+ throw "error!";
+ }).
+ next(function () {
+ // something 2 (not executed as an error occurs in the previous process)
+ }).
+ error(function (e) {
+ alert(e);
+ });
+
+ next(function () {
+ // something 1
+ }).
+ next(function () {
+ // asynchronous process
+ throw "error!";
+ }).
+ error(function (e) {
+ alert(e);
+ }).
+ next(function () {
+ // something 2 (executed since the exception would already be handled)
+ }).
+ error(function (e) {
+ alert(e);
+ });
+
+ next(function () {
+ alert("Hello!");
+ return wait(5);
+ }).
+ next(function () {
+ alert("World!");
+ });
+
+ next(function () {
+ alert(1);
+ return next(function () {
+ alert(2);
+ }).
+ next(function () {
+ alert(3);
+ });
+ }).
+ next(function () {
+ alert(4);
+ });
+
+ var http = {}
+ http.get = function (uri) {
+ var deferred = new Deferred();
+ var xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState == 4) {
+ if (xhr.status == 200) {
+ deferred.call(xhr);
+ } else {
+ deferred.fail(xhr);
+ }
+ }
+ };
+ deferred.canceller = function () { xhr.abort() };
+ return deferred;
+ }
+
+ loop(1000, function (n) {
+ // heavy process
+ });
+
+ function repeat(n, f) {
+ var i = 0, end = {}, ret = null;
+ return Deferred.next(function () {
+ var t = (new Date()).getTime();
+ divide: {
+ do {
+ if (i >= n) break divide;
+ ret = f(i++);
+ } while ((new Date()).getTime() - t < 20);
+ return Deferred.call(arguments.callee);
+ }
+ });
+ }
+
+ next(function () {
+ console.log("start");
+ }).
+ next(function () {
+ function pow(x, n) {
+ function _pow(n, r) {
+ console.log([n, r]);
+ if (n == 0) return r;
+ return call(_pow, n - 1, x * r);
+ }
+
+ return call(_pow, n, 1);
+ }
+
+ return call(pow, 2, 10);
+ }).
+ next(function (r) {
+ console.log([r, "end"]);
+ }).
+ error(function (e) {
+ alert(e);
+ })
+
+ loop(10, function (i) {
+ console.log(i)
+ });
+
+ $.get("README.markdown").next(function (data) {
+ console.log(data);
+ });
+
+})();
+
+(()=> {
+ console.log("start. gathering data.");
+
+ parallel([$.get("README.markdown"), $.get("ChangeLog")]).
+ next(function (values) {
+ var lengths = $.map(values, function (i) { return i.length });
+ console.log(lengths.join(", "));
+ });
+})();
+
+(()=> {
+ console.log("start. gathering data.");
+
+ parallel({html: $.get("README.markdown"), js: $.get("ChangeLog")}).
+ next(function (values) {
+ console.log(["html=", values.html.length, " js=", values.js.length].join(""));
+ });
+})();
+
+(()=> {
+ console.log("start. wait 3 sec.");
+
+ var list = [];
+ var printAndReturn = function (i) {
+ console.log(i + "msec elapsed");
+ return i;
+ };
+ list.push(wait(0).next(printAndReturn));
+ list.push(wait(1).next(printAndReturn));
+ list.push(wait(2).next(printAndReturn));
+ list.push(wait(3).next(printAndReturn));
+
+ parallel(list).next(function (values) {
+ console.log("Completed. values: " + values.join(", "));
+ });
+})();
+
+(()=> {
+ var queue = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+ var workers = new Array(2);
+ var work = function (job) {
+ console.log('working... ' + job);
+ return wait(Math.random() * 4);
+ };
+
+ for (var i = 0, len = workers.length; i < len; i++) {
+ workers[i] = next(function me() {
+ var job = queue.shift();
+ if (!job) return;
+
+ console.log("start worker: " + job);
+ return next(function () { return job }).
+ next(work).
+ next(me);
+ }).
+ error(function (e) {
+ alert(e);
+ });
+ }
+
+ parallel(workers).next(function () {
+ console.log('all done!');
+ });
+})();
+
+(()=> {
+ next(function () {
+ var sum = 0;
+ return loop({end: 100000, step: 1000}, function (n, o) {
+ console.log(["Processing divided loop:n=", n, ", sum=", sum, " last?=", o.last].join(""));
+ for (var i = 0; i < o.step; i++) {
+ // console.log(i + n);
+ sum += i + n;
+ }
+ console.log(["sum=", sum].join(""));
+ return sum;
+ });
+ }).
+ next(function (e) {
+ console.log("Result:" + e);
+ console.log("end");
+ }).
+ error(function (e) {
+ console.log(e);
+ });
+})();
+
+(()=> {
+ loop({begin: 1, end: 100, step: 10}, function (n, o) {
+ console.log(["Processing divided loop:n=", n, " last?=", o.last].join(""));
+ for (var i = 0; i < o.step; i++) {
+ var j = n + i;
+ console.log(j);
+ }
+ });
+})();
+
+(()=> {
+ Deferred.repeat(100, function (n, o) {
+ console.log(n);
+ for (var i = 0; i < Math.pow(n, 2); i++) {
+ for (var j = n; j; j--);
+ }
+ });
+})();
+
+(()=> {
+ loop(10, function (n) {
+ console.log(n);
+ return wait(0.1);
+ });
+
+ loop(10, function (n) {
+ console.log(String.fromCharCode(97 + n));
+ return wait(0.2);
+ });
+})();
+
+(()=> {
+ console.log(0);
+ loop(10,function (n) {
+ console.log(n);
+ return n;
+ }).
+ wait(1).
+ loop(10,function (n) {
+ var c = String.fromCharCode(97 + n);
+ console.log(c);
+ return c;
+ }).
+ next(function (i) {
+ console.log("end");
+ }).
+ error(function (e) {
+ alert(e);
+ });
+})();
+
+(()=> {
+ loop(5,function (i, o) {
+ console.log(i);
+ return o.last ? i : wait(1);
+ }).
+ next(function (e) {
+ console.log("end [" + e + "]");
+ }).
+ error(function (e) {
+ console.log(e);
+ });
+})();
+
+(()=> {
+ next(function (i) {
+ function delayloop(i) {
+ console.log(i++);
+ if (i < 5) {
+ return wait(1).next(function () {
+ return call(delayloop, i);
+ });
+ }
+ }
+
+ return call(delayloop, 0);
+ }).
+ next(function (e) {
+ console.log("end");
+ }).
+ error(function (e) {
+ console.log(e);
+ });
+})();
+
+(()=> {
+ function callcc(fun) {
+ var error = new Deferred();
+ return call(function () {
+ // JSDeferred passes current Deferred Object to this.
+ var ccdeferred = this;
+ // Call with current continuation (calling Deferred.next)
+ return fun(function (a) {
+ ccdeferred._next.call(a);
+ throw error
+ });
+ }).
+ error(function (e) {
+ // Current Deferred chain must be stopped
+ if (e === error) {
+ return e;
+ } else {
+ throw e;
+ }
+ });
+ }
+
+ callcc(function (cont) {
+ return 10 * 10 * cont(20);
+ }).
+ next(function (val) {
+ console.log("callcc1 returns:" + val);
+ });
+ // should show "callcc1 returns:20"
+
+ var cont;
+ var i = 0;
+ callcc(function (c) {
+ cont = c;
+ return 10;
+ }).
+ next(function (val) {
+ console.log("callcc2 returns:" + val);
+ if (!i++) cont(20);
+ });
+ // should show "callcc2 returns:10", "callcc returns:20"
+})();
+
+(()=> {
+ function callcc(fun) {
+ var error = new Deferred();
+ return call(function () {
+ // JSDeferred passes current Deferred Object to this.
+ var ccdeferred = this;
+ // Call with current continuation (calling Deferred.next)
+ return fun(function (a) {
+ ccdeferred._next.call(a);
+ throw error
+ });
+ }).
+ error(function (e) {
+ // Current Deferred chain must be stopped
+ if (e === error) {
+ return e;
+ } else {
+ throw e;
+ }
+ });
+ }
+
+ // http://www.sampou.org/scheme/t-y-scheme/t-y-scheme-Z-H-16.html#node_chap_14
+ // Just port above.
+ function amb() {
+ var alts = arguments;
+ var prevAmbFail = amb.ambFail;
+
+ return callcc(function (sk) {
+ return loop(alts.length,function (i) {
+ var alt = alts[i];
+ return callcc(function (fk) {
+ amb.ambFail = function () {
+ amb.ambFail = prevAmbFail;
+ return fk("fail");
+ };
+ return sk(alt);
+ });
+ }).
+ next(prevAmbFail);
+ });
+ }
+
+ amb.ambFail = function () { throw "amb tree exhausted" };
+
+ // Utility function
+ function amb1(ambvars) {
+ var f = wait(0);
+ var vars = {};
+ for (var k in ambvars) if (ambvars.hasOwnProperty(k)) (function (name, val) {
+ console.log(name);
+ f = f.next(function () {
+ return amb.apply(this, val).next(function (i) {
+ vars[name] = i;
+ return vars;
+ });
+ });
+ })(k, ambvars[k]);
+
+ return f;
+ }
+
+ function assert(cond) {
+ if (!cond) throw amb();
+ }
+
+ // http://mayokara.info/note/view/251
+ Array.prototype.uniq = function () {
+ for (var i = 0, l = this.length; i < l; i++) {
+ if (this.indexOf(this[i]) < i) {
+ this.splice(i--, l-- && 1);
+ }
+ }
+ return this;
+ };
+
+ amb1({
+ baker : [1, 2, 3, 4, 5],
+ cooper : [1, 2, 3, 4, 5],
+ fletcher: [1, 2, 3, 4, 5],
+ miller : [1, 2, 3, 4, 5],
+ smith : [1, 2, 3, 4, 5]
+ }).
+ next(function (vars) {
+ with (vars) {
+ console.log(vars);
+ // 簡易 distinct
+ assert([baker, cooper, fletcher, miller, smith].uniq().length == 5);
+ console.log("distinct passed");
+ assert(baker != 5);
+ assert(cooper != 1);
+ assert(fletcher != 1 && fletcher != 5);
+ assert(miller > cooper);
+ assert(Math.abs(smith - fletcher) != 1);
+ assert(Math.abs(fletcher - cooper) != 1);
+
+ return vars;
+ }
+ }).
+ next(function (vars) {
+ with (vars) {
+ console.log("solved");
+ console.log(vars);
+ alert(uneval(vars));
+ }
+ }).
+ error(function (e) {
+ alert(e)
+ });
+})();
+
+(()=> {
+ var d1 = new Deferred();
+ d1.callback.ok = function () {
+ alert("1");
+ };
+
+ var d2 = new Deferred();
+ d2.callback.ok = function () {
+ alert("2");
+ };
+
+ // Set d2 as continuation of d1.
+ d1._next = d2;
+
+ // Invoke the chain.
+ d1.call();
+})();
+
+(()=> {
+ next(function () { // this `next` is global function
+ alert("1");
+ }).
+ next(function () { // this `next` is Deferred#next
+ alert("2");
+ }).
+ next(function () {
+ alert("3");
+ });
+})();
+
+(()=> {
+ next(function () {
+ alert("1");
+ }).
+ next(function () {
+ alert("2");
+ // child Deferred
+ return next(function () {
+ alert("3");
+ });
+ }).
+ next(function () {
+ alert("4");
+ });
+})();
+
+(()=> {
+ next(function () {
+ alert("1");
+ }).
+ next(function () {
+ alert("2");
+ var d = next(function () {
+ alert("3");
+ });
+ d._next = this._next;
+ this.cancel();
+ }).
+ next(function () {
+ alert("4");
+ });
+})();
+
+(()=> {
+ next(function () {
+ alert("1");
+ }).
+ next(function () {
+ alert("2");
+ next(function () {
+ alert("3");
+ }).
+ next(function () {
+ alert("4");
+ });
+ });
+})();
+
+(()=> {
+ next(function () {
+ throw "Error";
+ }).
+ error(function (e) {
+ expect("Errorback called", "Error", e);
+ return e; // recovering error
+ }).
+ next(function (e) {
+ expect("Callback called", "Error", e);
+ throw "Error2";
+ }).
+ next(function (e) {
+ // This process is not called because
+ // the error is not recovered.
+ ng("Must not be called!!");
+ }).
+ error(function (e) {
+ expect("Errorback called", "Error2", e);
+ });
+})();
diff --git a/jsdeferred/jsdeferred.d.ts b/jsdeferred/jsdeferred.d.ts
new file mode 100644
index 0000000000..1e354e7ee5
--- /dev/null
+++ b/jsdeferred/jsdeferred.d.ts
@@ -0,0 +1,98 @@
+// Type definitions for JSDeferred
+// Project: https://github.com/cho45/jsdeferred
+// Definitions by: Daisuke Mino
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+interface Loop {
+ begin:number;
+ end:number;
+ step:number;
+}
+
+interface ConnectOption {
+ target:any;
+ args?:any[];
+ ok?:number;
+ ng?:number;
+}
+
+interface RetryOption {
+ wait:number;
+}
+
+interface DeferredizedFunction { (...arg:any[]):Deferred; }
+interface DeferredizedFunctionWithNumber { (n:number):Deferred; }
+interface FunctionWithNumber { (i:number); }
+interface ErrorCallback { (d:Deferred, ...args:any[]); }
+
+declare class Deferred {
+
+ static methods:string[];
+
+ static isDeferred(obj:any):bool;
+
+ static next(fun:Function):Deferred;
+
+ static chain(...args:any[]):Deferred;
+
+ static wait(n:number):Deferred;
+
+ static call(fun?:Function, ...args:any[]):Deferred;
+
+ static parallel(dl:any):Deferred;
+
+ static earlier(dl:any):Deferred;
+
+ static loop(n:number, fun:FunctionWithNumber):Deferred;
+
+ static loop(n:Loop, fun:FunctionWithNumber):Deferred;
+
+ static repeat(n:number, fun:FunctionWithNumber):Deferred;
+
+ static register(name:string, fun:DeferredizedFunction):void;
+
+ static connect(funo:any, options:string):DeferredizedFunction;
+
+ static connect(funo:Function, options?:ConnectOption):DeferredizedFunction;
+
+ static retry(retryCount:number, funcDeferred:DeferredizedFunctionWithNumber, options?:RetryOption):Deferred;
+
+ static define(obj?:any, list?:string[]):any;
+
+ constructor();
+
+ next(fun:Function):Deferred;
+
+ wait(n:number):Deferred;
+
+ error(fun:ErrorCallback):Deferred;
+
+ call(val?:any):Deferred;
+
+ fail(err:any):Deferred;
+
+ cancel():Deferred;
+
+ parallel(dl:any):Deferred;
+
+ loop(n:number, fun:FunctionWithNumber):Deferred;
+
+ loop(n:Loop, fun:FunctionWithNumber):Deferred;
+
+}
+
+
+declare function chain(...args:any[]):Deferred;
+
+declare function wait(n:number):Deferred;
+
+declare function call(fun?:Function, ...args:any[]):Deferred;
+
+declare function parallel(dl:any):Deferred;
+
+declare function earlier(dl:any):Deferred;
+
+declare function loop(n:number, fun:FunctionWithNumber):Deferred;
+declare function loop(n:Loop, fun:FunctionWithNumber):Deferred;
+
+declare function repeat(n:number, fun:FunctionWithNumber):Deferred;
\ No newline at end of file