├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── build.js
├── build
├── async-min.js
├── async.js
├── central-min.js
├── central.js
├── grandcentral-min.js
├── grandcentral.js
├── jshelpers-min.js
├── jshelpers.js
├── list-min.js
├── list.js
├── overload-min.js
└── overload.js
├── doc
├── build.js
├── en
│ ├── async
│ │ ├── features.html
│ │ └── features.md
│ ├── central
│ │ ├── features.html
│ │ └── features.md
│ ├── grandcentral
│ │ ├── features.html
│ │ └── features.md
│ ├── index.html
│ ├── index.md
│ ├── list
│ │ ├── features.html
│ │ └── features.md
│ └── overload
│ │ ├── features.html
│ │ └── features.md
├── index.html
├── index.md
├── javascripts
│ ├── page.js
│ ├── shBrushJScript.js
│ ├── shCore.js
│ └── xregexp.js
├── stylesheets
│ ├── default.css
│ ├── shCore.css
│ └── shThemeDefault.css
├── template.mustache
└── zh
│ ├── async
│ ├── features.html
│ ├── features.md
│ ├── introduction.html
│ ├── introduction.md
│ ├── secrets.html
│ └── secrets.md
│ ├── central
│ ├── features.html
│ ├── features.md
│ ├── introduction.html
│ ├── introduction.md
│ ├── secrets.html
│ └── secrets.md
│ ├── grandcentral
│ ├── features.html
│ ├── features.md
│ ├── introduction.html
│ ├── introduction.md
│ ├── secrets.html
│ └── secrets.md
│ ├── index.html
│ ├── index.md
│ ├── list
│ ├── features.html
│ ├── features.md
│ ├── introduction.html
│ ├── introduction.md
│ ├── secrets.html
│ └── secrets.md
│ └── overload
│ ├── features.html
│ ├── features.md
│ ├── introduction.html
│ ├── introduction.md
│ ├── secrets.html
│ └── secrets.md
├── index.js
├── package.json
├── samples
└── async
│ ├── 00-async-ajax.html
│ ├── 01-async-chain.html
│ ├── async-ajax.js
│ └── fake
│ ├── json.js
│ └── next-json.js
├── src
├── async.js
├── central.js
├── grandcentral.js
├── list.js
└── overload.js
└── test
├── index.html
├── javascripts
├── async.js
├── central.js
├── grandcentral.js
├── list.js
├── overload.js
└── qunit.js
└── stylesheets
└── qunit.css
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 1.0.7
4 |
5 | * Fix `...` (aka `Overload.More`) bug when it matches 0 arguments and thus should be recognized as empty `Array`.
6 | * Fix bug when comparing `...` (aka `Overload.More`) with numbers of `*` (aka `Overload.Any`).
7 |
8 | ## 1.0.6
9 |
10 | * Added support for exporting as YUI module.
11 |
12 | ## 1.0.5
13 |
14 | * Replaced all `.yield` in Async and List with `["yield"]` in order to avoid the use of reserved word.
15 | * Minor code tweak.
16 |
17 | ## 1.0.4
18 |
19 | * Fixed the misuse of module causing all scripts stop working for browsers.
20 |
21 | ## 1.0.3
22 |
23 | * Added `Async.collect` for parallel operations.
24 |
25 | ## 1.0.2
26 |
27 | * Redesign `...` (aka `Overload.More`) usage to make it work like CoffeeScript's.
28 |
29 | ## 1.0.1
30 |
31 | * Added `Async.instant` as a short-cut to `Async.wait(0, content)`.
32 |
33 | ## 1.0
34 |
35 | * First stable release.
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009, Baidu Inc.
2 | All rights reserved.
3 |
4 | Redistribution and use of this software in source and binary forms, with or
5 | without modification, are permitted provided that the following conditions
6 | are met:
7 |
8 | Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 |
11 | Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | Neither the name of Baidu Inc. nor the names of its contributors may be used
16 | to endorse or promote products derived from this software without specific
17 | prior written permission of Baidu Inc.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | jsHelpers is a set of tools help you write your AJAX application in a simpler way. It focuses on reducing complexity of JavaScript code, but it has nothing to do with DOM or Array. If you want some basic library helps you with DOM manipulation and JavaScript extension, try [jQuery](http://jquery.com), [Prototype](http://www.prototypejs.org), [Tangram](http://tangram.baidu.com), etc. jsHelpers works with these libraries but doesn't depend on any of them.
4 |
5 | What does jsHelpers do then? I apologize for not providing more details here. You can find it in [documentations](http://catchen.github.com/jsHelpers/). All documentations are in both Chinese and English.
6 |
7 | The latest release is [1.0.7](https://github.com/CatChen/jsHelpers/tree/1.0.7). In this release, all source code and unit tests are ready. It also works with [Node.js](http://nodejs.org/) and [Node Package Manager](http://npmjs.org/).
8 |
9 | # How to use
10 |
11 | ## Browser
12 |
13 | You can refer to any script files in the build directory. Every module has both minified and non-minified versions.
14 |
15 |
16 |
17 | ## Node.js
18 |
19 | You can install jsHelpers to your project via npm.
20 |
21 | npm install jshelpers
22 |
23 | Then you can use it in your node.js project.
24 |
25 | const jshelpers = require('jshelpers');
26 |
27 | # How to build
28 |
29 | If you edit the source code or documentation, you might want to rebuild them. You will need Node.js and NPM to do this.
30 |
31 | ## Build code
32 |
33 | You will need uglify-js to rebuild the code.
34 |
35 | npm install uglify-js
36 | node build.js
37 |
38 | ## Build documentations
39 |
40 | You will need showdown and mustache to rebuild the documentations.
41 |
42 | npm install showdown mustache
43 | cd doc
44 | node build.js
45 |
46 | # Changelog
47 |
48 | See the [changelog](https://github.com/CatChen/jsHelpers/blob/master/changelog.md) file.
49 |
--------------------------------------------------------------------------------
/build.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const uglify = require('uglify-js');
4 |
5 | const sourceDirectory = 'src';
6 | const destinationDirectory = 'build';
7 |
8 | if (path.existsSync(destinationDirectory)) {
9 | var oldFileNames = fs.readdirSync(destinationDirectory);
10 | for (var i = 0; i < oldFileNames.length; i++) {
11 | var oldFileName = path.join(destinationDirectory, oldFileNames[i]);
12 | fs.unlinkSync(oldFileName);
13 | }
14 | fs.rmdirSync(destinationDirectory);
15 | console.log('removed build directory');
16 | } else {
17 | console.log('build directory doesn\'t exist');
18 | }
19 |
20 | fs.mkdirSync(destinationDirectory, '755');
21 | console.log('created build directory');
22 |
23 | var fileNames = fs.readdirSync(sourceDirectory);
24 | var allFiles = [];
25 |
26 | for (var i = 0; i < fileNames.length; i++) {
27 | var sourceFileName = path.join(sourceDirectory, fileNames[i]);
28 | var destinationFileName = path.join(destinationDirectory, fileNames[i]);
29 | var destinationCompressedFileName = path.join(destinationDirectory, fileNames[i].replace(/.js$/, '-min.js'));
30 |
31 | var sourceFile = String(fs.readFileSync(sourceFileName));
32 | allFiles.push(sourceFile);
33 |
34 | var compressedFile = uglify(sourceFile);
35 |
36 | fs.writeFileSync(destinationFileName, sourceFile);
37 | console.log('copied ' + sourceFileName + ' into ' + destinationFileName);
38 | fs.writeFileSync(destinationCompressedFileName, compressedFile);
39 | console.log('compressed ' + sourceFileName + ' into ' + destinationCompressedFileName);
40 | }
41 |
42 | var composedFile = allFiles.join('');
43 | var compressedComposedFile = uglify(composedFile);
44 | fs.writeFileSync(path.join(destinationDirectory, 'jshelpers.js'), composedFile);
45 | console.log('composed all files into ' + path.join(destinationDirectory, 'jshelpers.js'));
46 | fs.writeFileSync(path.join(destinationDirectory, 'jshelpers-min.js'), compressedComposedFile);
47 | console.log('compressed all files into ' + path.join(destinationDirectory, 'jshelpers-min.js'));
48 |
--------------------------------------------------------------------------------
/build/async-min.js:
--------------------------------------------------------------------------------
1 | (function(){var a={};if(typeof module!="undefined"&&module.exports)module.exports=a;else if(typeof YUI!="undefined"&&YUI.add)YUI.add("async",function(b){b.Async=a},"1.0.6",{requires:[]});else if(typeof window=="object")window.Async=a;else return;var b=[],c=function(a){for(var c=0;c0&&!c.error){var b=d.shift();if(f){try{var e=b.call(c,c.result)}catch(g){c.error=g,c.state="error",i(c);break}if(e&&e instanceof a.Operation){h=a.chain(),h.onerror(function(a){c.error=h.error,c.state="error",i(c)});while(d.length>0)h.next(d.shift()),h.next(function(a){c.result=a;return a});h.next(function(a){c.state="completed",c.completed=!0;return a}),e.addCallback(function(a){c.result=a,h.go(a)})}else c.result=e}else try{b.call(c,c.result)}catch(g){c.error=g,c.state="error",i(c);break}}!h&&!c.error&&(c.state="completed",c.completed=!0)}else{while(d.length>0)h.next(d.shift());h.next(function(a){c.result=a,c.state="completed",c.completed=!0;return a})}},1);return this},this.go=function(a){return this.yield(a)},this.addCallback=function(a){d.push(a),(this.completed||f&&g)&&this.yield(this.result);return this},this.next=function(a){return this.addCallback(a)},this.wait=function(b){var c=this;f&&this.next(function(){return a.wait(b,c.result)});return this},this.onerror=function(a){e.push(a);return this}},a.chain=function(b){var c=new a.Operation({chain:!0});b&&c.next(b);return c},a.go=function(b){return a.chain().go(b)},a.collect=function(b,c){var d=new a.Operation,e=[],f=0,g=function(){f==b.length&&d.yield(e)};for(var h=0;ha},b.gte=function(a,b){return arguments.length==2&&b>=a},b["in"]=function(a,c){if(arguments.length<2)return!1;for(var d=0;d0?b[""](a,c):b[""](a)}},d=function(a){var b=[];a.listen=function(d,e){d instanceof Function||(d=c(d)),b.push({filter:d,handler:e});return a},a.call=function(c){for(var d=0;d testValue; };
99 | operators["gte"] = function(testValue, value) { return arguments.length == 2 && value >= testValue; };
100 |
101 | operators["in"] = function(testValue, value) {
102 | if (arguments.length < 2) {
103 | return false;
104 | }
105 | for (var i = 0; i < testValue.length; i++) {
106 | if (operators["eq"](testValue[i], value)) {
107 | return true;
108 | }
109 | }
110 | return false;
111 | };
112 |
113 | operators["nin"] = function(testValue, value) { return arguments.length == 2 && !operators["in"](testValue, value); };
114 |
115 | operators["all"] = function(testValue, value) {
116 | if (arguments.length < 2) {
117 | return false;
118 | }
119 | if (!(value instanceof Array)) {
120 | return false;
121 | }
122 | var found;
123 | for (var i = 0; i < testValue.length; i++) {
124 | found = false;
125 | for (var j = 0; j < value.length; j++) {
126 | if (operators["eq"](testValue[i], value[j])) {
127 | found = true;
128 | break;
129 | }
130 | }
131 | if (!found) {
132 | return false;
133 | }
134 | }
135 | return true;
136 | };
137 |
138 | operators["ex"] = function(testValue, value) {
139 | if (testValue === true) {
140 | return arguments.length == 2;
141 | } else if (testValue === false) {
142 | return arguments.length == 1;
143 | }
144 | return false;
145 | };
146 |
147 | operators["re"] = function(testValue, value) { return arguments.length == 2 && value && value.match && value.match(testValue); };
148 |
149 | operators["f"] = function(testValue, value) { return testValue.call(value, value); };
150 |
151 | var createFilter = function(condition) {
152 | return function(json) {
153 | if (arguments.length > 0) {
154 | return operators[""](condition, json);
155 | } else {
156 | return operators[""](condition);
157 | }
158 | };
159 | };
160 |
161 | var initiateGrandCentralService = function(target) {
162 | var filterHandlerBundles = [];
163 |
164 | target.listen = function(filter, handler) {
165 | if (!(filter instanceof Function)) {
166 | filter = createFilter(filter);
167 | }
168 | filterHandlerBundles.push({
169 | filter: filter,
170 | handler: handler
171 | });
172 | return target;
173 | };
174 |
175 | target.call = function(json) {
176 | for (var i = 0; i < filterHandlerBundles.length; i++) {
177 | if (filterHandlerBundles[i].filter.apply(this, arguments)) {
178 | filterHandlerBundles[i].handler(json);
179 | }
180 | }
181 | return target;
182 | };
183 | };
184 |
185 | GrandCentral.extend = function(target) {
186 | initiateGrandCentralService(target);
187 | return target;
188 | };
189 |
190 | GrandCentral.extend(GrandCentral);
191 | })();
192 |
--------------------------------------------------------------------------------
/build/list-min.js:
--------------------------------------------------------------------------------
1 | (function(){var a=function(){};a.prototype.item=function(){throw"abstract enumerator should not be instantiated"},a.prototype.next=function(){throw"abstract enumerator should not be instantiated"},a.prototype.reset=function(){throw"abstract enumerator should not be instantiated"};var b=function(a){var b=0,c=1,d=2,e=b,f=0;this.item=function(){if(e==c)return a[f];if(e==b)throw"incorrect index";if(e==d)throw"incorrect index"},this.next=function(){switch(e){case b:a.length===0?e=d:e=c;break;case c:f++,f>=a.length&&(e=d);break;case d:}return e!=d},this.reset=function(){e=b,f=0}};b.prototype=new a;var c=function(a){var b=NaN,c=[];this.item=function(){if(b>=0){b in c||(c[b]=a.item());return c[b]}return a.item()},this.next=function(){b>=-1&&b++;return a.next()},this.reset=function(){b=-1;return a.reset()},this.cache=function(){return[].slice.call(c,0)}};c.prototype=new a;var d=function(a){this.item=a.item,this.next=a.next,this.reset=a.reset};d.prototype=new a;var e=function(a,b){this.item=a.item,this.next=a.next,this.reset=a.reset,b.item&&(this.item=function(){return b.item(a)}),b.next&&(this.next=function(){return b.next(a)}),b.reset&&(this.reset=function(){return b.reset(a)})};e.prototype=new a;var f=function(a){this.yield=function(b){a.yield&&a.yield(b)},this.end=function(){a.end&&a.end()}},g=function(c){var d,e=[],f=-1;if(!c)d=new b([]),e=[],f=0;else if(arguments.length>1)d=new b([].slice.call(arguments,0)),e=[].slice.call(arguments,0),f=e.length;else if(c instanceof Array)d=new b([].slice.call(c,0)),e=[].slice.call(c,0),f=e.length;else if(c instanceof a)d=c;else throw"source should be an array";this.at=function(a){var b=0;if(a<0)throw"incorrect index";if(e.length>a)return e[a];if(f>=0&&a>=f)throw"incorrect index";d.reset();while(d.next()){e[b]=d.item();if(a===0){var c=e[b];return c}a--,b++}f=b;throw"incorrect index"},this.length=function(){if(f<0){d.reset(),f=0;while(d.next())f++}return f},this.each=function(a){d.reset();for(var b=0;b=l.length&&k!=h)a.call(n,n);j=k;break;case h:}return j!=h},reset:function(){m=-1,j=b}});return new g(new c(o))},g.iterate=function(a,b){var e=0,f=1,h,i=e,j=new d({item:function(){switch(i){case e:throw"incorrect index";case f:return h}},next:function(){switch(i){case e:h=b,i=f;break;case f:h=a.call(h,h)}return!0},reset:function(){i=e}});return new g(new c(j))},g.count=function(a,b){a=a||0,b=b||1;return g.iterate(function(a){return a+b},a)},g.repeat=function(a){return g.iterate(function(a){return a},a)},g.concatenate=function(){var a=0,b=1,e=2,f=[].slice.call(arguments,0),h=0,i=a,j=new d({item:function(){return f[h].enumerator().item()},next:function(){switch(i){case a:f[h].enumerator().reset(),i=b;return j.next();case b:if(!f[h].enumerator().next()){h++;if(hb?a:b},a);throw"cannot process empty list"},g.prototype.minimum=function(){var a=this.at(0);if(a)return this.drop(1).fold(function(a,b){return a0?c:-1},this.lastIndexOf=function(a,b){b=b||this.length();var c=this.take(b),d=c.reverse(),e=this.indexOf.call(d,a);return e>=0?c.length()-1-e:-1},this.every=function(a,b){return this.all(function(c){return a.call(b,c)})},this.some=function(a,b){return this.any(function(c){return a.call(b,c)})},this.forEach=function(a,b){this.each(function(c){a.call(b,c)})},this.map=function(a,c){return b.prototype.map.call(this,function(b){return a.call(c,b)})},this.filter=function(a,c){return b.prototype.filter.call(this,function(b){return a.call(c,b)})};var d=this.reduce=function(a,b){return arguments.length>1?this.fold(function(b,c){return a.call(undefined,b,c)},b):d.call(this.drop(1),a,this.at(0))};this.reduceRight=function(a,b){return d.apply(this.reverse(),arguments)}};b.prototype=new a}()
--------------------------------------------------------------------------------
/build/overload-min.js:
--------------------------------------------------------------------------------
1 | (function(){var Overload={};if(typeof module!="undefined"&&module.exports)module.exports=Overload;else if(typeof YUI!="undefined"&&YUI.add)YUI.add("overload",function(a){a.Overload=Overload},"1.0.6",{requires:[]});else if(typeof window=="object")window.Overload=Overload;else return;var copySignature=function(a){var b=a.slice(0);a.more&&(b.more=!0);return b},parseSignature=function(signature){if(signature.replace(/(^\s+|\s+$)/ig,"")==="")signature=[];else{signature=signature.split(",");for(var i=0;if.length?(c=!0,e=copySignature(e),e.length=f.length):e.length0?c=!0:h<0&&(d=!0)}return c&&!d?1:!c&&d?-1:0},matchSignature=function(a,b){if(a.lengthb.length&&!b.more)return!1;for(var c=0;c0?b[b.length-1]:null}},e=function(){var a=d(arguments);if(a){var b=Array.prototype.slice.call(arguments,0);if(a.signature.more){var c=b.splice(a.signature.length);b.push(c)}return a["function"].apply(this,b)}throw"cannot select a proper overload"};e.match=c,e.select=d,e.add=function(a,c){if(a instanceof Array)a=copySignature(a);else if(a.constructor==String)a=parseSignature(a);else throw"signature is neither a string nor an array";for(var d=0;d signature2.length) {
84 | signature1Better = true;
85 | signature1 = copySignature(signature1);
86 | signature1.length = signature2.length;
87 | } else if (signature1.length < signature2.length) {
88 | signature2Better = true;
89 | signature2 = copySignature(signature2);
90 | signature2.length = signature1.length;
91 | }
92 | }
93 | for (var i = 0; i < signature1.length; i++) {
94 | var comparison = inheritanceComparator(signature1[i], signature2[i]);
95 | if (comparison > 0) {
96 | signature1Better = true;
97 | } else if (comparison < 0) {
98 | signature2Better = true;
99 | }
100 | }
101 | if (signature1Better && !signature2Better) {
102 | return 1;
103 | } else if (!signature1Better && signature2Better) {
104 | return -1;
105 | } else {
106 | /* if both signatures are better in some way it means a conflict */
107 | return 0;
108 | }
109 | };
110 |
111 | var matchSignature = function(argumentsArray, signature) {
112 | if (argumentsArray.length < signature.length) {
113 | return false;
114 | } else if (argumentsArray.length > signature.length && !signature.more) {
115 | return false;
116 | }
117 | for (var i = 0; i < signature.length; i++) {
118 | if (!(argumentsArray[i] === null
119 | || argumentsArray[i] === undefined
120 | || signature[i] == Overload.Any
121 | || argumentsArray[i] instanceof signature[i]
122 | || argumentsArray[i].constructor == signature[i])) {
123 | return false;
124 | }
125 | }
126 | return true;
127 | };
128 |
129 | Overload.create = function(overloadsArray) {
130 | var overloads = [];
131 |
132 | var match = function(argumentsArray) {
133 | var matches = [];
134 | for (var i = 0; i < overloads.length; i++) {
135 | if (matchSignature(argumentsArray, overloads[i].signature)) {
136 | matches.push(overloads[i]);
137 | }
138 | }
139 | return matches;
140 | };
141 |
142 | var select = function(argumentsArray) {
143 | var matches = match(argumentsArray);
144 | switch (matches.length) {
145 | case 0:
146 | return null;
147 | case 1:
148 | return matches[0];
149 | default:
150 | matches = matches.sort(overloadComparator);
151 | if (overloadComparator(matches[matches.length - 1], matches[matches.length - 2]) > 0) {
152 | return matches[matches.length - 1];
153 | } else {
154 | return null;
155 | }
156 | }
157 | };
158 |
159 | var overloaded = function() {
160 | var overload = select(arguments);
161 | if (overload) {
162 | var transformedArguments = Array.prototype.slice.call(arguments, 0);
163 | if (overload.signature.more) {
164 | var moreArguments = transformedArguments.splice(overload.signature.length);
165 | transformedArguments.push(moreArguments);
166 | }
167 | return overload['function'].apply(this, transformedArguments);
168 | } else {
169 | throw "cannot select a proper overload";
170 | }
171 | };
172 |
173 | overloaded.match = match;
174 |
175 | overloaded.select = select;
176 |
177 | overloaded.add = function(signature, overload) {
178 | if (signature instanceof Array) {
179 | signature = copySignature(signature);
180 | } else if (signature.constructor == String) {
181 | signature = parseSignature(signature);
182 | } else {
183 | throw "signature is neither a string nor an array";
184 | }
185 | for (var i = 0; i < signature.length; i++) {
186 | if (!(signature[i] instanceof Function)) {
187 | throw "argument type should be a function";
188 | }
189 | if (i < signature.length - 1 && signature[i] == Overload.More) {
190 | throw "arguments type cannot be used in any argument except the last one";
191 | }
192 | }
193 | if (signature[signature.length - 1] == Overload.More) {
194 | signature.length = signature.length - 1;
195 | signature.more = true;
196 | }
197 | overloads.push({
198 | "signature": signature,
199 | "function": overload
200 | });
201 | return this;
202 | };
203 |
204 | return overloaded;
205 | };
206 |
207 | Overload.add = function(signature, overload) {
208 | return Overload.create().add(signature, overload);
209 | };
210 |
211 | Overload.Any = function any() {
212 | throw "this type is only an identifier and should not be instantiated";
213 | };
214 |
215 | Overload.More = function more() {
216 | throw "this type is only an identifier and should not be instantiated";
217 | };
218 | })();
219 |
--------------------------------------------------------------------------------
/doc/build.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const showdown = new (require('showdown').converter)();
4 | const mustache = require('mustache');
5 |
6 | const docRoot = '.';
7 | const templateFileName = path.join(docRoot, '/template.mustache');
8 |
9 | var template = String(fs.readFileSync(templateFileName));
10 |
11 | var buildIndex = function(html) {
12 | var headings = [];
13 | html = html.replace(/\\/gm, function(wholeMatch, match1, match2) {
14 | headings.push({
15 | level: parseInt(match1),
16 | name: match2
17 | });
18 | return ''+ match2 + '';
19 | });
20 |
21 | var headingStack = [];
22 | var headingPointer = headingStack;
23 | var headingLevel = 0;
24 | for (var i = 0; i < headings.length; i++) {
25 | while (headings[i].level < headingLevel) {
26 | headingPointer = headingPointer.parent;
27 | headingLevel--;
28 | }
29 | if (headings[i].level == headingLevel) {
30 | var siblingHeading = [];
31 | headingPointer.parent.push(siblingHeading);
32 | siblingHeading.parent = headingPointer.parent;
33 | headingPointer = siblingHeading;
34 | }
35 | while (headings[i].level > headingLevel) {
36 | var childHeading = [];
37 | headingPointer.push(childHeading);
38 | childHeading.parent = headingPointer;
39 | headingPointer = childHeading;
40 | headingLevel++;
41 | }
42 | headingPointer.name = headings[i].name;
43 | }
44 | return buildIndexHTML(headingStack);
45 | };
46 |
47 | var buildIndexHTML = function(stack) {
48 | var html = '
';
49 | for (var i = 0; i < stack.length; i++) {
50 | var heading = stack[i];
51 | html += '
' + stack[i].name + '';
52 | if (stack[i].length > 0) {
53 | html += buildIndexHTML(stack[i]);
54 | }
55 | html += '
';
56 | }
57 | html += '
';
58 | return html;
59 | };
60 |
61 | var processContent = function(html) {
62 | return html.replace(/\(.*?)\<\/h[\d]\>/gm, function(wholeMatch, match1, match2) {
63 | return ''+ match2 + '';
64 | }).replace(/\/gm, function(wholeMatch, match1) {
65 | return '';
66 | });
67 | };
68 |
69 | var processDirectory = function(directory) {
70 | console.log('change directory to: ' + directory);
71 | var fileNames = fs.readdirSync(directory);
72 | for (var i = 0; i < fileNames.length; i++) {
73 | var fileName = fileNames[i];
74 | var fileFullName = path.join(directory, fileName);
75 | if (fileName.match(/^\./)) {
76 | continue;
77 | }
78 | if (fs.statSync(fileFullName).isDirectory()) {
79 | processDirectory(fileFullName);
80 | } else if (fileName.match(/\.md$/)) {
81 | console.log('read file: ' + fileFullName);
82 | var fileContent = String(fs.readFileSync(fileFullName));
83 | var directoryDepth = (directory == '.') ? 0 : directory.split('/').length + 1;
84 | var relativePath = new Array(directoryDepth).join('../');
85 | var htmlContent = processContent(showdown.makeHtml(fileContent));
86 | var indexContent = buildIndex(htmlContent);
87 | var convertedFileContent = mustache.to_html(template, {
88 | title: fileContent.match(/# (.*)/) ? fileContent.match(/# (.*)/)[1] : 'jsHelpers',
89 | stylesheets: [
90 | path.join(relativePath, 'stylesheets/default.css'),
91 | path.join(relativePath, 'stylesheets/shCore.css'),
92 | path.join(relativePath, 'stylesheets/shThemeDefault.css')
93 | ],
94 | javascripts: [
95 | path.join(relativePath, 'javascripts/xregexp.js'),
96 | path.join(relativePath, 'javascripts/shCore.js'),
97 | path.join(relativePath, 'javascripts/shBrushJScript.js'),
98 | path.join(relativePath, 'javascripts/page.js')
99 | ],
100 | index: indexContent,
101 | content: htmlContent
102 | });
103 | var convertedFileFullName = fileFullName.replace(/\.md$/, '.html');
104 | if (path.existsSync(convertedFileFullName)) {
105 | console.log('delete file: ' + convertedFileFullName);
106 | fs.unlinkSync(convertedFileFullName);
107 | }
108 | console.log('write file: ' + convertedFileFullName);
109 | fs.writeFileSync(convertedFileFullName, convertedFileContent);
110 | }
111 | }
112 | };
113 |
114 | processDirectory(docRoot);
115 |
--------------------------------------------------------------------------------
/doc/en/async/features.md:
--------------------------------------------------------------------------------
1 | # Async Features
2 |
3 | Async is a component for the unification of JavaScript asynchronous programming style. It let all asynchronous functions return instances of Async.Operation and they don't have to handle callback functions themselves.
4 |
5 | ## Async.Operation
6 |
7 | A class represents an asynchronous operation. Every asynchronous operation should instantiate an Async.Operation object.
8 |
9 | /* jQuery required for this sample */
10 |
11 | var getAsync = function(url, data) {
12 | var operation = new Async.Operation();
13 | $.get(url, data, function(result) { operation.yield(result); }, "json");
14 | return operation;
15 | };
16 |
17 | var getOperation = getAsync("/ping", "")
18 | getOperation.addCallback(function(result) { alert("ping returns: " + result); });
19 |
20 | ### Async.Operation.result
21 |
22 | * type: instance
23 |
24 | Result of the asynchronous operation. It should be undefinied before yield is called. It should be the same value as the argument of yield after yield is called.
25 |
26 | ### Async.Operation.state
27 |
28 | * type: instance
29 |
30 | State of the asynchronous operation. It should be "running" before yield is called. It should be "completed" after yield is called.
31 |
32 | ### Async.Operation.completed
33 |
34 | * type: instance
35 |
36 | Indicator of whether the asynchronous operation is completed. It should be false before yield is called. It should be true after yield is called.
37 |
38 | ### Async.Operation.yield()
39 |
40 | * type: instance
41 | * input:
42 | * value (optional)
43 | * output: this : Operation
44 |
45 | Return value for a specific asynchronous operation.
46 |
47 | var waitAsync = function(delay) {
48 | var operation = new Async.Operation();
49 | setTimeout(function() {
50 | var result = "You have waited " + delay + " millisecond(s)."
51 | operation.yield(result);
52 | }, delay);
53 | return operation;
54 | }
55 |
56 | ### Async.Operation.addCallback()
57 |
58 | * type: instance
59 | * input:
60 | * callback : Function
61 | * output: this : Operation
62 |
63 | Add callback function to a specific asynchronous operation. These callback functions will be executed one by one and they will receive one argument as the result of the asynchronous operation.
64 |
65 | var waitOperation = wait(999);
66 | waitOperation.addCallback(function(result) { alert(result); });
67 |
68 | ## Async.chain()
69 |
70 | * type: static
71 | * input: none
72 | * output: chain : Operation
73 |
74 | Create an asynchronous execution queue.
75 |
76 | Async
77 | .chain()
78 | .next(firstFunction)
79 | .next(secondFunction)
80 | .next(thirdFunction)
81 | .go();
82 |
83 | ### Async.chain().result
84 |
85 | * type: instance
86 |
87 | Result of the asynchronous operation. It should be undefined before go is called. It should be the result of the last executed function in the queue after go is called.
88 |
89 | ### Async.chain().state
90 |
91 | * type: instance
92 |
93 | State of the asynchronous operation. It should be "waiting" before go is called. It should be "chain running" after go is called. It should be "completed" when all functions in the queue are executed.
94 |
95 | ### Async.chain().completed
96 |
97 | * type: instance
98 |
99 | Indicator of whether the asynchronous operation is completed. It should be false before all functions in the queue are executed. It should be true after that.
100 |
101 | ### Async.chain().next()
102 |
103 | * type: instance
104 | * input:
105 | * function : Function
106 | * output: this : Operation
107 |
108 | Append a function to the queue. If the function returns an instance of Async.Operation, the queue will wait for the asynchronous operation inside this function to be finished before moving on to next function in the queue. Otherwise, next function in the queue is executed right after this function. In both situations, the function will receive one argument when it's call. This argument will be the argument of go if the function is the first in the queue. Otherwise, it's result of the previous function in the queue. In both situations, the result of this function will be passed on to next function in the queue as an argument.
109 |
110 | var plusOne = function(i) {
111 | return i + 1;
112 | };
113 |
114 | var plusOneAsync = function(i) {
115 | var operation = new Async.Operation();
116 | setTimeout(function() { operation.yield(i + 1); }, 1000);
117 | return operation;
118 | };
119 |
120 | Async
121 | .chain()
122 | .next(plusOne)
123 | .next(plusOneAsync)
124 | .next(function(i) { alert(i); })
125 | .go(0);
126 |
127 | ### Async.chain().go()
128 |
129 | * type: instance
130 | * input:
131 | * value (optional)
132 | * output: this : Operation
133 |
134 | Start the functions queue. If go is called with one argument, this argument will be the argument of the first function in the queue. Appending more functions to the queue by calling next is still possible after go is called.
135 |
136 | Async
137 | .chain()
138 | .next(firstFunction)
139 | .next(secondFunction)
140 | .go()
141 | .next(thirdFunction);
142 |
143 | ### Async.chain().wait()
144 |
145 | * type: instance
146 | * input:
147 | * delay : Number
148 | * output: this : Operation
149 |
150 | Let the functions queue wait in milliseconds. It will let the result of the previous function pass through and the next function will receive it as an argument.
151 |
152 | Async
153 | .chain()
154 | .next(firstFunction)
155 | .wait(999)
156 | .next(secondFunction)
157 | .go();
158 |
159 | ## Helpers
160 |
161 | Below are the helper functions besides Async's main functions.
162 |
163 | ### Async.go()
164 |
165 | * type: static
166 | * input:
167 | * value (optional)
168 | * output: operation : Operation
169 |
170 | Create an asynchronous function queue and start the queue imidiately. If an argument is passed to go, it will be passed to the first function in the queue.
171 |
172 | var plusOne = function(i) {
173 | return i + 1;
174 | };
175 |
176 | var plusOneAsync = function(i) {
177 | var operation = new Async.Operation();
178 | setTimeout(function() { operation.yield(i + 1); }, 1000);
179 | return operation;
180 | };
181 |
182 | Async
183 | .go(0)
184 | .next(plusOne)
185 | .next(plusOneAsync)
186 | .next(function(i) { alert(i); });
187 |
188 | ### Async.collect()
189 |
190 | * type: static
191 | * input:
192 | * functions : Array
193 | * functionArguments : Array (optional)
194 | * output: operation : Operation
195 |
196 | Create an asynchronous operation containing a set of parallel child operations. The operation will call its callbacks when all child operations are completed.
197 |
198 | var plusOne = function(i) {
199 | return i + 1;
200 | };
201 |
202 | var plusOneAsync = function(i) {
203 | var operation = new Async.Operation();
204 | setTimeout(function() { operation.yield(i + 1); }, 1000);
205 | return operation;
206 | };
207 |
208 | var parallelOperation = Async
209 | .collect([
210 | plusOne,
211 | plusOneAsync
212 | ], [99, 100]);
213 | parallelOperation.addCallback(function(results) { alert(results); });
214 |
215 | ### Async.wait()
216 |
217 | * type: static
218 | * input:
219 | * delay : Number
220 | * value (optional)
221 | * output: operation : Operation
222 |
223 | Wait in millisenconds as an asynchronous operation. If the second argument is assigned, callback functions will receive it as an argument.
224 |
225 | var waitOperation = Async.wait(999, "predefined result");
226 | waitOperation.addCallback(function(result) { alert(result); });
227 |
228 | ### Async.instant()
229 |
230 | * type: static
231 | * input:
232 | * value (optional)
233 | * output: operation : Operation
234 |
235 | Generate an instant yielded asynchronous operation. If an argument is given, it will be passed to the callback functions.
236 |
237 | var instantOperation = Async.instant("predefined result");
238 | instantOperation.addCallback(function(result) { alert(result); });
239 |
240 | ### Function.prototype.asyncCall()
241 |
242 | * type: instance
243 | * input:
244 | * context
245 | * values : Params (optional)
246 | * output: operation : Operation
247 |
248 | Call function in an asynchronous manner. It works like Function.prototype.call() and returns an instance of Async.Operation.
249 |
250 | var sayHello = function(name) { return "Hello, " + name; };
251 | sayHello
252 | .asyncCall(this, "Cat")
253 | .addCallback(function(result) { alert(result); });
254 |
255 | ### Function.prototype.asyncApply()
256 |
257 | * type: instance
258 | * input:
259 | * context
260 | * values : Array (optional)
261 | * output: operatoin : Operation
262 |
263 | Call function in an asynchronous manner. It works like Function.prototype.apply() and returns an instance of Async.Operation.
264 |
265 | var sayHello = function(name) { return "Hello, " + name; };
266 | sayHello
267 | .asyncApple(this, ["Cat"])
268 | .addCallback(function(result) { alert(result); });
269 |
--------------------------------------------------------------------------------
/doc/en/central/features.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Central Features
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
129 |
133 |
143 |
144 |
--------------------------------------------------------------------------------
/doc/en/central/features.md:
--------------------------------------------------------------------------------
1 | # Central Features
2 |
3 | Central is a JavaScript component for central event dispatcher.
4 |
5 | ## Central
6 |
7 | A static class for listening to and dispatching events.
8 |
9 | Central.listen("move", function(e) {
10 | element.style.left = parseInt(element.style.left) + e.x + "px";
11 | element.style.top = parseInt(element.style.top) + e.y + "px";
12 | });
13 |
14 | Central.call("move", {
15 | x: 10,
16 | y: -20
17 | });
18 |
19 | ### Central.listen()
20 |
21 | * type: static
22 | * input:
23 | * command : String
24 | * handler : Function
25 | * output: Central
26 |
27 | Listen to an event by name.
28 |
29 | Central.listen("move", function(e) {
30 | element.style.left = parseInt(element.style.left) + e.x + "px";
31 | element.style.top = parseInt(element.style.top) + e.y + "px";
32 | });
33 |
34 | ### Central.call()
35 |
36 | * type: static
37 | * input:
38 | * command : String
39 | * argument : Object
40 | * output: Central
41 |
42 | Dispatch event by name.
43 |
44 | Central.call("move", {
45 | x: 10,
46 | y: -20
47 | });
48 |
49 | ## Central.extend()
50 |
51 | * type: static
52 | * input: target : Object
53 | * output: target : Object
54 |
55 | Extend an object and it will have the functionalities of Central.
56 |
57 | ### Central.extend().listen()
58 |
59 | * type: instance
60 | * input:
61 | * command : String
62 | * handler : Function
63 | * output: this
64 |
65 | Listen to an event by name.
66 |
67 | var controller = new Controller();
68 |
69 | Central.extend(controller);
70 |
71 | controller.listen("move", function(e) {
72 | element.style.left = parseInt(element.style.left) + e.x + "px";
73 | element.style.top = parseInt(element.style.top) + e.y + "px";
74 | });
75 |
76 | ### Central.extend().call()
77 |
78 | * type: instance
79 | * input:
80 | * command : String
81 | * argument : Object
82 | * output: this
83 |
84 | Dispatch event by name.
85 |
86 | var controller = new Controller();
87 |
88 | Central.extend(controller);
89 |
90 | controller.call("move", {
91 | x: 10,
92 | y: -20
93 | });
94 |
--------------------------------------------------------------------------------
/doc/en/index.md:
--------------------------------------------------------------------------------
1 | # jsHelpers
2 |
3 | jsHelpers are several JavaScript components for composing complicated AJAX applications. They are designed to help you write in declarative manner via internal domain specific language. You only need to describe the problem in a proper way and the component will manage the process of solving the problem.
4 |
5 | ## Async
6 |
7 |
8 | * Features
9 |
10 |
11 | If your application involves a lot of AJAX operation, Aysnc might help you simplify your code and interface and let you write asynchronous code in synchronous way. You can describe AJAX operation queue in a declarative way.
12 |
13 | You can encapsulate fundamental AJAX operation with Async and use Async.Operation to manage every AJAX operation within your application. In the following example, all functions with "Async" suffix return an instance of Async.Operation. Asynchronous callee doesn't need to accept callback function in arguments and doesn't need to invoke callback function explicitly. Asynchronous function returns like any synchronous function does and only those who care about asynchronous result need to retrieve the result.
14 |
15 | /* jQuery required for this sample */
16 |
17 | var getAsync = function(url, data) {
18 | var operation = new Async.Operation();
19 | $.get(url, data, function(result) { operation.yield(result); }, "json");
20 | return operation;
21 | };
22 |
23 | var plusAsync = function(x, y) {
24 | return getAsync("/plus", "x=" + x + "&y=" + y);
25 | };
26 |
27 | var minusAsync = function(x, y) { /* implementation */ };
28 |
29 | var multiplyAsync = function(x, y) { /* implementation */ };
30 |
31 | var divideAsync = function(x, y) { /* implementation */ };
32 |
33 | var calculateAsync = function(operand, x, y) {
34 | switch operand {
35 | case "+":
36 | return plusAsync(x, y);
37 | case "-":
38 | return minusAsync(x, y);
39 | case "*":
40 | return multiplyAsync(x, y);
41 | case "/":
42 | return divideAsync(x, y);
43 | }
44 | }
45 |
46 | calculateAsync("+", 99, 1)
47 | .addCallback(function(result) { alert(result); });
48 |
49 | ## Overload
50 |
51 |
52 | * Features
53 |
54 |
55 | You can utilize Overload and simplify function overloading signature description if your application has overloaded functions.
56 |
57 | In JavaScript, overloaded functions with the same name must be implemented with one function and this function need to decide which overloaded function is called within itself. Pitfall of such approach is that you have to maintain hard-coded dispatchers within each overloaded functions. All these dispatchers have something in common but code reuse is impossible.
58 |
59 | var User = function(name) { this.name = name; };
60 |
61 | var sayHello = Overload
62 | .add("String",
63 | function(string) { alert("Hello, " + string); })
64 | .add([User],
65 | function(user) { sayHello(user.name) })
66 |
67 | ## Central
68 |
69 |
70 | * Features
71 |
72 |
73 | If your application is composed of a lot of modules, you would like to decouple all these modules. One way to do so is rewriting method invoking with event dispatching.
74 |
75 | For example: If methods of module A invoke methods of module B, A depends on B. If it's rewritten as event dispatched by A being listened, A doens't depend on B but B depends on A. In order to remove dependency between A and B, we can use a Central event dispatcher instead. A dispatches event via Central event dispatcher and B listens to Central event dispatcher.
76 |
77 | Central event dispatcher distinguish events by their names. One event name represents one event, no matter which module calls of listens to.
78 |
79 | /* jQuery required for this sample */
80 |
81 | var mapController = new (function() {
82 | var mapOffset = { x: 0, y: 0 };
83 | var redrawMap = function() { /* implementation */ };
84 |
85 | Central.listen("mapmove", function(event) {
86 | mapOffset.x += event.x;
87 | mapOffset.y += event.y;
88 | redrawMap();
89 | });
90 | })();
91 |
92 | var keyboardListener = new (function() {
93 | var LEFT_ARROW = 37,
94 | RIGHT_ARROW = 39,
95 | UP_ARROW = 38,
96 | DOWN_ARROW = 40;
97 |
98 | $(document).keypress(function(event) {
99 | switch (event.which) {
100 | case LEFT_ARROW:
101 | Central.call("mapmove", { x: -10, y: 0 });
102 | break;
103 | case RIGHT_ARROW:
104 | Central.call("mapmove", { x: 10, y: 0 });
105 | break;
106 | case UP_ARROW:
107 | Central.call("mapmove", { x: 0, y: 10 });
108 | break;
109 | case DOWN_ARROW:
110 | Central.call("mapmove", { x: 0, y: -10 });
111 | break;
112 | }
113 | });
114 | })();
115 |
116 | var mouseListener = new (function() {
117 | /* monitor mouse events and dispatch move event in Central */
118 | /* this module won't be loaded on touch screen devices */
119 | })();
120 |
121 | var touchListener = new (function() {
122 | /* monitor touch events and dispatch move event in Central */
123 | /* this module will be loaded on touch screen devices */
124 | })();
125 |
126 | ## GrandCentral
127 |
128 |
129 | * Features
130 |
131 |
132 | If your application has an event source and it needs to dispatch events based on event data, GrandCentral can help you with this. All you need to do is writing patterns for matching in JSON.
133 |
134 | For example: Your application watches to all AJAX operations and decide how to react based on JSON in response. In this case, you can write handlers for each types of reaction and use GrandCentral to pattern match JSON response and to call the right handler.
135 |
136 | /* jQuery required for this sample */
137 |
138 | $(document).ajaxComplete(function(event, xhr, settings) {
139 | var response = {
140 | status: xhr.status,
141 | json: $.parseJSON(xhr.responseText)
142 | };
143 | GrandCentral.call(response);
144 | };
145 |
146 | var chatMessageController = new (function() {
147 | var renderChatMessage = function(message) { /* implementation */ };
148 |
149 | GrandCentral.listen({
150 | status: 200,
151 | json: {
152 | command: "message"
153 | }
154 | }, function(response) {
155 | renderChatMessage(response.json.message);
156 | });
157 | })();
158 |
159 | var systemNotificationController = new (function() {
160 | var renderSystemNotification = function(notification) { /* implementation */ };
161 |
162 | GrandCentral.listen({
163 | status: 200,
164 | json: {
165 | command: "notification"
166 | }
167 | }, function(response) {
168 | renderSystemNotification(response.json.notification);
169 | });
170 | })();
171 |
172 | var errorController = new (function() {
173 | var displayErrorMessage = function() { /* implementation */ };
174 |
175 | GrandCentral.listen({
176 | status$ne: 200
177 | }, function(response) {
178 | displayErrorMessage();
179 | });
180 | })();
181 |
182 | ## List
183 |
184 |
185 | * Features
186 |
187 |
188 | JavaScript built-in Array seems to be not enough if you need to process a lot of data in the form of 1-dimension set. You would like to use Haskell List in this case and this List component for JavaScript is a substitute. List encapsulates Array, provides Haskell List style methods, and simplify your work on data processing.
189 |
190 | var list1 = new List(1, 2, 3, 4, 5, 4, 3, 2, 1, 0);
191 |
192 | var list2 = list1.filter(function(i) { return i > 2; });
193 | assert(list2.toArray() == [3, 4, 5, 4, 3]);
194 |
195 | var list3 = list2.map(function(i) { return i * i; });
196 | assert(list3.toArray() == [9, 16, 25, 16, 9]);
197 |
198 | var list4 = List.iterate(function(i) { return i * 2; }, 1);
199 |
200 | var list5 = list4.take(10);
201 | assert(list5.toArray() == [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]);
202 |
203 | var list6 = list4
204 | .map(function(i) { return i - 1; })
205 | .dropWhile(function(i) { return i < 100; })
206 | .takeWhile(function(i) { return i < 1000; });
207 | assert(list6.toArray() == [127, 255, 511]);
208 |
--------------------------------------------------------------------------------
/doc/en/overload/features.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Overload Features
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
Overload is for creating overloaded functions in JavaScript in a simpler and better way. It let you describe the signature of different functions and relieve you of handling signatures recognition in your function.
If the signature of an overloaded function includes classes that can't be evaluate in the global scope via eval, the signature could be passed as an Array. In this case, "*" is replaced by Overload.Any and "..." is replaced by Overload.More.
If classes in signatures of overloaded functions have inheritance relationship, Overload will choose the only best match. If there's no only best best, an error will be thrown.