├── .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 | 19 |
20 |

Central Features

21 | 22 |

Central is a JavaScript component for central event dispatcher.

23 | 24 |

Central

25 | 26 |

A static class for listening to and dispatching events.

27 | 28 |
Central.listen("move", function(e) {
 29 |     element.style.left = parseInt(element.style.left) + e.x + "px";
 30 |     element.style.top = parseInt(element.style.top) + e.y + "px";
 31 | });
 32 | 
 33 | Central.call("move", {
 34 |     x: 10,
 35 |     y: -20
 36 | });
 37 | 
38 | 39 |

Central.listen()

40 | 41 |
    42 |
  • type: static
  • 43 |
  • input: 44 |
    • command : String
    • 45 |
    • handler : Function
  • 46 |
  • output: Central
  • 47 |
48 | 49 |

Listen to an event by name.

50 | 51 |
Central.listen("move", function(e) {
 52 |     element.style.left = parseInt(element.style.left) + e.x + "px";
 53 |     element.style.top = parseInt(element.style.top) + e.y + "px";
 54 | });
 55 | 
56 | 57 |

Central.call()

58 | 59 |
    60 |
  • type: static
  • 61 |
  • input: 62 |
    • command : String
    • 63 |
    • argument : Object
  • 64 |
  • output: Central
  • 65 |
66 | 67 |

Dispatch event by name.

68 | 69 |
Central.call("move", {
 70 |     x: 10,
 71 |     y: -20
 72 | });
 73 | 
74 | 75 |

Central.extend()

76 | 77 |
    78 |
  • type: static
  • 79 |
  • input: target : Object
  • 80 |
  • output: target : Object
  • 81 |
82 | 83 |

Extend an object and it will have the functionalities of Central.

84 | 85 |

Central.extend().listen()

86 | 87 |
    88 |
  • type: instance
  • 89 |
  • input: 90 |
    • command : String
    • 91 |
    • handler : Function
  • 92 |
  • output: this
  • 93 |
94 | 95 |

Listen to an event by name.

96 | 97 |
var controller = new Controller();
 98 | 
 99 | Central.extend(controller);
100 | 
101 | controller.listen("move", function(e) {
102 |     element.style.left = parseInt(element.style.left) + e.x + "px";
103 |     element.style.top = parseInt(element.style.top) + e.y + "px";
104 | });
105 | 
106 | 107 |

Central.extend().call()

108 | 109 |
    110 |
  • type: instance
  • 111 |
  • input: 112 |
    • command : String
    • 113 |
    • argument : Object
  • 114 |
  • output: this
  • 115 |
116 | 117 |

Dispatch event by name.

118 | 119 |
var controller = new Controller();
120 | 
121 | Central.extend(controller);
122 | 
123 | controller.call("move", {
124 |     x: 10,
125 |     y: -20
126 | });
127 | 
128 |
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 | 19 |
20 |

Overload Features

21 | 22 |

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.

23 | 24 |

Overload

25 | 26 |

A static class for creating overloaded functions.

27 | 28 |
var sum = Overload
 29 |     .add("Number, Number",
 30 |         function(x, y) { return x + y; })
 31 |     .add("Number, Number, Number",
 32 |         function(x, y, z) { return x + y + z; });
 33 | 
 34 | alert(sum(1, 2));
 35 | alert(sum(3, 4, 5));
 36 | 
37 | 38 |

Overload.add()

39 | 40 |
    41 |
  • type: static and instance
  • 42 |
  • input: 43 |
    • types : String or Array
    • 44 |
    • handler : Function
  • 45 |
  • output: overloaded : Function
  • 46 |
47 | 48 |

Create an overloaded function entrance and add the first function or add a function to the existed overloaded function entrance.

49 | 50 |
var concatenate = Overload
 51 |     .add("String, String"),
 52 |         function(s1, s2) { return s1 + s2; })
 53 |     .add("String, String, String"),
 54 |         function(s1, s2, s3) { return s1 + s2 + s3; });
 55 | 
 56 | concatenate
 57 |     .add("Array",
 58 |     function(array) { return array.join(""); })
 59 |     .add("Array, String",
 60 |         function(array, separator) { return array.join(separator); });
 61 | 
 62 | alert(concatenate("hello", " ", "world"));
 63 | alert(concatenate([1, 2, 3], " + "));
 64 | 
65 | 66 |

Any Argument

67 | 68 |

"*" represents an argument which matches any type of variable.

69 | 70 |
var add = Overload
 71 |     .add("*, *",
 72 |         function(x, y) { return x + y; })
 73 |     .add("*, *, *",
 74 |         function(x, y, z) { return x + y + z; });
 75 | 
 76 | alert(add(1, 2, 3));
 77 | alert(add("hello", " ", "world"));
 78 | 
79 | 80 |

More Argument

81 | 82 |

"..." represents arguments of any number.

83 | 84 |
var sum = Overload
 85 |     .add("Number",
 86 |         function(x) { return x; })
 87 |     .add("Number, Number",
 88 |         function(x, y) { return x + y; })
 89 |     .add("Number, Number, Number",
 90 |         function(x, y, z) { return x + y + z; })
 91 |     .add("Number, Number, Number, ...",
 92 |         function(x, y, z, more) {
 93 |             return x + y + z + sum.apply(this, more);
 94 |         });
 95 | 
 96 | alert(sum(1, 2));
 97 | alert(sum(1, 2, 3));
 98 | alert(sum(1, 2, 3, 4, 5, 6));
 99 | 
100 | 101 |

Internal Class

102 | 103 |

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.

104 | 105 |
var User = function(name) { this.name = name; };
106 | 
107 | var sayHello = Overload
108 |     .add("String",
109 |         function(string) { alert("Hello, " + string); }) 
110 |     .add("String, String",
111 |         function(string1, string2) { sayHello(string1 + " and " + string2); }) 
112 |     .add([User],
113 |         function(user) { sayHello(user.name); })
114 |     .add([User, User],
115 |         function(user1, user2) { sayHello(user1.name, user2.name); })
116 |     .add([Overload.Any],
117 |         function(object) { sayHello(object.toString()); })
118 |     .add([Overload.More],
119 |         function(objects) { sayHello(objects.join(" & ")); });
120 | 
121 | sayHello("World");
122 | sayHello(new User("Cat"), new User("Erik"));
123 | sayHello(1, 2, 3, 4, 5);
124 | 
125 | 126 |

Class Inheritance Resolution

127 | 128 |

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.

129 | 130 |
var Parent = function() {};
131 | var Child = function() {};
132 | Child.prototype = new Parent();
133 | 
134 | var selectClass = Overload
135 |     .add([Parent],
136 |         function(parent) { return "[Parent]"; })
137 |     .add([Child],
138 |         function(child) { return "[Child]"; })
139 |     .add([Parent, Child],
140 |         function(parent, child) { return "[Parent, Child]"; })
141 |     .add([Child, Parent],
142 |         function(child, parent) { return "[Child Parent]"; });
143 | 
144 | alert(selectClass(new Parent()));
145 | alert(selectClass(new Child()));
146 | try {
147 |     alert(selectClass(new Parent(), new Parent()));
148 | } catch (e) {
149 |     alert (e);
150 | }
151 | try {
152 |     alert(selectClass(new Child(), new Child()));
153 | } catch (e) {
154 |     alert (e);
155 | }
156 | 
157 |
158 | 162 | 172 | 173 | -------------------------------------------------------------------------------- /doc/en/overload/features.md: -------------------------------------------------------------------------------- 1 | # Overload Features 2 | 3 | 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. 4 | 5 | ## Overload 6 | 7 | A static class for creating overloaded functions. 8 | 9 | var sum = Overload 10 | .add("Number, Number", 11 | function(x, y) { return x + y; }) 12 | .add("Number, Number, Number", 13 | function(x, y, z) { return x + y + z; }); 14 | 15 | alert(sum(1, 2)); 16 | alert(sum(3, 4, 5)); 17 | 18 | ### Overload.add() 19 | 20 | * type: static and instance 21 | * input: 22 | * types : String or Array 23 | * handler : Function 24 | * output: overloaded : Function 25 | 26 | Create an overloaded function entrance and add the first function or add a function to the existed overloaded function entrance. 27 | 28 | var concatenate = Overload 29 | .add("String, String"), 30 | function(s1, s2) { return s1 + s2; }) 31 | .add("String, String, String"), 32 | function(s1, s2, s3) { return s1 + s2 + s3; }); 33 | 34 | concatenate 35 | .add("Array", 36 | function(array) { return array.join(""); }) 37 | .add("Array, String", 38 | function(array, separator) { return array.join(separator); }); 39 | 40 | alert(concatenate("hello", " ", "world")); 41 | alert(concatenate([1, 2, 3], " + ")); 42 | 43 | #### Any Argument 44 | 45 | "*" represents an argument which matches any type of variable. 46 | 47 | var add = Overload 48 | .add("*, *", 49 | function(x, y) { return x + y; }) 50 | .add("*, *, *", 51 | function(x, y, z) { return x + y + z; }); 52 | 53 | alert(add(1, 2, 3)); 54 | alert(add("hello", " ", "world")); 55 | 56 | #### More Argument 57 | 58 | "..." represents arguments of any number. 59 | 60 | var sum = Overload 61 | .add("Number", 62 | function(x) { return x; }) 63 | .add("Number, Number", 64 | function(x, y) { return x + y; }) 65 | .add("Number, Number, Number", 66 | function(x, y, z) { return x + y + z; }) 67 | .add("Number, Number, Number, ...", 68 | function(x, y, z, more) { 69 | return x + y + z + sum.apply(this, more); 70 | }); 71 | 72 | alert(sum(1, 2)); 73 | alert(sum(1, 2, 3)); 74 | alert(sum(1, 2, 3, 4, 5, 6)); 75 | 76 | #### Internal Class 77 | 78 | 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. 79 | 80 | var User = function(name) { this.name = name; }; 81 | 82 | var sayHello = Overload 83 | .add("String", 84 | function(string) { alert("Hello, " + string); }) 85 | .add("String, String", 86 | function(string1, string2) { sayHello(string1 + " and " + string2); }) 87 | .add([User], 88 | function(user) { sayHello(user.name); }) 89 | .add([User, User], 90 | function(user1, user2) { sayHello(user1.name, user2.name); }) 91 | .add([Overload.Any], 92 | function(object) { sayHello(object.toString()); }) 93 | .add([Overload.More], 94 | function(objects) { sayHello(objects.join(" & ")); }); 95 | 96 | sayHello("World"); 97 | sayHello(new User("Cat"), new User("Erik")); 98 | sayHello(1, 2, 3, 4, 5); 99 | 100 | #### Class Inheritance Resolution 101 | 102 | 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. 103 | 104 | var Parent = function() {}; 105 | var Child = function() {}; 106 | Child.prototype = new Parent(); 107 | 108 | var selectClass = Overload 109 | .add([Parent], 110 | function(parent) { return "[Parent]"; }) 111 | .add([Child], 112 | function(child) { return "[Child]"; }) 113 | .add([Parent, Child], 114 | function(parent, child) { return "[Parent, Child]"; }) 115 | .add([Child, Parent], 116 | function(child, parent) { return "[Child Parent]"; }); 117 | 118 | alert(selectClass(new Parent())); 119 | alert(selectClass(new Child())); 120 | try { 121 | alert(selectClass(new Parent(), new Parent())); 122 | } catch (e) { 123 | alert (e); 124 | } 125 | try { 126 | alert(selectClass(new Child(), new Child())); 127 | } catch (e) { 128 | alert (e); 129 | } 130 | -------------------------------------------------------------------------------- /doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jsHelpers 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 |
20 |

jsHelpers

21 | 22 |

Please select language: (English version isn't complete yet.)

23 | 24 |

English | Chinese

25 | 26 |

请选择语言:(英语版本尚未完成。)

27 | 28 |

中文 | 英文

29 |
30 | 34 | 44 | 45 | -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 | # jsHelpers 2 | 3 | Please select language: (English version isn't complete yet.) 4 | 5 | English | Chinese 6 | 7 | 请选择语言:(英语版本尚未完成。) 8 | 9 | 中文 | 英文 10 | -------------------------------------------------------------------------------- /doc/javascripts/page.js: -------------------------------------------------------------------------------- 1 | window.onload = function(e) { 2 | var html = document.body.innerHTML; 3 | 4 | var toggleTreeVisibility = window.toggleTreeVisibility = function() { 5 | var indexElement = document.getElementById('index'); 6 | var toggleElement = document.getElementById('toggle'); 7 | if (indexElement.className == 'collapsed') { 8 | indexElement.className = 'expanded'; 9 | toggleElement.innerHTML = 'Hide Index'; 10 | } else { 11 | indexElement.className = 'collapsed'; 12 | toggleElement.innerHTML = 'Show Index'; 13 | } 14 | }; 15 | }; -------------------------------------------------------------------------------- /doc/javascripts/shBrushJScript.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SyntaxHighlighter 3 | * http://alexgorbatchev.com/SyntaxHighlighter 4 | * 5 | * SyntaxHighlighter is donationware. If you are using it, please donate. 6 | * http://alexgorbatchev.com/SyntaxHighlighter/donate.html 7 | * 8 | * @version 9 | * 3.0.83 (July 02 2010) 10 | * 11 | * @copyright 12 | * Copyright (C) 2004-2010 Alex Gorbatchev. 13 | * 14 | * @license 15 | * Dual licensed under the MIT and GPL licenses. 16 | */ 17 | ;(function() 18 | { 19 | // CommonJS 20 | typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null; 21 | 22 | function Brush() 23 | { 24 | var keywords = 'break case catch continue ' + 25 | 'default delete do else false ' + 26 | 'for function if in instanceof ' + 27 | 'new null return super switch ' + 28 | 'this throw true try typeof var while with' 29 | ; 30 | 31 | var r = SyntaxHighlighter.regexLib; 32 | 33 | this.regexList = [ 34 | { regex: r.multiLineDoubleQuotedString, css: 'string' }, // double quoted strings 35 | { regex: r.multiLineSingleQuotedString, css: 'string' }, // single quoted strings 36 | { regex: r.singleLineCComments, css: 'comments' }, // one line comments 37 | { regex: r.multiLineCComments, css: 'comments' }, // multiline comments 38 | { regex: /\s*#.*/gm, css: 'preprocessor' }, // preprocessor tags like #region and #endregion 39 | { regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' } // keywords 40 | ]; 41 | 42 | this.forHtmlScript(r.scriptScriptTags); 43 | }; 44 | 45 | Brush.prototype = new SyntaxHighlighter.Highlighter(); 46 | Brush.aliases = ['js', 'jscript', 'javascript']; 47 | 48 | SyntaxHighlighter.brushes.JScript = Brush; 49 | 50 | // CommonJS 51 | typeof(exports) != 'undefined' ? exports.Brush = Brush : null; 52 | })(); 53 | -------------------------------------------------------------------------------- /doc/stylesheets/default.css: -------------------------------------------------------------------------------- 1 | body { 2 | _text-align: center; 3 | } 4 | #index { 5 | position: fixed; 6 | _position: absolute; 7 | z-index: 999; 8 | top: 20px; 9 | right: 20px; 10 | padding: 20px; 11 | border: 1px solid #999; 12 | background-color: #fff; 13 | opacity: 0.9; 14 | } 15 | #index #toggle { 16 | display: block; 17 | text-align: right; 18 | } 19 | #index.collapsed ul { 20 | display: none; 21 | } 22 | #index.expanded ul { 23 | display: block; 24 | } 25 | #content { 26 | position: relative; 27 | width: 800px; 28 | margin: auto; 29 | _text-align: left; 30 | } 31 | -------------------------------------------------------------------------------- /doc/stylesheets/shCore.css: -------------------------------------------------------------------------------- 1 | /** 2 | * SyntaxHighlighter 3 | * http://alexgorbatchev.com/SyntaxHighlighter 4 | * 5 | * SyntaxHighlighter is donationware. If you are using it, please donate. 6 | * http://alexgorbatchev.com/SyntaxHighlighter/donate.html 7 | * 8 | * @version 9 | * 3.0.83 (July 02 2010) 10 | * 11 | * @copyright 12 | * Copyright (C) 2004-2010 Alex Gorbatchev. 13 | * 14 | * @license 15 | * Dual licensed under the MIT and GPL licenses. 16 | */ 17 | .syntaxhighlighter a, 18 | .syntaxhighlighter div, 19 | .syntaxhighlighter code, 20 | .syntaxhighlighter table, 21 | .syntaxhighlighter table td, 22 | .syntaxhighlighter table tr, 23 | .syntaxhighlighter table tbody, 24 | .syntaxhighlighter table thead, 25 | .syntaxhighlighter table caption, 26 | .syntaxhighlighter textarea { 27 | -moz-border-radius: 0 0 0 0 !important; 28 | -webkit-border-radius: 0 0 0 0 !important; 29 | background: none !important; 30 | border: 0 !important; 31 | bottom: auto !important; 32 | float: none !important; 33 | height: auto !important; 34 | left: auto !important; 35 | line-height: 1.1em !important; 36 | margin: 0 !important; 37 | outline: 0 !important; 38 | overflow: visible !important; 39 | padding: 0 !important; 40 | position: static !important; 41 | right: auto !important; 42 | text-align: left !important; 43 | top: auto !important; 44 | vertical-align: baseline !important; 45 | width: auto !important; 46 | box-sizing: content-box !important; 47 | font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important; 48 | font-weight: normal !important; 49 | font-style: normal !important; 50 | font-size: 1em !important; 51 | min-height: inherit !important; 52 | min-height: auto !important; 53 | } 54 | 55 | .syntaxhighlighter { 56 | width: 100% !important; 57 | margin: 1em 0 1em 0 !important; 58 | position: relative !important; 59 | overflow: auto !important; 60 | font-size: 1em !important; 61 | } 62 | .syntaxhighlighter.source { 63 | overflow: hidden !important; 64 | } 65 | .syntaxhighlighter .bold { 66 | font-weight: bold !important; 67 | } 68 | .syntaxhighlighter .italic { 69 | font-style: italic !important; 70 | } 71 | .syntaxhighlighter .line { 72 | white-space: pre !important; 73 | } 74 | .syntaxhighlighter table { 75 | width: 100% !important; 76 | } 77 | .syntaxhighlighter table caption { 78 | text-align: left !important; 79 | padding: .5em 0 0.5em 1em !important; 80 | } 81 | .syntaxhighlighter table td.code { 82 | width: 100% !important; 83 | } 84 | .syntaxhighlighter table td.code .container { 85 | position: relative !important; 86 | } 87 | .syntaxhighlighter table td.code .container textarea { 88 | box-sizing: border-box !important; 89 | position: absolute !important; 90 | left: 0 !important; 91 | top: 0 !important; 92 | width: 100% !important; 93 | height: 100% !important; 94 | border: none !important; 95 | background: white !important; 96 | padding-left: 1em !important; 97 | overflow: hidden !important; 98 | white-space: pre !important; 99 | } 100 | .syntaxhighlighter table td.gutter .line { 101 | text-align: right !important; 102 | padding: 0 0.5em 0 1em !important; 103 | } 104 | .syntaxhighlighter table td.code .line { 105 | padding: 0 1em !important; 106 | } 107 | .syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line { 108 | padding-left: 0em !important; 109 | } 110 | .syntaxhighlighter.show { 111 | display: block !important; 112 | } 113 | .syntaxhighlighter.collapsed table { 114 | display: none !important; 115 | } 116 | .syntaxhighlighter.collapsed .toolbar { 117 | padding: 0.1em 0.8em 0em 0.8em !important; 118 | font-size: 1em !important; 119 | position: static !important; 120 | width: auto !important; 121 | height: auto !important; 122 | } 123 | .syntaxhighlighter.collapsed .toolbar span { 124 | display: inline !important; 125 | margin-right: 1em !important; 126 | } 127 | .syntaxhighlighter.collapsed .toolbar span a { 128 | padding: 0 !important; 129 | display: none !important; 130 | } 131 | .syntaxhighlighter.collapsed .toolbar span a.expandSource { 132 | display: inline !important; 133 | } 134 | .syntaxhighlighter .toolbar { 135 | position: absolute !important; 136 | right: 1px !important; 137 | top: 1px !important; 138 | width: 11px !important; 139 | height: 11px !important; 140 | font-size: 10px !important; 141 | z-index: 10 !important; 142 | } 143 | .syntaxhighlighter .toolbar span.title { 144 | display: inline !important; 145 | } 146 | .syntaxhighlighter .toolbar a { 147 | display: block !important; 148 | text-align: center !important; 149 | text-decoration: none !important; 150 | padding-top: 1px !important; 151 | } 152 | .syntaxhighlighter .toolbar a.expandSource { 153 | display: none !important; 154 | } 155 | .syntaxhighlighter.ie { 156 | font-size: .9em !important; 157 | padding: 1px 0 1px 0 !important; 158 | } 159 | .syntaxhighlighter.ie .toolbar { 160 | line-height: 8px !important; 161 | } 162 | .syntaxhighlighter.ie .toolbar a { 163 | padding-top: 0px !important; 164 | } 165 | .syntaxhighlighter.printing .line.alt1 .content, 166 | .syntaxhighlighter.printing .line.alt2 .content, 167 | .syntaxhighlighter.printing .line.highlighted .number, 168 | .syntaxhighlighter.printing .line.highlighted.alt1 .content, 169 | .syntaxhighlighter.printing .line.highlighted.alt2 .content { 170 | background: none !important; 171 | } 172 | .syntaxhighlighter.printing .line .number { 173 | color: #bbbbbb !important; 174 | } 175 | .syntaxhighlighter.printing .line .content { 176 | color: black !important; 177 | } 178 | .syntaxhighlighter.printing .toolbar { 179 | display: none !important; 180 | } 181 | .syntaxhighlighter.printing a { 182 | text-decoration: none !important; 183 | } 184 | .syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a { 185 | color: black !important; 186 | } 187 | .syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a { 188 | color: #008200 !important; 189 | } 190 | .syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a { 191 | color: blue !important; 192 | } 193 | .syntaxhighlighter.printing .keyword { 194 | color: #006699 !important; 195 | font-weight: bold !important; 196 | } 197 | .syntaxhighlighter.printing .preprocessor { 198 | color: gray !important; 199 | } 200 | .syntaxhighlighter.printing .variable { 201 | color: #aa7700 !important; 202 | } 203 | .syntaxhighlighter.printing .value { 204 | color: #009900 !important; 205 | } 206 | .syntaxhighlighter.printing .functions { 207 | color: #ff1493 !important; 208 | } 209 | .syntaxhighlighter.printing .constants { 210 | color: #0066cc !important; 211 | } 212 | .syntaxhighlighter.printing .script { 213 | font-weight: bold !important; 214 | } 215 | .syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a { 216 | color: gray !important; 217 | } 218 | .syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a { 219 | color: #ff1493 !important; 220 | } 221 | .syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a { 222 | color: red !important; 223 | } 224 | .syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a { 225 | color: black !important; 226 | } 227 | -------------------------------------------------------------------------------- /doc/stylesheets/shThemeDefault.css: -------------------------------------------------------------------------------- 1 | /** 2 | * SyntaxHighlighter 3 | * http://alexgorbatchev.com/SyntaxHighlighter 4 | * 5 | * SyntaxHighlighter is donationware. If you are using it, please donate. 6 | * http://alexgorbatchev.com/SyntaxHighlighter/donate.html 7 | * 8 | * @version 9 | * 3.0.83 (July 02 2010) 10 | * 11 | * @copyright 12 | * Copyright (C) 2004-2010 Alex Gorbatchev. 13 | * 14 | * @license 15 | * Dual licensed under the MIT and GPL licenses. 16 | */ 17 | .syntaxhighlighter { 18 | background-color: white !important; 19 | } 20 | .syntaxhighlighter .line.alt1 { 21 | background-color: white !important; 22 | } 23 | .syntaxhighlighter .line.alt2 { 24 | background-color: white !important; 25 | } 26 | .syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 { 27 | background-color: #e0e0e0 !important; 28 | } 29 | .syntaxhighlighter .line.highlighted.number { 30 | color: black !important; 31 | } 32 | .syntaxhighlighter table caption { 33 | color: black !important; 34 | } 35 | .syntaxhighlighter .gutter { 36 | color: #afafaf !important; 37 | } 38 | .syntaxhighlighter .gutter .line { 39 | border-right: 3px solid #6ce26c !important; 40 | } 41 | .syntaxhighlighter .gutter .line.highlighted { 42 | background-color: #6ce26c !important; 43 | color: white !important; 44 | } 45 | .syntaxhighlighter.printing .line .content { 46 | border: none !important; 47 | } 48 | .syntaxhighlighter.collapsed { 49 | overflow: visible !important; 50 | } 51 | .syntaxhighlighter.collapsed .toolbar { 52 | color: blue !important; 53 | background: white !important; 54 | border: 1px solid #6ce26c !important; 55 | } 56 | .syntaxhighlighter.collapsed .toolbar a { 57 | color: blue !important; 58 | } 59 | .syntaxhighlighter.collapsed .toolbar a:hover { 60 | color: red !important; 61 | } 62 | .syntaxhighlighter .toolbar { 63 | color: white !important; 64 | background: #6ce26c !important; 65 | border: none !important; 66 | } 67 | .syntaxhighlighter .toolbar a { 68 | color: white !important; 69 | } 70 | .syntaxhighlighter .toolbar a:hover { 71 | color: black !important; 72 | } 73 | .syntaxhighlighter .plain, .syntaxhighlighter .plain a { 74 | color: black !important; 75 | } 76 | .syntaxhighlighter .comments, .syntaxhighlighter .comments a { 77 | color: #008200 !important; 78 | } 79 | .syntaxhighlighter .string, .syntaxhighlighter .string a { 80 | color: blue !important; 81 | } 82 | .syntaxhighlighter .keyword { 83 | color: #006699 !important; 84 | } 85 | .syntaxhighlighter .preprocessor { 86 | color: gray !important; 87 | } 88 | .syntaxhighlighter .variable { 89 | color: #aa7700 !important; 90 | } 91 | .syntaxhighlighter .value { 92 | color: #009900 !important; 93 | } 94 | .syntaxhighlighter .functions { 95 | color: #ff1493 !important; 96 | } 97 | .syntaxhighlighter .constants { 98 | color: #0066cc !important; 99 | } 100 | .syntaxhighlighter .script { 101 | font-weight: bold !important; 102 | color: #006699 !important; 103 | background-color: none !important; 104 | } 105 | .syntaxhighlighter .color1, .syntaxhighlighter .color1 a { 106 | color: gray !important; 107 | } 108 | .syntaxhighlighter .color2, .syntaxhighlighter .color2 a { 109 | color: #ff1493 !important; 110 | } 111 | .syntaxhighlighter .color3, .syntaxhighlighter .color3 a { 112 | color: red !important; 113 | } 114 | 115 | .syntaxhighlighter .keyword { 116 | font-weight: bold !important; 117 | } 118 | -------------------------------------------------------------------------------- /doc/template.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{title}} 6 | {{#stylesheets}} 7 | 8 | {{/stylesheets}} 9 | {{#javascripts}} 10 | 11 | {{/javascripts}} 12 | 13 | 14 | 18 |
19 | {{{content}}} 20 |
21 | 25 | 36 | 37 | -------------------------------------------------------------------------------- /doc/zh/async/features.md: -------------------------------------------------------------------------------- 1 | # Async 功能列表 2 | 3 | Async 是一个用于统一 JavaScript 异步编程模式的组件,通过让异步函数统一返回 Async.Operation 使得异步函数不再需要自行处理异步回调,其详细设计目标及实现机制请参考系列文章《写个 JavaScript 异步调用框架 (Part 1, 2, 3, 4, 5, 6)》,本文仅用作面向开发者用户的说明文档。 4 | 5 | ## Async.Operation 6 | 7 | 表示异步操作的类,异步函数在每次调用时都应该实例化该类的一个新实例。 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 | 异步操作的结果。在 yield 执行之前,值一直为 undefined 。在 yield 执行之后,值为 yield 传入的值。 25 | 26 | ### Async.Operation.state 27 | 28 | * type: instance 29 | 30 | 异步操作的状态。在 yield 执行之前,状态一直为 "running" 。在 yield 执行之后,状态为 "completed" 。 31 | 32 | ### Async.Operation.completed 33 | 34 | * type: instance 35 | 36 | 异步操作是否已完成。在 yield 执行之前,已完成标志位一直为 false 。在 yield 执行之后,已完成标志位一直为 true 。 37 | 38 | ### Async.Operation.yield() 39 | 40 | * type: instance 41 | * input: 42 | * value (optional) 43 | * output: this : Operation 44 | 45 | 为特定的异步操作返回结果。 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 | 为特定的异步操作添加回调函数。回调函数会在异步操作返回后逐一被调用,回调函数都会接收到一个参数,也就是异步操作的返回结果。 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 | 创建一个异步调用队列。 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 | 异步操作的结果。在 go 执行之前,值一直为 undefined 。在 go 执行之后,值为 go 传入的值。随后队列中每一个函数执行完,这个值都会更新为最后执行完的函数的返回值。 88 | 89 | ### Async.chain().state 90 | 91 | * type: instance 92 | 93 | 异步操作的状态。在 go 执行之前,状态一直为 "waiting" 。在 go 执行之后,状态为 "chain running" 。当队列中的所有函数都执行完后,状态为 "completed" 。 94 | 95 | ### Async.chain().completed 96 | 97 | * type: instance 98 | 99 | 异步操作是否已完成。在队列执行之前,已完成标志位一直为 false 。在队列执行完之后,已完成标志位改为 true 。 100 | 101 | ### Async.chain().next() 102 | 103 | * type: instance 104 | * input: 105 | * function : Function 106 | * output: this : Operation 107 | 108 | 向异步调用队列添加函数。如果该函数返回值类型为 Async.Operation ,队列会等待该异步操作结束后再执行下一个函数;否则队列在执行完该函数后立即执行下一个函数。无论是哪种情况,该函数都会接收到一个参数,该参数为 go 传入的初始值,或者是上一个的函数返回值。该函数的返回值会作为唯一一个参数传给下一个的函数。 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 | 启动函数队列。如果向 go 传入一个参数,该参数将会作为唯一参数传递给函数队列中的第一个函数。在执行 go 之后,仍可以通过next向函数队列追加函数,并且函数队列总会执行这些追加的函数。 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 | 让函数队列等待指定的毫秒数。如果 wait 接收到一个参数,则它会原封不动地把这个参数返回,使得 wait 后面一个函数总能接收到 wait 前面一个函数的返回值。 151 | 152 | Async 153 | .chain() 154 | .next(firstFunction) 155 | .wait(999) 156 | .next(secondFunction) 157 | .go(); 158 | 159 | ## Helpers 160 | 161 | 以下是 Async 主要功能以外的辅助函数 162 | 163 | ### Async.go() 164 | 165 | * type: static 166 | * input: 167 | * value (optional) 168 | * output: operation : Operation 169 | 170 | 创建一个异步调用队列,并立即启动该队列。如果传入一个参数,则该参数作为队列第一个函数的唯一参数。 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 | 创建一个异步操作,它包含若干个并行的同步或异步子操作,仅当所有子操作都完成后该异步操作进行回调。 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 | 等待指定的毫秒数,然后开始执行回调函数。回调函数将要接收到的异步操作结果,可以在第二个参数指定。 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 | 生成一个立即返回的异步函数。回调函数将要接收到的异步操作结果,可以在参数中指定。 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 | 以异步方式调用同步函数,使用方式与 Function.prototype.call() 一致,返回类型为 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 | 以异步方式调用同步函数,使用方式与 Function.prototype.apply() 一致,返回类型为 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/zh/async/introduction.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Async 快速入门 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 |
20 |

Async 快速入门

21 | 22 |

在大多数应用程序当中, Async 用于解决 Ajax 带来的复杂异步回调关系,因此先将基本的 Ajax 操作封装起来,让它返回 Operation 实例。

23 | 24 |
/* jQuery required for this sample */
25 | 
26 | var getAsync = function(url, data) {
27 |     var operation = new Async.Operation();
28 |     $.get(url, data, function(result) { operation.yield(result); }, "json");
29 |     return operation;
30 | };
31 | 
32 | 33 |

接下来,我们编写调用 Ajax 操作的函数,让它同样返回 Operation 实例。

34 | 35 |
var plusAsync = function(x, y) {
36 |     return getAsync("/plus", "x=" + x + "&y=" + y);
37 | }
38 | 
39 | 40 |

按照这个风格,所有直接调用异步操作的函数都明明为以 Async 结尾,并且返回 Operation 实例。这样,整个应用程序中哪些函数是异步的,哪些函数是同步的,一看就知道了。对于异步函数,也有统一的获取结果方式。

41 | 42 |
var x = 1;
43 | var y = 2;
44 | 
45 | plusAsync(x, y)
46 |     .addCallback(function(result) { alert(result); });
47 | 
48 |
49 | 53 | 63 | 64 | -------------------------------------------------------------------------------- /doc/zh/async/introduction.md: -------------------------------------------------------------------------------- 1 | # Async 快速入门 2 | 3 | 在大多数应用程序当中, Async 用于解决 Ajax 带来的复杂异步回调关系,因此先将基本的 Ajax 操作封装起来,让它返回 Operation 实例。 4 | 5 | /* jQuery required for this sample */ 6 | 7 | var getAsync = function(url, data) { 8 | var operation = new Async.Operation(); 9 | $.get(url, data, function(result) { operation.yield(result); }, "json"); 10 | return operation; 11 | }; 12 | 13 | 接下来,我们编写调用 Ajax 操作的函数,让它同样返回 Operation 实例。 14 | 15 | var plusAsync = function(x, y) { 16 | return getAsync("/plus", "x=" + x + "&y=" + y); 17 | } 18 | 19 | 按照这个风格,所有直接调用异步操作的函数都明明为以 Async 结尾,并且返回 Operation 实例。这样,整个应用程序中哪些函数是异步的,哪些函数是同步的,一看就知道了。对于异步函数,也有统一的获取结果方式。 20 | 21 | var x = 1; 22 | var y = 2; 23 | 24 | plusAsync(x, y) 25 | .addCallback(function(result) { alert(result); }); 26 | -------------------------------------------------------------------------------- /doc/zh/async/secrets.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Async 内部实现 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 |
20 |

Async 内部实现

21 | 22 |

如果你需要使用 Async ,请参考对应的接口文档。本文档用于解释接口文档中没有提及的内部实现,但这部分实现随时可能被修改。

23 | 24 |

Async.Operation

25 | 26 |

在实例化一个异步操作时,可以传入一组选项。如果其中的 chain 属性为 true ,则该异步操作与 Async.chain() 创建的异步调用队列无异。

27 | 28 |
var operation = new Async.Operation({ chain: true });
 29 | 
30 | 31 |

Async.Operation.error

32 | 33 |
    34 |
  • type: instance
  • 35 |
36 | 37 |

异步操作的错误信息。

38 | 39 |

Async.Operation.go()

40 | 41 |
    42 |
  • type: instance
  • 43 |
  • input: 44 |
    • value (optional)
  • 45 |
  • output: this : Operation
  • 46 |
47 | 48 |

Async.Operation.yield 的别名,在异步队列中使用。

49 | 50 |

Async.Operation.next()

51 | 52 |
    53 |
  • type: instance
  • 54 |
  • input: 55 |
    • function : Function
  • 56 |
  • output: this : Operation
  • 57 |
58 | 59 |

Async.Operation.addCallback 的别名,在异步队列中使用。

60 | 61 |

Async.Operation.onerror()

62 | 63 |
    64 |
  • type: instance
  • 65 |
  • input: 66 |
    • handler : Function
  • 67 |
  • output: this : Operation
  • 68 |
69 | 70 |

为异步操作添加错误处理函数。当异步操作的回调发生错误时,错误处理函数将被调用。

71 | 72 |
var operation = new Async.Operation();
 73 | operation.addCallback(function() { throw "predefined error"; });
 74 | operation.onerror(function(operation) { alert(operation.error); });
 75 | operation.yield();
 76 | 
77 | 78 |

Async.chain()

79 | 80 |

Async.chain().error

81 | 82 |
    83 |
  • type: instance
  • 84 |
85 | 86 |

异步队列的错误信息。

87 | 88 |

Async.chain().onerror()

89 | 90 |
    91 |
  • type: instance
  • 92 |
  • input: 93 |
    • handler : Function
  • 94 |
  • output: this : Operation
  • 95 |
96 | 97 |

为异步队列添加错误处理函数。当异步队列的回调发生错误时,错误处理函数将被调用。

98 | 99 |

Helpers

100 | 101 |

Async.onerror()

102 | 103 |
    104 |
  • type: static
  • 105 |
  • input: 106 |
    • handler : Function
  • 107 |
  • output: Async
  • 108 |
109 | 110 |

为 Async 添加错误处理函数。任何异步操作或异步队列发生错误时,错误处理函数都会被调用。

111 |
112 | 116 | 126 | 127 | -------------------------------------------------------------------------------- /doc/zh/async/secrets.md: -------------------------------------------------------------------------------- 1 | # Async 内部实现 2 | 3 | 如果你需要使用 Async ,请参考对应的接口文档。本文档用于解释接口文档中没有提及的内部实现,但这部分实现随时可能被修改。 4 | 5 | ## Async.Operation 6 | 7 | 在实例化一个异步操作时,可以传入一组选项。如果其中的 chain 属性为 true ,则该异步操作与 Async.chain() 创建的异步调用队列无异。 8 | 9 | var operation = new Async.Operation({ chain: true }); 10 | 11 | ### Async.Operation.error 12 | 13 | * type: instance 14 | 15 | 异步操作的错误信息。 16 | 17 | ### Async.Operation.go() 18 | 19 | * type: instance 20 | * input: 21 | * value (optional) 22 | * output: this : Operation 23 | 24 | Async.Operation.yield 的别名,在异步队列中使用。 25 | 26 | ### Async.Operation.next() 27 | 28 | * type: instance 29 | * input: 30 | * function : Function 31 | * output: this : Operation 32 | 33 | Async.Operation.addCallback 的别名,在异步队列中使用。 34 | 35 | ### Async.Operation.onerror() 36 | 37 | * type: instance 38 | * input: 39 | * handler : Function 40 | * output: this : Operation 41 | 42 | 为异步操作添加错误处理函数。当异步操作的回调发生错误时,错误处理函数将被调用。 43 | 44 | var operation = new Async.Operation(); 45 | operation.addCallback(function() { throw "predefined error"; }); 46 | operation.onerror(function(operation) { alert(operation.error); }); 47 | operation.yield(); 48 | 49 | ## Async.chain() 50 | 51 | ### Async.chain().error 52 | 53 | * type: instance 54 | 55 | 异步队列的错误信息。 56 | 57 | ### Async.chain().onerror() 58 | 59 | * type: instance 60 | * input: 61 | * handler : Function 62 | * output: this : Operation 63 | 64 | 为异步队列添加错误处理函数。当异步队列的回调发生错误时,错误处理函数将被调用。 65 | 66 | ## Helpers 67 | 68 | ### Async.onerror() 69 | 70 | * type: static 71 | * input: 72 | * handler : Function 73 | * output: Async 74 | 75 | 为 Async 添加错误处理函数。任何异步操作或异步队列发生错误时,错误处理函数都会被调用。 76 | -------------------------------------------------------------------------------- /doc/zh/central/features.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Central 说明文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 |
20 |

Central 说明文档

21 | 22 |

Central 是一个用于在 JavaScript 环境中快速创建事件派发者的组件。

23 | 24 |

Central

25 | 26 |

用于监听事件和派发事件的静态类。

27 | 28 |
Central.listen("move", function(e) {
 29 |     element.style.left = parseInt(element.style.left) + e.x + "px";
 30 |     element.style.top = parseInt(element.style.top) + e.y + "px";
 31 | });
 32 | 
 33 | Central.call("move", {
 34 |     x: 10,
 35 |     y: -20
 36 | });
 37 | 
38 | 39 |

Central.listen()

40 | 41 |
    42 |
  • type: static
  • 43 |
  • input: 44 |
    • command : String
    • 45 |
    • handler : Function
  • 46 |
  • output: Central
  • 47 |
48 | 49 |

监听指定名称的事件。

50 | 51 |
Central.listen("move", function(e) {
 52 |     element.style.left = parseInt(element.style.left) + e.x + "px";
 53 |     element.style.top = parseInt(element.style.top) + e.y + "px";
 54 | });
 55 | 
56 | 57 |

Central.call()

58 | 59 |
    60 |
  • type: static
  • 61 |
  • input: 62 |
    • command : String
    • 63 |
    • argument : Object
  • 64 |
  • output: Central
  • 65 |
66 | 67 |

派发指定名称的事件。

68 | 69 |
Central.call("move", {
 70 |     x: 10,
 71 |     y: -20
 72 | });
 73 | 
74 | 75 |

Central.extend()

76 | 77 |
    78 |
  • type: static
  • 79 |
  • input: target : Object
  • 80 |
  • output: target : Object
  • 81 |
82 | 83 |

扩展指定对象,使其拥有 Central 功能。

84 | 85 |

Central.extend().listen()

86 | 87 |
    88 |
  • type: instance
  • 89 |
  • input: 90 |
    • command : String
    • 91 |
    • handler : Function
  • 92 |
  • output: this
  • 93 |
94 | 95 |

监听指定名称的事件。

96 | 97 |
var controller = new Controller();
 98 | 
 99 | Central.extend(controller);
100 | 
101 | controller.listen("move", function(e) {
102 |     element.style.left = parseInt(element.style.left) + e.x + "px";
103 |     element.style.top = parseInt(element.style.top) + e.y + "px";
104 | });
105 | 
106 | 107 |

Central.extend().call()

108 | 109 |
    110 |
  • type: instance
  • 111 |
  • input: 112 |
    • command : String
    • 113 |
    • argument : Object
  • 114 |
  • output: this
  • 115 |
116 | 117 |

派发指定名称的事件。

118 | 119 |
var controller = new Controller();
120 | 
121 | Central.extend(controller);
122 | 
123 | controller.call("move", {
124 |     x: 10,
125 |     y: -20
126 | });
127 | 
128 |
129 | 133 | 143 | 144 | -------------------------------------------------------------------------------- /doc/zh/central/features.md: -------------------------------------------------------------------------------- 1 | # Central 说明文档 2 | 3 | Central 是一个用于在 JavaScript 环境中快速创建事件派发者的组件。 4 | 5 | ## Central 6 | 7 | 用于监听事件和派发事件的静态类。 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 | 监听指定名称的事件。 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 | 派发指定名称的事件。 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 | 扩展指定对象,使其拥有 Central 功能。 56 | 57 | ### Central.extend().listen() 58 | 59 | * type: instance 60 | * input: 61 | * command : String 62 | * handler : Function 63 | * output: this 64 | 65 | 监听指定名称的事件。 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 | 派发指定名称的事件。 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/zh/central/introduction.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Central 快速入门 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 |
20 |

Central 快速入门

21 | 22 |

如果需要监听一个事件,我们可以使用 Central.listen 。

23 | 24 |
Central.listen("eventname", function(e) {
25 |     /* handle event */
26 | });
27 | 
28 | 29 |

如果需要派发一个事件,我们可以使用 Central.call 。

30 | 31 |
Central.call("eventname", {
32 |     /* event argument */
33 | });
34 | 
35 | 36 |

如果单例的 Central 无法满足需求,可以使用 Central.extend 获取多个实例。

37 | 38 |
var controller = new Controller();
39 | 
40 | Central.extend(controller);
41 | 
42 | controller.listen("eventname", function(e) {
43 |     /* handle event */
44 | });
45 | 
46 | controller.call("eventname", {
47 |     /* event argument */
48 | });
49 | 
50 |
51 | 55 | 65 | 66 | -------------------------------------------------------------------------------- /doc/zh/central/introduction.md: -------------------------------------------------------------------------------- 1 | # Central 快速入门 2 | 3 | 如果需要监听一个事件,我们可以使用 Central.listen 。 4 | 5 | Central.listen("eventname", function(e) { 6 | /* handle event */ 7 | }); 8 | 9 | 如果需要派发一个事件,我们可以使用 Central.call 。 10 | 11 | Central.call("eventname", { 12 | /* event argument */ 13 | }); 14 | 15 | 如果单例的 Central 无法满足需求,可以使用 Central.extend 获取多个实例。 16 | 17 | var controller = new Controller(); 18 | 19 | Central.extend(controller); 20 | 21 | controller.listen("eventname", function(e) { 22 | /* handle event */ 23 | }); 24 | 25 | controller.call("eventname", { 26 | /* event argument */ 27 | }); 28 | -------------------------------------------------------------------------------- /doc/zh/central/secrets.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Central 内部实现 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 |
20 |

Central 内部实现

21 | 22 |

如果你需要使用 Central ,请参考对应的接口文档。本文档用于解释接口文档中没有提及的内部实现,但这部分实现随时可能被修改。

23 | 24 |

Central

25 | 26 |

Central 并没有接口文档之外的内部实现需要说明。

27 |
28 | 32 | 42 | 43 | -------------------------------------------------------------------------------- /doc/zh/central/secrets.md: -------------------------------------------------------------------------------- 1 | # Central 内部实现 2 | 3 | 如果你需要使用 Central ,请参考对应的接口文档。本文档用于解释接口文档中没有提及的内部实现,但这部分实现随时可能被修改。 4 | 5 | ## Central 6 | 7 | Central 并没有接口文档之外的内部实现需要说明。 8 | -------------------------------------------------------------------------------- /doc/zh/grandcentral/features.md: -------------------------------------------------------------------------------- 1 | # GrandCentral 说明文档 2 | 3 | GrandCentral 是一个用于在 JavaScript 环境中快速创建 JSON 派发者的组件,关注 JSON 的组件可以通过模式匹配捕获符合特定条件的 JSON 。 4 | 5 | ## GrandCentral 6 | 7 | 用于匹配 JSON 和派发 JSON 的静态类。 8 | 9 | GrandCentral.listen({ 10 | status: 200, 11 | command: "friendstatus" 12 | }, function(json) { 13 | /* update friend status */ 14 | }); 15 | 16 | GrandCentral.call({ 17 | status: 200, 18 | command: "friendstatus", 19 | content: [ 20 | { 21 | username: "user0", 22 | status: "online" 23 | }, 24 | { 25 | username: "user42", 26 | status: "away" 27 | } 28 | ] 29 | }); 30 | 31 | ### GrandCentral.listen() 32 | 33 | * type: static 34 | * input: 35 | * filter : Function || Object 36 | * handler : Function 37 | * output: Central 38 | 39 | 使用函数过滤 JSON 或使用模式匹配 JSON ,并使用处理函数处理符合条件的 JSON 。 40 | 41 | GrandCentral.listen(function(json) { 42 | return (json.status == 200 && json.command == "friendstatus") 43 | }, function(json) { 44 | /* update friend status */ 45 | }); 46 | 47 | GrandCentral.listen({ 48 | status: 200, 49 | command: "friendstatus" 50 | }, function(json) { 51 | /* update friend status */ 52 | }); 53 | 54 | #### Default Operator 55 | 56 | 如果不使用任何的 Operator ,则视为 Default Operator 。如果值是 Array ,按照 in Operator 处理;如果值是 RegExp ,按照 re Operator 处理;如果值是 Function ,按照 f Operator 处理;否则,按照 eq Operator 处理。 57 | 58 | GrandCentral.listen({ 59 | value1: 42, 60 | value2: ["hello", "world"] 61 | }, function(json) { 62 | /* will capture call below */ 63 | }); 64 | 65 | GrandCentral.call({ 66 | value1: 42, 67 | value2: "world", 68 | value3: "other" 69 | }); 70 | 71 | #### eq Operator 72 | 73 | eq Operator 用于比较两个值是否相等。对于 String 、 Number 、 Boolean ,比较值是否全等;对于 Array ,对 Array 中的每一个元素使用 eq Operator 进行比较;对于 Object ,对 Object 中的每一个元素使用 Default Operator 进行比较。对于 null 和 undefined ,比较值是否全等。 74 | 75 | GrandCentral.listen({ 76 | value1$eq: "hello world", 77 | value2$eq: 42, 78 | value3$eq: true, 79 | value4$eq: ["hello", "world"], 80 | value5$eq: { 81 | value5_1: 42, 82 | value5_2: ["hello", "world"] 83 | } 84 | }, function(json) { 85 | /* will capture call below */ 86 | }); 87 | 88 | GrandCentral.call({ 89 | value1: "hello world", 90 | value2: 42, 91 | value3: true, 92 | value4: ["hello", "world"], 93 | value5: { 94 | value5_1: 42, 95 | value5_2: "world", 96 | value5_3: "other" 97 | }, 98 | value6: "other" 99 | }); 100 | 101 | #### ne Operator 102 | 103 | ne Operator 用于比较两个值是否不等。其比较结果相当于对 eq Operator 比较结果取反。 104 | 105 | GrandCentral.listen({ 106 | value1$ne: "hello world", 107 | value2$ne: 42 108 | }, function(json) { 109 | /* will capture call below */ 110 | }); 111 | 112 | GrandCentral.call({ 113 | value1: "", 114 | value2: 24, 115 | value3: "other" 116 | }); 117 | 118 | #### lt Operator 119 | 120 | lt Operator 用于比较数值是否小于某个给定值。 121 | 122 | GrandCentral.listen({ 123 | value1$lt: 0 124 | }, function(json) { 125 | /* will capture call below */ 126 | }); 127 | 128 | GrandCentral.call({ 129 | value1: -1, 130 | value2: "other" 131 | }); 132 | 133 | #### lte Operator 134 | 135 | lte Operator 用于比较数值是否小于或等于某个给定值。 136 | 137 | GrandCentral.listen({ 138 | value1$lte: 0, 139 | value2$lte: 0 140 | }, function(json) { 141 | /* will capture call below */ 142 | }); 143 | 144 | GrandCentral.call({ 145 | value1: -1, 146 | value2: 0, 147 | value3: "other" 148 | }); 149 | 150 | #### gt Operator 151 | 152 | gt Operator 用于比较数值是否小于某个给定值。 153 | 154 | GrandCentral.listen({ 155 | value1$gt: 0 156 | }, function(json) { 157 | /* will capture call below */ 158 | }); 159 | 160 | GrandCentral.call({ 161 | value1: 1, 162 | value2: "other" 163 | }); 164 | 165 | #### gte Operator 166 | 167 | gte Operator 用于比较数值是否小于或等于某个给定值。 168 | 169 | GrandCentral.listen({ 170 | value1$gte: 0, 171 | value2$gte: 0 172 | }, function(json) { 173 | /* will capture call below */ 174 | }); 175 | 176 | GrandCentral.call({ 177 | value1: 1, 178 | value2: 0, 179 | value3: "other" 180 | }); 181 | 182 | #### in Operator 183 | 184 | in Operator 用于检测值是否等于 Array 中的任一值。其中等于指的是 eq Operator 。 185 | 186 | GrandCentral.listen({ 187 | value1$in: ["hello", "world"], 188 | value2$in: [{ 189 | value2_1: 0 190 | }, { 191 | value2_1: 1 192 | }] 193 | }, function(json) { 194 | /* will capture call below */ 195 | }); 196 | 197 | GrandCentral.call({ 198 | value1: "world", 199 | value2: { 200 | value2_1: 0, 201 | value2_1: "other" 202 | }, 203 | value3: "other" 204 | }); 205 | 206 | #### nin Operator 207 | 208 | nin Operator 用于检测是否不等于 Array 中的任一值。相对于对 in Operator 结果取反。 209 | 210 | GrandCentral.listen({ 211 | value1$nin: ["hello", "world"], 212 | value2$nin: [{ 213 | value2_1: 0 214 | }, { 215 | value2_1: 1 216 | }] 217 | }, function(json) { 218 | /* will capture call below */ 219 | }); 220 | 221 | GrandCentral.call({ 222 | value1: "", 223 | value2: { 224 | value2_1: -1, 225 | value2_1: "other" 226 | }, 227 | value3: "other" 228 | }); 229 | 230 | #### all Operator 231 | 232 | all Operator 用于检查 Array 中的值是否都属于某个给定的 Array 。其中属于指的是 in Operator 。 233 | 234 | GrandCentral.listen({ 235 | value1$all: ["hello", "world"] 236 | }, function(json) { 237 | /* will capture call below */ 238 | }); 239 | 240 | GrandCentral.call({ 241 | value1: ["hello", "and", "world"], 242 | value2: "other" 243 | }); 244 | 245 | #### ex Operator 246 | 247 | ex Operator 用于检测某个值是否存在,或者是某个值是否不存在。 248 | 249 | GrandCentral.listen({ 250 | value1$ex: true, 251 | value2$ex: false 252 | }, function(json) { 253 | /* will capture call below */ 254 | }); 255 | 256 | GrandCentral.call({ 257 | value1: "hello world", 258 | value3: "other" 259 | }); 260 | 261 | #### re Operator 262 | 263 | re Operator 用于检测 String 是否匹配给定的 RegExp 。 264 | 265 | GrandCentral.listen({ 266 | value1$re: /^A.*/ 267 | }, function(json) { 268 | /* will capture call below */ 269 | }); 270 | 271 | GrandCentral.call({ 272 | value1: "A13579", 273 | value2: "other" 274 | }); 275 | 276 | #### f Operator 277 | 278 | f Operator 用于检测值是否能够通过给定的函数表达式。 279 | 280 | GrandCentral.listen({ 281 | value1$f: function(json) { return json.x > json.y; }, 282 | value2$f: function(json) { return !json; } 283 | }, function(json) { 284 | /* will capture call below */ 285 | }); 286 | 287 | GrandCentral.call({ 288 | value1: { 289 | x: 1, 290 | y: 0 291 | }, 292 | value2: false, 293 | value3: "other" 294 | }); 295 | 296 | ### GrandCentral.call() 297 | 298 | * type: static 299 | * input: 300 | * json : Object 301 | * output: Central 302 | 303 | 派发 JSON 。 304 | 305 | GrandCentral.call({ 306 | status: 200, 307 | command: "friendstatus", 308 | content: [ 309 | { 310 | username: "user0", 311 | status: "online" 312 | }, 313 | { 314 | username: "user42", 315 | status: "away" 316 | } 317 | ] 318 | }); 319 | 320 | ## GrandCentral.extend() 321 | 322 | * type: static 323 | * input: target : Object 324 | * output: target : Object 325 | 326 | 扩展指定对象,使其拥有 Central 功能。 327 | 328 | ### GrandCentral.extend().listen() 329 | 330 | * type: instance 331 | * input: 332 | * filter : Function || Object 333 | * handler : Function 334 | * output: this 335 | 336 | 使用函数过滤 JSON 或使用模式匹配 JSON ,并使用处理函数处理符合条件的 JSON 。 337 | 338 | var controller = new Controller(); 339 | 340 | GrandCentral.extend(controller); 341 | 342 | controller.listen(function(json) { 343 | return (json.status == 200 && json.command == "friendstatus") 344 | }, function(json) { 345 | /* update friend status */ 346 | }); 347 | 348 | ### GrandCentral.extend().call() 349 | 350 | * type: instance 351 | * input: 352 | * json : Object 353 | * output: this 354 | 355 | 派发指定名称的 JSON 。 356 | 357 | var controller = new Controller(); 358 | 359 | Central.extend(controller); 360 | 361 | controller.call({ 362 | status: 200, 363 | command: "friendstatus", 364 | content: [ 365 | { 366 | username: "user0", 367 | status: "online" 368 | }, 369 | { 370 | username: "user42", 371 | status: "away" 372 | } 373 | ] 374 | }); 375 | -------------------------------------------------------------------------------- /doc/zh/grandcentral/introduction.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GrandCentral 快速入门 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 |
20 |

GrandCentral 快速入门

21 | 22 |

如果需要捕捉指定属性等于指定值的 JSON ,我们可以使用 GrandCentral.listen 。

23 | 24 |
Central.listen({
25 |     status: 200,
26 |     command: "friendstatus"
27 | }, function(json) {
28 |     /* update friend status */
29 | });
30 | 
31 | 32 |

如果需要派发一个 JSON ,我们可以使用 Central.call 。

33 | 34 |
Central.call({
35 |     status: 200,
36 |     command: "friendstatus",
37 |     content: [
38 |         {
39 |             username: "user0",
40 |             status: "online"
41 |         },
42 |         {
43 |             username: "user42",
44 |             status: "away"
45 |         }
46 |     ]
47 | });
48 | 
49 | 50 |

如果单例的 GrandCentral 无法满足需求,可以使用 GrandCentral.extend 获取多个实例。

51 | 52 |
var controller = new Controller();
53 | 
54 | GrandCentral.extend(controller);
55 | 
56 | controller.listen({
57 |     status: 200,
58 |     command: "friendstatus"
59 | }, function(json) {
60 |     /* update friend status */
61 | });
62 | 
63 | controller.call({
64 |     status: 200,
65 |     command: "friendstatus",
66 |     content: [
67 |         {
68 |             username: "user0",
69 |             status: "online"
70 |         },
71 |         {
72 |             username: "user42",
73 |             status: "away"
74 |         }
75 |     ]
76 | });
77 | 
78 |
79 | 83 | 93 | 94 | -------------------------------------------------------------------------------- /doc/zh/grandcentral/introduction.md: -------------------------------------------------------------------------------- 1 | # GrandCentral 快速入门 2 | 3 | 如果需要捕捉指定属性等于指定值的 JSON ,我们可以使用 GrandCentral.listen 。 4 | 5 | Central.listen({ 6 | status: 200, 7 | command: "friendstatus" 8 | }, function(json) { 9 | /* update friend status */ 10 | }); 11 | 12 | 如果需要派发一个 JSON ,我们可以使用 Central.call 。 13 | 14 | Central.call({ 15 | status: 200, 16 | command: "friendstatus", 17 | content: [ 18 | { 19 | username: "user0", 20 | status: "online" 21 | }, 22 | { 23 | username: "user42", 24 | status: "away" 25 | } 26 | ] 27 | }); 28 | 29 | 如果单例的 GrandCentral 无法满足需求,可以使用 GrandCentral.extend 获取多个实例。 30 | 31 | var controller = new Controller(); 32 | 33 | GrandCentral.extend(controller); 34 | 35 | controller.listen({ 36 | status: 200, 37 | command: "friendstatus" 38 | }, function(json) { 39 | /* update friend status */ 40 | }); 41 | 42 | controller.call({ 43 | status: 200, 44 | command: "friendstatus", 45 | content: [ 46 | { 47 | username: "user0", 48 | status: "online" 49 | }, 50 | { 51 | username: "user42", 52 | status: "away" 53 | } 54 | ] 55 | }); 56 | -------------------------------------------------------------------------------- /doc/zh/grandcentral/secrets.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GrandCentral 内部实现 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 |
20 |

GrandCentral 内部实现

21 | 22 |

如果你需要使用 GrandCentral ,请参考对应的接口文档。本文档用于解释接口文档中没有提及的内部实现,但这部分实现随时可能被修改。

23 | 24 |

GrandCentral.Operators

25 | 26 |
    27 |
  • type: static
  • 28 |
29 | 30 |

GrandCentral 所有的 Operator 函数集合。可以通过修改这个集合实现对 Operator 的修改。 GrandCentral 单例及所有通过扩展对象获得的实例共享这一个 Operator 函数集合。其中,每一个 Operator 函数接收两个参数,代表测试表达式的 testValue 以及代表待测试值的 value 。如果待测试值不存在(而非等于 undefined ),则 Operator 函数内部 arguments.length 等于 1 。

31 | 32 |
GrandCentral.Operators["odd"] = function(testValue, value) {
33 |     return arguments.length == 2 && ((value % 2 != 0) == testValue)
34 | };
35 | 
36 | GrandCentral.Operators["even"] = function(testValue, value) {
37 |     return arguments.length == 2 && ((value % 2 == 0) == testValue)
38 | };
39 | 
40 | GrandCentral.listen({
41 |     value1$odd: true,
42 |     value2$even: true
43 | }, function(json) {
44 |     /* will capture call below */
45 | });
46 | 
47 | GrandCentral.call({
48 |     value1: 99,
49 |     value2: 42
50 |     value3: "other"
51 | });
52 | 
53 |
54 | 58 | 68 | 69 | -------------------------------------------------------------------------------- /doc/zh/grandcentral/secrets.md: -------------------------------------------------------------------------------- 1 | # GrandCentral 内部实现 2 | 3 | 如果你需要使用 GrandCentral ,请参考对应的接口文档。本文档用于解释接口文档中没有提及的内部实现,但这部分实现随时可能被修改。 4 | 5 | ## GrandCentral.Operators 6 | 7 | * type: static 8 | 9 | GrandCentral 所有的 Operator 函数集合。可以通过修改这个集合实现对 Operator 的修改。 GrandCentral 单例及所有通过扩展对象获得的实例共享这一个 Operator 函数集合。其中,每一个 Operator 函数接收两个参数,代表测试表达式的 testValue 以及代表待测试值的 value 。如果待测试值不存在(而非等于 undefined ),则 Operator 函数内部 arguments.length 等于 1 。 10 | 11 | GrandCentral.Operators["odd"] = function(testValue, value) { 12 | return arguments.length == 2 && ((value % 2 != 0) == testValue) 13 | }; 14 | 15 | GrandCentral.Operators["even"] = function(testValue, value) { 16 | return arguments.length == 2 && ((value % 2 == 0) == testValue) 17 | }; 18 | 19 | GrandCentral.listen({ 20 | value1$odd: true, 21 | value2$even: true 22 | }, function(json) { 23 | /* will capture call below */ 24 | }); 25 | 26 | GrandCentral.call({ 27 | value1: 99, 28 | value2: 42 29 | value3: "other" 30 | }); 31 | -------------------------------------------------------------------------------- /doc/zh/index.md: -------------------------------------------------------------------------------- 1 | # JavaScript 辅助模块 2 | 3 | JavaScript 辅助模块是一组专门用于构建复杂 Ajax 应用的基础模块。这些模块有一个共同点,就是它们都让你更多地用声明式语言描述问题,更少地用命令式语言解决问题。从本质上来说,它们相当于在 JavaScript 之上建立了各自问题领域的 Internal DSL (Domain Specific Language) ,并且鼓励你使用这些 Internal DSL 描述该领域的问题,然后模块内部的逻辑会为你求解这些问题,你也就无需编写控制求解过程的命令。 4 | 5 | ## Async 6 | 7 | * 快速入门 8 | * 接口文档 9 | * 实现文档 10 | 11 | 如果你的应用程序涉及大量的 Ajax 操作,并且采用了分层的设计思想, Async 能够简化异步操作的接口,使得你可以如同控制同步流程一样控制异步流程。如果你的应用程序还涉及 Ajax 操作队列, Async 能够简化这些队列的实现,让你以声明式语言描述异步队列。 12 | 13 | 你可以使用 Async 封装最基础的 Ajax 操作,从而使得整个应用程序都通过 Async.Operation 来管理 Ajax 操作,而无需为异步函数加入回调参数。在下面这个例子中,所有名称以 Async 结尾的函数都返回 Async.Operation 实例,这使得异步函数无需接收回调函数,也无需主动调用回调函数,如同同步函数一般把调用结果返回既可。只有在真正关心回调结果的地方,才去获取回调结果。 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 | * 接口文档 53 | * 实现文档 54 | 55 | 如果你的应用程序包含若干带有多个重载的函数,你可以使用 Overload 简化对重载入口的描述。 56 | 57 | 在一般情况下,如果一个函数有多个重载,往往意味着你要在这个函数的内部自行编写代码以识别用户调用的是哪一个重载,然后再选择对应的逻辑执行下去。这样做的坏处是,每一个函数都需要有自己的重载识别逻辑,这些逻辑有相似的地方,但是又略有不同,你很难将它们抽取为子函数,但每个函数独立维护一份这样的逻辑成本也不低。 Overload 完成了对这部分逻辑的抽象工作,使得你不再需要重复编写和维护这些代码,然后提供一个声明式接口,你只需要描述重载入口就可以了。 58 | 59 | 在下面这个例子当中,我们通过简单的描述实现了一个函数多个重载的分离。 60 | 61 | var User = function(name) { this.name = name; }; 62 | 63 | var sayHello = Overload 64 | .add("String", 65 | function(string) { alert("Hello, " + string); }) 66 | .add([User], 67 | function(user) { sayHello(user.name) }) 68 | 69 | ## Central 70 | 71 | * 快速入门 72 | * 接口文档 73 | * 实现文档 74 | 75 | 如果你的应用程序由多个不同的模块组成,并且你希望减少这些模块之间的相互依赖关系,一个很简单的解决方案就是把模块之间的方法调用改写为事件派发,并且把事件监听和派发工作都交由中央事件派发器去做。 76 | 77 | 例如说,原本 A 模块需要调用 B 模块,那么就存在 A 对 B 的单向依赖;如果改写为 A 派发事件, B 监听事件,则可以去掉 A 对 B 的单向依赖,但会增加 B 对 A 的单向依赖。为了解决这个问题,我们可以使用中央事件派发器 Central , A 通过 Central 派发事件, B 通过 Central 监听 A 可能派发的事件,这时候 A 和 B 之间就不存在任何的依赖关系了,它们都单向依赖于 Central 。 78 | 79 | Central 通过字符串形式的事件名称区分不同的事件。无论由哪个模块调用 Central 进行派发,使用同一个名称的事件应该总是代表同一个语义。 80 | 81 | /* jQuery required for this sample */ 82 | 83 | var mapController = new (function() { 84 | var mapOffset = { x: 0, y: 0 }; 85 | var redrawMap = function() { /* implementation */ }; 86 | 87 | Central.listen("mapmove", function(event) { 88 | mapOffset.x += event.x; 89 | mapOffset.y += event.y; 90 | redrawMap(); 91 | }); 92 | })(); 93 | 94 | var keyboardListener = new (function() { 95 | var LEFT_ARROW = 37, 96 | RIGHT_ARROW = 39, 97 | UP_ARROW = 38, 98 | DOWN_ARROW = 40; 99 | 100 | $(document).keypress(function(event) { 101 | switch (event.which) { 102 | case LEFT_ARROW: 103 | Central.call("mapmove", { x: -10, y: 0 }); 104 | break; 105 | case RIGHT_ARROW: 106 | Central.call("mapmove", { x: 10, y: 0 }); 107 | break; 108 | case UP_ARROW: 109 | Central.call("mapmove", { x: 0, y: 10 }); 110 | break; 111 | case DOWN_ARROW: 112 | Central.call("mapmove", { x: 0, y: -10 }); 113 | break; 114 | } 115 | }); 116 | })(); 117 | 118 | var mouseListener = new (function() { 119 | /* monitor mouse events and dispatch move event in Central */ 120 | /* this module won't be loaded on touch screen devices */ 121 | })(); 122 | 123 | var touchListener = new (function() { 124 | /* monitor touch events and dispatch move event in Central */ 125 | /* this module will be loaded on touch screen devices */ 126 | })(); 127 | 128 | ## GrandCentral 129 | 130 | * 快速入门 131 | * 接口文档 132 | * 实现文档 133 | 134 | 如果你的应用程序需要监听一个事件源,并从中根据事件数据进行分拣, GrandCentral 能够帮你完成分拣工作,而你需要做的只是使用简单的 JSON 描述分拣需要进行匹配的模式。 135 | 136 | 一个常见的例子是,你的应用程序需要监听所有的 AJAX 请求,然后根据服务器端返回的 JSON 内容来判断具体应该如何响应。这时候你可以将响应进行分组,为每一组响应编写一个处理函数,然后使用 GrandCentral 匹配服务器端返回的 JSON 并且让它来调用正确的处理函数。 137 | 138 | /* jQuery required for this sample */ 139 | 140 | $(document).ajaxComplete(function(event, xhr, settings) { 141 | var response = { 142 | status: xhr.status, 143 | json: $.parseJSON(xhr.responseText) 144 | }; 145 | GrandCentral.call(response); 146 | }; 147 | 148 | var chatMessageController = new (function() { 149 | var renderChatMessage = function(message) { /* implementation */ }; 150 | 151 | GrandCentral.listen({ 152 | status: 200, 153 | json: { 154 | command: "message" 155 | } 156 | }, function(response) { 157 | renderChatMessage(response.json.message); 158 | }); 159 | })(); 160 | 161 | var systemNotificationController = new (function() { 162 | var renderSystemNotification = function(notification) { /* implementation */ }; 163 | 164 | GrandCentral.listen({ 165 | status: 200, 166 | json: { 167 | command: "notification" 168 | } 169 | }, function(response) { 170 | renderSystemNotification(response.json.notification); 171 | }); 172 | })(); 173 | 174 | var errorController = new (function() { 175 | var displayErrorMessage = function() { /* implementation */ }; 176 | 177 | GrandCentral.listen({ 178 | status$ne: 200 179 | }, function(response) { 180 | displayErrorMessage(); 181 | }); 182 | })(); 183 | 184 | ## List 185 | 186 | * 快速入门 187 | * 接口文档 188 | * 实现文档 189 | 190 | 如果你的应用程序需要处理数组形式的数据,然而你觉得 Array 内置的功能并不够用,并且希望能够使用 Haskell 风格的 List 处理形式,那么你可以使用 List 组件。 List 组件能够封装 Array 操作,提供 Haskell 命令风格的方法,简化列表形式数据的操作。 191 | 192 | var list1 = new List(1, 2, 3, 4, 5, 4, 3, 2, 1, 0); 193 | 194 | var list2 = list1.filter(function(i) { return i > 2; }); 195 | assert(list2.toArray() == [3, 4, 5, 4, 3]); 196 | 197 | var list3 = list2.map(function(i) { return i * i; }); 198 | assert(list3.toArray() == [9, 16, 25, 16, 9]); 199 | 200 | var list4 = List.iterate(function(i) { return i * 2; }, 1); 201 | 202 | var list5 = list4.take(10); 203 | assert(list5.toArray() == [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]); 204 | 205 | var list6 = list4 206 | .map(function(i) { return i - 1; }) 207 | .dropWhile(function(i) { return i < 100; }) 208 | .takeWhile(function(i) { return i < 1000; }); 209 | assert(list6.toArray() == [127, 255, 511]); 210 | -------------------------------------------------------------------------------- /doc/zh/list/introduction.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | List 快速入门 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 |
20 |

List 快速入门

21 | 22 |

要使用 List ,首先需要创建一个基于 Array 的 List ,然后各种演算都可以通过 List 进行。完成演算后,可以使用 toArray 方法将数据输出为 Array ,可以使用 each 方法遍历数据,还可以通过 at 方法按索引访问数据。

23 | 24 |
var originalArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
25 | var originalList = new List(originalArray);
26 | 
27 | var calculatedList = originalList
28 |     .map(function(i) { return i * (i + 1) / 2; })
29 |     .filter(function(i) { return i > 10 && i < 50; })
30 | 
31 | var calculatedArray = calculatedList.toArray();
32 | assert(calculatedArray == [15, 21, 28, 36, 45]);
33 | 
34 | calculatedList.each(function(i) { alert(i); });
35 | 
36 | assert(calculatedList.at(0) == 15);
37 | assert(calculatedList.at(4) == 45);
38 | 
39 | 40 |

在某些情况下,无穷 List 会特别有用。例如说,你需要按照一个规则填充一个数组,这个填充可以无限进行下去,但是你在演算之前无法预知需要填充多大的数组。一个具体的例子是,你需要知道对 Fibonacci 数列排除掉第一位的 0 后进行连乘的话,多少项后乘积会超过 10000 ,这时候你无法预知需要生成 Fibonacci 数列的前多少位,所以可以使用无穷 List 来解决这个问题。

41 | 42 |
var a = 0, b = 1;
43 | var list = List
44 |     .generate(function(proxy) {
45 |         proxy.yield(a);
46 |         var aNext = b;
47 |         var bNext = a + b;
48 |         a = aNext;
49 |         b = bNext;
50 |     })
51 |     .drop(1)
52 |     .scan(function(acc, i) { return acc * i; }, 1)
53 |     .takeWhile(function(i) { return i < 10000; });
54 | assert(list.length() == 8);
55 | 
56 |
57 | 61 | 71 | 72 | -------------------------------------------------------------------------------- /doc/zh/list/introduction.md: -------------------------------------------------------------------------------- 1 | # List 快速入门 2 | 3 | 要使用 List ,首先需要创建一个基于 Array 的 List ,然后各种演算都可以通过 List 进行。完成演算后,可以使用 toArray 方法将数据输出为 Array ,可以使用 each 方法遍历数据,还可以通过 at 方法按索引访问数据。 4 | 5 | var originalArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 6 | var originalList = new List(originalArray); 7 | 8 | var calculatedList = originalList 9 | .map(function(i) { return i * (i + 1) / 2; }) 10 | .filter(function(i) { return i > 10 && i < 50; }) 11 | 12 | var calculatedArray = calculatedList.toArray(); 13 | assert(calculatedArray == [15, 21, 28, 36, 45]); 14 | 15 | calculatedList.each(function(i) { alert(i); }); 16 | 17 | assert(calculatedList.at(0) == 15); 18 | assert(calculatedList.at(4) == 45); 19 | 20 | 在某些情况下,无穷 List 会特别有用。例如说,你需要按照一个规则填充一个数组,这个填充可以无限进行下去,但是你在演算之前无法预知需要填充多大的数组。一个具体的例子是,你需要知道对 Fibonacci 数列排除掉第一位的 0 后进行连乘的话,多少项后乘积会超过 10000 ,这时候你无法预知需要生成 Fibonacci 数列的前多少位,所以可以使用无穷 List 来解决这个问题。 21 | 22 | var a = 0, b = 1; 23 | var list = List 24 | .generate(function(proxy) { 25 | proxy.yield(a); 26 | var aNext = b; 27 | var bNext = a + b; 28 | a = aNext; 29 | b = bNext; 30 | }) 31 | .drop(1) 32 | .scan(function(acc, i) { return acc * i; }, 1) 33 | .takeWhile(function(i) { return i < 10000; }); 34 | assert(list.length() == 8); 35 | -------------------------------------------------------------------------------- /doc/zh/list/secrets.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | List 内部实现 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 |
20 |

List 内部实现

21 | 22 |

如果你需要使用 List ,请参考对应的接口文档。本文档用于解释接口文档中没有提及的内部实现,但这部分实现随时可能被修改。

23 | 24 |

List.cache()

25 | 26 |
    27 |
  • type: instance
  • 28 |
  • input: none
  • 29 |
  • output: Array
  • 30 |
31 | 32 |

导出列表级别的缓存,以数组形式表示,仅用于调试时监视。修改导出缓存不影响 List 内部的实际缓存。

33 | 34 |

List.enumerator()

35 | 36 |
    37 |
  • type: instance
  • 38 |
  • input: none
  • 39 |
  • output: AbstractEnumerator
  • 40 |
41 | 42 |

获取列表的枚举器。枚举器应该是 AbstractEnumerator 的派生类。

43 | 44 |

List.enumerator().cache()

45 | 46 |
    47 |
  • type: instance
  • 48 |
  • input: none
  • 49 |
  • output: Array
  • 50 |
51 | 52 |

只有 CachedEnumerator 实例拥有该方法。导出枚举器级别的缓存,以数组形式表示,仅用于调试时监视。修改导出缓存不影响 List 内部的实际缓存。

53 | 54 |

List.ES5Array

55 | 56 |

表示 ECMAScript 5 Array 的类,提供 ECMAScript 5 Array 的数组方法,并且基于 List 构建。

57 | 58 |
var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0);
 59 | alert(array.indexOf(2));
 60 | alert(array.indexOf(2, 5));
 61 | alert(array.reduce(function(acc, i) { return acc * i; }));
 62 | 
63 | 64 |

List.ES5Array.indexOf()

65 | 66 |
    67 |
  • type: instance
  • 68 |
  • input: 69 |
    • searchElement
    • 70 |
    • fromIndex : Number (optional)
  • 71 |
  • output: index : Number
  • 72 |
73 | 74 |

搜索特定元素首次出现的位置,并返回索引。可以从指定的索引开始从左向右搜索。

75 | 76 |
var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0);
 77 | alert(array.indexOf(2));
 78 | alert(array.indexOf(2, 5));
 79 | 
80 | 81 |

List.ES5Array.lastIndexOf()

82 | 83 |
    84 |
  • type: instance
  • 85 |
  • input: 86 |
    • searchElement
    • 87 |
    • fromIndex : Number (optional)
  • 88 |
  • output: index : Number
  • 89 |
90 | 91 |

从右向左搜索特定元素首次出现的位置,并返回索引。可以从指定的索引开始从右向左搜索。

92 | 93 |
var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0);
 94 | alert(array.lastIndexOf(2));
 95 | alert(array.lastIndexOf(2, 5));
 96 | 
97 | 98 |

List.ES5Array.every()

99 | 100 |
    101 |
  • type: instance
  • 102 |
  • input: 103 |
    • callbackfn : Function
    • 104 |
    • thisArg (optional)
  • 105 |
  • output: result : Boolean
  • 106 |
107 | 108 |

检测数组是否每一项都满足特定条件。

109 | 110 |
var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0);
111 | alert(array.every(function(i) { return i > 0; });
112 | 
113 | 114 |

List.ES5Array.some()

115 | 116 |
    117 |
  • type: instance
  • 118 |
  • input: 119 |
    • callbackfn : Function
    • 120 |
    • thisArg (optional)
  • 121 |
  • output: result : Boolean
  • 122 |
123 | 124 |

检测数组是否至少有一项都满足特定条件。

125 | 126 |
var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0);
127 | alert(array.some(function(i) { return i > 0; });
128 | 
129 | 130 |

List.ES5Array.forEach()

131 | 132 |
    133 |
  • type: instance
  • 134 |
  • input: 135 |
    • callbackfn : Function
    • 136 |
    • thisArg (optional)
  • 137 |
  • output: undefined
  • 138 |
139 | 140 |

遍历数组,分别将每一项作为参数在调用回调函数时传入。

141 | 142 |
var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0);
143 | array.forEach(function(i) { alert(i); }
144 | 
145 | 146 |

List.ES5Array.map()

147 | 148 |
    149 |
  • type: instance
  • 150 |
  • input: 151 |
    • callbackfn : Function
    • 152 |
    • thisArg (optional)
  • 153 |
  • output: array : List.ES5Array
  • 154 |
155 | 156 |

遍历数组,分别将每一项作为参数在调用回调函数时传入,用回调函数的返回值组成新的数组。

157 | 158 |
var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0);
159 | array.map(function(i) { return i * 2; }
160 | 
161 | 162 |

List.ES5Array.filter()

163 | 164 |
    165 |
  • type: instance
  • 166 |
  • input: 167 |
    • callbackfn : Function
    • 168 |
    • thisArg (optional)
  • 169 |
  • output: array : List.ES5Array
  • 170 |
171 | 172 |

遍历数组,分别将每一项作为参数在调用回调函数时传入,过滤出回调函数返回 true 的项,并返回这些项组成的新数组。

173 | 174 |
var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0);
175 | array.filter(function(i) { return i > 2; }
176 | 
177 | 178 |

List.ES5Array.reduce()

179 | 180 |
    181 |
  • type: instance
  • 182 |
  • input: 183 |
    • callbackfn : Function
    • 184 |
    • initialValue (optional)
  • 185 |
  • output: result
  • 186 |
187 | 188 |

从左到右遍历数组,对数组的每一项进行归并运算,并返回归并运算的结果。如果给定初值,则使用初值和第一项进行归并;否则,使用第一项和第二项进行归并。

189 | 190 |
var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0);
191 | sum = array.reduce(function(acc, i) { return acc + i; }
192 | 
193 | 194 |

List.ES5Array.reduceRight()

195 | 196 |
    197 |
  • type: instance
  • 198 |
  • input: 199 |
    • callbackfn : Function
    • 200 |
    • initialValue (optional)
  • 201 |
  • output: result
  • 202 |
203 | 204 |

从右到左遍历数组,对数组的每一项进行归并运算,并返回归并运算的结果。如果给定初值,则使用初值和第一项进行归并;否则,使用第一项和第二项进行归并。

205 | 206 |
var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0);
207 | reversedString = array.reduceRight(function(acc, i) { return acc + i.toString(); }
208 | 
209 |
210 | 214 | 224 | 225 | -------------------------------------------------------------------------------- /doc/zh/list/secrets.md: -------------------------------------------------------------------------------- 1 | # List 内部实现 2 | 3 | 如果你需要使用 List ,请参考对应的接口文档。本文档用于解释接口文档中没有提及的内部实现,但这部分实现随时可能被修改。 4 | 5 | ## List.cache() 6 | 7 | * type: instance 8 | * input: none 9 | * output: Array 10 | 11 | 导出列表级别的缓存,以数组形式表示,仅用于调试时监视。修改导出缓存不影响 List 内部的实际缓存。 12 | 13 | ## List.enumerator() 14 | 15 | * type: instance 16 | * input: none 17 | * output: AbstractEnumerator 18 | 19 | 获取列表的枚举器。枚举器应该是 AbstractEnumerator 的派生类。 20 | 21 | ## List.enumerator().cache() 22 | 23 | * type: instance 24 | * input: none 25 | * output: Array 26 | 27 | 只有 CachedEnumerator 实例拥有该方法。导出枚举器级别的缓存,以数组形式表示,仅用于调试时监视。修改导出缓存不影响 List 内部的实际缓存。 28 | 29 | ## List.ES5Array 30 | 31 | 表示 ECMAScript 5 Array 的类,提供 ECMAScript 5 Array 的数组方法,并且基于 List 构建。 32 | 33 | var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0); 34 | alert(array.indexOf(2)); 35 | alert(array.indexOf(2, 5)); 36 | alert(array.reduce(function(acc, i) { return acc * i; })); 37 | 38 | ### List.ES5Array.indexOf() 39 | 40 | * type: instance 41 | * input: 42 | * searchElement 43 | * fromIndex : Number (optional) 44 | * output: index : Number 45 | 46 | 搜索特定元素首次出现的位置,并返回索引。可以从指定的索引开始从左向右搜索。 47 | 48 | var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0); 49 | alert(array.indexOf(2)); 50 | alert(array.indexOf(2, 5)); 51 | 52 | 53 | ### List.ES5Array.lastIndexOf() 54 | 55 | * type: instance 56 | * input: 57 | * searchElement 58 | * fromIndex : Number (optional) 59 | * output: index : Number 60 | 61 | 从右向左搜索特定元素首次出现的位置,并返回索引。可以从指定的索引开始从右向左搜索。 62 | 63 | var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0); 64 | alert(array.lastIndexOf(2)); 65 | alert(array.lastIndexOf(2, 5)); 66 | 67 | ### List.ES5Array.every() 68 | 69 | * type: instance 70 | * input: 71 | * callbackfn : Function 72 | * thisArg (optional) 73 | * output: result : Boolean 74 | 75 | 检测数组是否每一项都满足特定条件。 76 | 77 | var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0); 78 | alert(array.every(function(i) { return i > 0; }); 79 | 80 | ### List.ES5Array.some() 81 | 82 | * type: instance 83 | * input: 84 | * callbackfn : Function 85 | * thisArg (optional) 86 | * output: result : Boolean 87 | 88 | 检测数组是否至少有一项都满足特定条件。 89 | 90 | var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0); 91 | alert(array.some(function(i) { return i > 0; }); 92 | 93 | ### List.ES5Array.forEach() 94 | 95 | * type: instance 96 | * input: 97 | * callbackfn : Function 98 | * thisArg (optional) 99 | * output: undefined 100 | 101 | 遍历数组,分别将每一项作为参数在调用回调函数时传入。 102 | 103 | var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0); 104 | array.forEach(function(i) { alert(i); } 105 | 106 | ### List.ES5Array.map() 107 | 108 | * type: instance 109 | * input: 110 | * callbackfn : Function 111 | * thisArg (optional) 112 | * output: array : List.ES5Array 113 | 114 | 遍历数组,分别将每一项作为参数在调用回调函数时传入,用回调函数的返回值组成新的数组。 115 | 116 | var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0); 117 | array.map(function(i) { return i * 2; } 118 | 119 | ### List.ES5Array.filter() 120 | 121 | * type: instance 122 | * input: 123 | * callbackfn : Function 124 | * thisArg (optional) 125 | * output: array : List.ES5Array 126 | 127 | 遍历数组,分别将每一项作为参数在调用回调函数时传入,过滤出回调函数返回 true 的项,并返回这些项组成的新数组。 128 | 129 | var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0); 130 | array.filter(function(i) { return i > 2; } 131 | 132 | ### List.ES5Array.reduce() 133 | 134 | * type: instance 135 | * input: 136 | * callbackfn : Function 137 | * initialValue (optional) 138 | * output: result 139 | 140 | 从左到右遍历数组,对数组的每一项进行归并运算,并返回归并运算的结果。如果给定初值,则使用初值和第一项进行归并;否则,使用第一项和第二项进行归并。 141 | 142 | var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0); 143 | sum = array.reduce(function(acc, i) { return acc + i; } 144 | 145 | ### List.ES5Array.reduceRight() 146 | 147 | * type: instance 148 | * input: 149 | * callbackfn : Function 150 | * initialValue (optional) 151 | * output: result 152 | 153 | 从右到左遍历数组,对数组的每一项进行归并运算,并返回归并运算的结果。如果给定初值,则使用初值和第一项进行归并;否则,使用第一项和第二项进行归并。 154 | 155 | var array = new List.ES5Array(1, 2, 3, 4, 5, 4, 3, 2, 1, 0); 156 | reversedString = array.reduceRight(function(acc, i) { return acc + i.toString(); } 157 | -------------------------------------------------------------------------------- /doc/zh/overload/features.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Overload 说明文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 |
20 |

Overload 说明文档

21 | 22 |

Overload 是一个用于在 JavaScript 环境中快速创建函数重载的组件,让使用者能够通过简单的语言描述不同的重载入口,免除各个函数自行处理函数重载的麻烦。 Overload 的细设计目标及实现机制请参考 Weblab 系列文章《让 JavaScript 轻松支持函数重载 (Part 1, 2)》,本文仅用作面向开发者用户的说明文档。

23 | 24 |

Overload

25 | 26 |

用于创建重载入口函数的静态类。

27 | 28 |
var sum = Overload
 29 |     .add("Number, Number",
 30 |         function(x, y) { return x + y; })
 31 |     .add("Number, Number, Number",
 32 |         function(x, y, z) { return x + y + z; });
 33 | 
 34 | alert(sum(1, 2));
 35 | alert(sum(3, 4, 5));
 36 | 
37 | 38 |

Overload.add()

39 | 40 |
    41 |
  • type: static and instance
  • 42 |
  • input: 43 |
    • types : String or Array
    • 44 |
    • handler : Function
  • 45 |
  • output: overloaded : Function
  • 46 |
47 | 48 |

创建重载入口函数,并添加函数重载,或在已有入口函数上添加重载。

49 | 50 |
var concatenate = Overload
 51 |     .add("String, String"),
 52 |         function(s1, s2) { return s1 + s2; })
 53 |     .add("String, String, String"),
 54 |         function(s1, s2, s3) { return s1 + s2 + s3; });
 55 | 
 56 | concatenate
 57 |     .add("Array",
 58 |     function(array) { return array.join(""); })
 59 |     .add("Array, String",
 60 |         function(array, separator) { return array.join(separator); });
 61 | 
 62 | alert(concatenate("hello", " ", "world"));
 63 | alert(concatenate([1, 2, 3], " + "));
 64 | 
65 | 66 |

Any Argument

67 | 68 |

如果重载包括可以匹配任意类型的形参,这个类型使用 "*" 代替。

69 | 70 |
var add = Overload
 71 |     .add("*, *",
 72 |         function(x, y) { return x + y; })
 73 |     .add("*, *, *",
 74 |         function(x, y, z) { return x + y + z; });
 75 | 
 76 | alert(add(1, 2, 3));
 77 | alert(add("hello", " ", "world"));
 78 | 
79 | 80 |

More Argument

81 | 82 |

如果重载的形参个数可以是 n 个到无数个,将第 n 个参数使用 "..." 表示。

83 | 84 |
var sum = Overload
 85 |     .add("Number",
 86 |         function(x) { return x; })
 87 |     .add("Number, Number",
 88 |         function(x, y) { return x + y; })
 89 |     .add("Number, Number, Number",
 90 |         function(x, y, z) { return x + y + z; })
 91 |     .add("Number, Number, Number, ...",
 92 |         function(x, y, z, more) {
 93 |             return x + y + z + sum.apply(this, more);
 94 |         });
 95 | 
 96 | alert(sum(1, 2));
 97 | alert(sum(1, 2, 3));
 98 | alert(sum(1, 2, 3, 4, 5, 6));
 99 | 
100 | 101 |

Internal Class

102 | 103 |

如果重载的形参包括全局 eval 无法解释的类,形参列表可以以数组的形式传入 add 。匹配任意类型的 "*" 类型,可使用 Overload.Any 代替。匹配形参列表末端任意多个参数的 "..." ,可使用 Overload.More 代替。

104 | 105 |
var User = function(name) { this.name = name; };
106 | 
107 | var sayHello = Overload
108 |     .add("String",
109 |         function(string) { alert("Hello, " + string); }) 
110 |     .add("String, String",
111 |         function(string1, string2) { sayHello(string1 + " and " + string2); }) 
112 |     .add([User],
113 |         function(user) { sayHello(user.name); })
114 |     .add([User, User],
115 |         function(user1, user2) { sayHello(user1.name, user2.name); })
116 |     .add([Overload.Any],
117 |         function(object) { sayHello(object.toString()); })
118 |     .add([Overload.More],
119 |         function(objects) { sayHello(objects.join(" & ")); });
120 | 
121 | sayHello("World");
122 | sayHello(new User("Cat"), new User("Erik"));
123 | sayHello(1, 2, 3, 4, 5);
124 | 
125 | 126 |

Class Inheritance Resolution

127 | 128 |

如果多个重载的形参之间存在继承关系, Overload 会选择最匹配的唯一一个重载。如果不存在唯一一个最匹配的重载,则抛出错误。

129 | 130 |
var Parent = function() {};
131 | var Child = function() {};
132 | Child.prototype = new Parent();
133 | 
134 | var selectClass = Overload
135 |     .add([Parent],
136 |         function(parent) { return "[Parent]"; })
137 |     .add([Child],
138 |         function(child) { return "[Child]"; })
139 |     .add([Parent, Child],
140 |         function(parent, child) { return "[Parent, Child]"; })
141 |     .add([Child, Parent],
142 |         function(child, parent) { return "[Child Parent]"; });
143 | 
144 | alert(selectClass(new Parent()));
145 | alert(selectClass(new Child()));
146 | try {
147 |     alert(selectClass(new Parent(), new Parent()));
148 | } catch (e) {
149 |     alert (e);
150 | }
151 | try {
152 |     alert(selectClass(new Child(), new Child()));
153 | } catch (e) {
154 |     alert (e);
155 | }
156 | 
157 |
158 | 162 | 172 | 173 | -------------------------------------------------------------------------------- /doc/zh/overload/features.md: -------------------------------------------------------------------------------- 1 | # Overload 说明文档 2 | 3 | Overload 是一个用于在 JavaScript 环境中快速创建函数重载的组件,让使用者能够通过简单的语言描述不同的重载入口,免除各个函数自行处理函数重载的麻烦。 Overload 的细设计目标及实现机制请参考 Weblab 系列文章《让 JavaScript 轻松支持函数重载 (Part 1, 2)》,本文仅用作面向开发者用户的说明文档。 4 | 5 | ## Overload 6 | 7 | 用于创建重载入口函数的静态类。 8 | 9 | var sum = Overload 10 | .add("Number, Number", 11 | function(x, y) { return x + y; }) 12 | .add("Number, Number, Number", 13 | function(x, y, z) { return x + y + z; }); 14 | 15 | alert(sum(1, 2)); 16 | alert(sum(3, 4, 5)); 17 | 18 | ### Overload.add() 19 | 20 | * type: static and instance 21 | * input: 22 | * types : String or Array 23 | * handler : Function 24 | * output: overloaded : Function 25 | 26 | 创建重载入口函数,并添加函数重载,或在已有入口函数上添加重载。 27 | 28 | var concatenate = Overload 29 | .add("String, String"), 30 | function(s1, s2) { return s1 + s2; }) 31 | .add("String, String, String"), 32 | function(s1, s2, s3) { return s1 + s2 + s3; }); 33 | 34 | concatenate 35 | .add("Array", 36 | function(array) { return array.join(""); }) 37 | .add("Array, String", 38 | function(array, separator) { return array.join(separator); }); 39 | 40 | alert(concatenate("hello", " ", "world")); 41 | alert(concatenate([1, 2, 3], " + ")); 42 | 43 | #### Any Argument 44 | 45 | 如果重载包括可以匹配任意类型的形参,这个类型使用 "*" 代替。 46 | 47 | var add = Overload 48 | .add("*, *", 49 | function(x, y) { return x + y; }) 50 | .add("*, *, *", 51 | function(x, y, z) { return x + y + z; }); 52 | 53 | alert(add(1, 2, 3)); 54 | alert(add("hello", " ", "world")); 55 | 56 | #### More Argument 57 | 58 | 如果重载的形参个数可以是 n 个到无数个,将第 n 个参数使用 "..." 表示。 59 | 60 | var sum = Overload 61 | .add("Number", 62 | function(x) { return x; }) 63 | .add("Number, Number", 64 | function(x, y) { return x + y; }) 65 | .add("Number, Number, Number", 66 | function(x, y, z) { return x + y + z; }) 67 | .add("Number, Number, Number, ...", 68 | function(x, y, z, more) { 69 | return x + y + z + sum.apply(this, more); 70 | }); 71 | 72 | alert(sum(1, 2)); 73 | alert(sum(1, 2, 3)); 74 | alert(sum(1, 2, 3, 4, 5, 6)); 75 | 76 | #### Internal Class 77 | 78 | 如果重载的形参包括全局 eval 无法解释的类,形参列表可以以数组的形式传入 add 。匹配任意类型的 "*" 类型,可使用 Overload.Any 代替。匹配形参列表末端任意多个参数的 "..." ,可使用 Overload.More 代替。 79 | 80 | var User = function(name) { this.name = name; }; 81 | 82 | var sayHello = Overload 83 | .add("String", 84 | function(string) { alert("Hello, " + string); }) 85 | .add("String, String", 86 | function(string1, string2) { sayHello(string1 + " and " + string2); }) 87 | .add([User], 88 | function(user) { sayHello(user.name); }) 89 | .add([User, User], 90 | function(user1, user2) { sayHello(user1.name, user2.name); }) 91 | .add([Overload.Any], 92 | function(object) { sayHello(object.toString()); }) 93 | .add([Overload.More], 94 | function(objects) { sayHello(objects.join(" & ")); }); 95 | 96 | sayHello("World"); 97 | sayHello(new User("Cat"), new User("Erik")); 98 | sayHello(1, 2, 3, 4, 5); 99 | 100 | #### Class Inheritance Resolution 101 | 102 | 如果多个重载的形参之间存在继承关系, Overload 会选择最匹配的唯一一个重载。如果不存在唯一一个最匹配的重载,则抛出错误。 103 | 104 | var Parent = function() {}; 105 | var Child = function() {}; 106 | Child.prototype = new Parent(); 107 | 108 | var selectClass = Overload 109 | .add([Parent], 110 | function(parent) { return "[Parent]"; }) 111 | .add([Child], 112 | function(child) { return "[Child]"; }) 113 | .add([Parent, Child], 114 | function(parent, child) { return "[Parent, Child]"; }) 115 | .add([Child, Parent], 116 | function(child, parent) { return "[Child Parent]"; }); 117 | 118 | alert(selectClass(new Parent())); 119 | alert(selectClass(new Child())); 120 | try { 121 | alert(selectClass(new Parent(), new Parent())); 122 | } catch (e) { 123 | alert (e); 124 | } 125 | try { 126 | alert(selectClass(new Child(), new Child())); 127 | } catch (e) { 128 | alert (e); 129 | } 130 | -------------------------------------------------------------------------------- /doc/zh/overload/introduction.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Overload 快速入门 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 |
20 |

Overload 快速入门

21 | 22 |

使用 Overload.add ,我们可以创建一个支持重载的函数,并添加第一个重载。

23 | 24 |
var sum = Overload
25 |     .add("Number, Number",
26 |         function(x, y) { return x + y; })
27 | 
28 | 29 |

再次使用 add ,可以添加下一个重载。

30 | 31 |
sum
32 |     .add("Number, Number, Number",
33 |         function(x, y, z) { return x + y + z; });
34 | 
35 | 36 |

之后只要你调用这个函数,对应的重载就会被调用。

37 | 38 |
alert(sum(1, 2));
39 | alert(sum(3, 4, 5));
40 | 
41 | 42 |

如果形参列表包含自定义类型,你可以使用数组来表示某一个重载的形参列表。

43 | 44 |
var User = function(name) { this.name = name; };
45 | 
46 | var sayHello = Overload
47 |     .add([User]
48 |         function(user) { alert("Hello, " + user.name); });
49 | 
50 | 51 |

对于函数的调用者而言,这一切都是透明的。同时, Overload 也为组织函数逻辑带来了极大的方便。

52 |
53 | 57 | 67 | 68 | -------------------------------------------------------------------------------- /doc/zh/overload/introduction.md: -------------------------------------------------------------------------------- 1 | # Overload 快速入门 2 | 3 | 使用 Overload.add ,我们可以创建一个支持重载的函数,并添加第一个重载。 4 | 5 | var sum = Overload 6 | .add("Number, Number", 7 | function(x, y) { return x + y; }) 8 | 9 | 再次使用 add ,可以添加下一个重载。 10 | 11 | sum 12 | .add("Number, Number, Number", 13 | function(x, y, z) { return x + y + z; }); 14 | 15 | 之后只要你调用这个函数,对应的重载就会被调用。 16 | 17 | alert(sum(1, 2)); 18 | alert(sum(3, 4, 5)); 19 | 20 | 如果形参列表包含自定义类型,你可以使用数组来表示某一个重载的形参列表。 21 | 22 | var User = function(name) { this.name = name; }; 23 | 24 | var sayHello = Overload 25 | .add([User] 26 | function(user) { alert("Hello, " + user.name); }); 27 | 28 | 对于函数的调用者而言,这一切都是透明的。同时, Overload 也为组织函数逻辑带来了极大的方便。 29 | -------------------------------------------------------------------------------- /doc/zh/overload/secrets.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Overload 内部实现 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 |
20 |

Overload 内部实现

21 | 22 |

如果你需要使用 Overload ,请参考对应的接口文档。本文档用于解释接口文档中没有提及的内部实现,但这部分实现随时可能被修改。

23 | 24 |

Overload

25 | 26 |

Overload.create()

27 | 28 |
    29 |
  • type: static
  • 30 |
  • input: none
  • 31 |
  • output: overloaded : Function
  • 32 |
33 | 34 |

创建重载方法入口,但不添加任何函数重载。静态的 add 方法实际上是调用了静态的 create 方法后,获得了实例再调用实例的 add 方法。

35 | 36 |
var sum = Overload.create();
 37 | 
 38 | sum
 39 |     .add("Number, Number",
 40 |         function(x, y) { return x + y; })
 41 | 
 42 | sum
 43 |     .add("Number, Number, Number",
 44 |         function(x, y, z) { return x + y + z; });
 45 | 
46 | 47 |

Overload.match()

48 | 49 |
    50 |
  • type: instance
  • 51 |
  • input: 52 |
    • arguments : Array
  • 53 |
  • output: overloads : Array
  • 54 |
55 | 56 |

根据给定的实参列表,筛选出所有类型匹配的函数重载。

57 | 58 |
var User = function(name) { this.name = name; };
 59 | 
 60 | var sayHello = Overload
 61 |     .add("String",
 62 |         function(string) { alert("Hello, " + string); }) 
 63 |     .add([User],
 64 |         function(user) { sayHello(user.name); })
 65 |     .add("*",
 66 |         function(object) { sayHello(object.toString()); })
 67 | 
 68 | alert(sayHello.match(["Cat"]).length);
 69 | 
70 | 71 |

Overload.select()

72 | 73 |
    74 |
  • type: instance
  • 75 |
  • input: 76 |
    • arguments : Array
  • 77 |
  • output: overload : Function
  • 78 |
79 | 80 |

根据给定的实参列表,筛选出唯一一个最匹配的函数重载。如果不存在惟一一个最匹配的函数重载,则返回 null 。

81 | 82 |
var User = function(name) { this.name = name; };
 83 | 
 84 | var sayHello = Overload
 85 |     .add("String",
 86 |         function(string) { alert("Hello, " + string); }) 
 87 |     .add([User],
 88 |         function(user) { sayHello(user.name); })
 89 |     .add("*",
 90 |         function(object) { sayHello(object.toString()); })
 91 | 
 92 | alert(sayHello.select(["Cat"]));
 93 | 
94 |
95 | 99 | 109 | 110 | -------------------------------------------------------------------------------- /doc/zh/overload/secrets.md: -------------------------------------------------------------------------------- 1 | # Overload 内部实现 2 | 3 | 如果你需要使用 Overload ,请参考对应的接口文档。本文档用于解释接口文档中没有提及的内部实现,但这部分实现随时可能被修改。 4 | 5 | ## Overload 6 | 7 | ### Overload.create() 8 | 9 | * type: static 10 | * input: none 11 | * output: overloaded : Function 12 | 13 | 创建重载方法入口,但不添加任何函数重载。静态的 add 方法实际上是调用了静态的 create 方法后,获得了实例再调用实例的 add 方法。 14 | 15 | var sum = Overload.create(); 16 | 17 | sum 18 | .add("Number, Number", 19 | function(x, y) { return x + y; }) 20 | 21 | sum 22 | .add("Number, Number, Number", 23 | function(x, y, z) { return x + y + z; }); 24 | 25 | ### Overload.match() 26 | 27 | * type: instance 28 | * input: 29 | * arguments : Array 30 | * output: overloads : Array 31 | 32 | 根据给定的实参列表,筛选出所有类型匹配的函数重载。 33 | 34 | var User = function(name) { this.name = name; }; 35 | 36 | var sayHello = Overload 37 | .add("String", 38 | function(string) { alert("Hello, " + string); }) 39 | .add([User], 40 | function(user) { sayHello(user.name); }) 41 | .add("*", 42 | function(object) { sayHello(object.toString()); }) 43 | 44 | alert(sayHello.match(["Cat"]).length); 45 | 46 | ### Overload.select() 47 | 48 | * type: instance 49 | * input: 50 | * arguments : Array 51 | * output: overload : Function 52 | 53 | 根据给定的实参列表,筛选出唯一一个最匹配的函数重载。如果不存在惟一一个最匹配的函数重载,则返回 null 。 54 | 55 | var User = function(name) { this.name = name; }; 56 | 57 | var sayHello = Overload 58 | .add("String", 59 | function(string) { alert("Hello, " + string); }) 60 | .add([User], 61 | function(user) { sayHello(user.name); }) 62 | .add("*", 63 | function(object) { sayHello(object.toString()); }) 64 | 65 | alert(sayHello.select(["Cat"])); 66 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const Async = require('./src/async'); 2 | const Overload = require('./src/overload'); 3 | const Central = require('./src/central'); 4 | const GrandCentral = require('./src/grandcentral'); 5 | const List = require('./src/list'); 6 | 7 | module.exports = { 8 | Async: Async, 9 | Overload: Overload, 10 | Central: Central, 11 | GrandCentral: GrandCentral, 12 | List: List 13 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jshelpers", 3 | "description": "Helpers for JavaScript", 4 | "homepage": "http://catchen.github.com/jsHelpers/", 5 | "version": "1.0.8", 6 | "author": { 7 | "name": "Cat Chen", 8 | "email": "catchen@catchen.me", 9 | "web": "http://catchen.me" 10 | }, 11 | "main": "./index.js", 12 | "directories": { 13 | "lib": "./src", 14 | "doc": "./doc", 15 | "example": "./samples" 16 | }, 17 | "devDependencies": { 18 | "uglify-js": ">=1.0.3", 19 | "showdown": ">=0.0.1", 20 | "mustache": ">=0.3.1-dev" 21 | }, 22 | "repositories": [ 23 | { 24 | "type": "git", 25 | "url": "git://github.com/CatChen/jsHelpers.git" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /samples/async/00-async-ajax.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | Async Ajax 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 28 | 29 | 30 | 31 | 32 |
loading...
33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /samples/async/01-async-chain.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | Async Chain 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 33 | 34 | 35 | 36 | 37 |
loading...
38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /samples/async/async-ajax.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var Ajax = window.Ajax = function() {}; 3 | 4 | Ajax.get = function(url, data) { 5 | var operation = new Async.Operation(); 6 | $.get(url, data, function(result) { operation.yield(result); }, "json"); 7 | return operation; 8 | }; 9 | 10 | Ajax.post = function(url, data) { 11 | var operation = new Async.Operation(); 12 | $.post(url, data, function(result) { operation.yield(result); }, "json"); 13 | return operation; 14 | }; 15 | })(); 16 | -------------------------------------------------------------------------------- /samples/async/fake/json.js: -------------------------------------------------------------------------------- 1 | { 2 | "text": "hello world", 3 | "next": "fake/next-json.js" 4 | } -------------------------------------------------------------------------------- /samples/async/fake/next-json.js: -------------------------------------------------------------------------------- 1 | { 2 | "text": "you got me" 3 | } -------------------------------------------------------------------------------- /src/central.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var Central = {}; 3 | if (typeof module != 'undefined' && module.exports) { 4 | module.exports = Central; 5 | } else if (typeof YUI != 'undefined' && YUI.add) { 6 | YUI.add('central', function(Y) { 7 | Y.Central = Central; 8 | }, '1.0.6', { 9 | requires: [] 10 | }) 11 | } else if (typeof window == 'object') { 12 | window.Central = Central; 13 | } else { 14 | return; 15 | } 16 | 17 | var initiateCentralService = function(target) { 18 | var listeners = {}; 19 | 20 | target.listen = function(command, handler) { 21 | listeners[command] = listeners[command] || []; 22 | var i = 0; 23 | var handlers = listeners[command]; 24 | while (i < handlers.length && handlers[i] != handlers.length) { 25 | i++; 26 | } 27 | if (i == handlers.length) { 28 | handlers[handlers.length] = handler; 29 | } 30 | return target; 31 | }; 32 | 33 | target.call = function(command, argument) { 34 | if (listeners[command]) { 35 | var i; 36 | var handlers = listeners[command]; 37 | for (i = 0; i < handlers.length; i++) { 38 | try { 39 | handlers[i](argument); 40 | } catch (error) {} 41 | } 42 | } 43 | return target; 44 | }; 45 | }; 46 | 47 | Central.extend = function(target) { 48 | initiateCentralService(target); 49 | return target; 50 | }; 51 | 52 | Central.extend(Central); 53 | })(); 54 | -------------------------------------------------------------------------------- /src/grandcentral.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var GrandCentral = {}; 3 | if (typeof module != 'undefined' && module.exports) { 4 | module.exports = GrandCentral; 5 | } else if (typeof YUI != 'undefined' && YUI.add) { 6 | YUI.add('grandcentral', function(Y) { 7 | Y.GrandCentral = GrandCentral; 8 | }, '1.0.6', { 9 | requires: [] 10 | }) 11 | } else if (typeof window == 'object') { 12 | window.GrandCentral = GrandCentral; 13 | } else { 14 | return; 15 | } 16 | 17 | var operators = GrandCentral.Operators = {}; 18 | 19 | operators[""] = function(testValue, value) { 20 | if (testValue instanceof Array) { 21 | return operators["in"].apply(this, arguments); 22 | } else if (testValue instanceof RegExp) { 23 | return operators["re"].apply(this, arguments); 24 | } else if (testValue instanceof Function) { 25 | return operators["ld"].apply(this, arguments); 26 | } else { 27 | return operators["eq"].apply(this, arguments); 28 | } 29 | }; 30 | 31 | operators["eq"] = function(testValue, value) { 32 | if (arguments.length < 2) { 33 | return false; 34 | } 35 | if (testValue === null || testValue === undefined || value === null || value === undefined) { 36 | return (value === testValue); 37 | } 38 | switch (testValue.constructor) { 39 | case String: 40 | case Number: 41 | case Boolean: 42 | return testValue.constructor == value.constructor && testValue == value; 43 | default: 44 | if (testValue instanceof Array) { 45 | if (!(value instanceof Array)) { 46 | return false; 47 | } 48 | if (value.length != testValue.length) { 49 | return false; 50 | } 51 | for (var i = 0; i < testValue.length; i++) { 52 | if (!operators["eq"](testValue[i], value[i])) { 53 | return false; 54 | } 55 | } 56 | return true; 57 | } else { 58 | if (!(value instanceof Object)) { 59 | return false; 60 | } 61 | /* assuming that something is neither String, Number, Boolean, nor Array is Object */ 62 | for (var key in testValue) { 63 | var index = key.lastIndexOf("$"); 64 | var valueKey; 65 | var childValue; 66 | var childTestValue = testValue[key]; 67 | var operator; 68 | if (index < 0) { 69 | valueKey = key; 70 | operator = ""; 71 | } else { 72 | valueKey = key.substr(0, index); 73 | operator = key.substr(index + 1); 74 | } 75 | if (operators[operator]) { 76 | if (valueKey in value) { 77 | childValue = value[valueKey]; 78 | if (!operators[operator](childTestValue, childValue)) { 79 | return false; 80 | } 81 | } else { 82 | if (!operators[operator](childTestValue)) { 83 | return false; 84 | } 85 | } 86 | } else { 87 | throw "operator doesn't exist: " + operator; 88 | } 89 | } 90 | return true; 91 | } 92 | } 93 | }; 94 | 95 | operators["ne"] = function(testValue, value) { return !operators["eq"](testValue, value); }; 96 | operators["lt"] = function(testValue, value) { return arguments.length == 2 && value < testValue; }; 97 | operators["lte"] = function(testValue, value) { return arguments.length == 2 && value <= testValue; }; 98 | operators["gt"] = function(testValue, value) { return arguments.length == 2 && value > 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 | -------------------------------------------------------------------------------- /src/overload.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var Overload = {}; 3 | if (typeof module != 'undefined' && module.exports) { 4 | module.exports = Overload; 5 | } else if (typeof YUI != 'undefined' && YUI.add) { 6 | YUI.add('overload', function(Y) { 7 | Y.Overload = Overload; 8 | }, '1.0.6', { 9 | requires: [] 10 | }) 11 | } else if (typeof window == 'object') { 12 | window.Overload = Overload; 13 | } else { 14 | return; 15 | } 16 | 17 | var copySignature = function(signature) { 18 | var copy = signature.slice(0); 19 | if (signature.more) { 20 | copy.more = true; 21 | } 22 | return copy; 23 | }; 24 | 25 | var parseSignature = function(signature) { 26 | if (signature.replace(/(^\s+|\s+$)/ig, "") === "") { 27 | signature = []; 28 | } else { 29 | signature = signature.split(","); 30 | for (var i = 0; i < signature.length; i++) { 31 | var typeExpression = signature[i].replace(/(^\s+|\s+$)/ig, ""); 32 | var type = null; 33 | if (typeExpression == "*") { 34 | type = Overload.Any; 35 | } else if (typeExpression == "...") { 36 | type = Overload.More; 37 | } else { 38 | try { 39 | type = eval("(" + typeExpression + ")"); 40 | } catch (error) { 41 | throw "type expression cannot be evaluated: " + typeExpression; 42 | } 43 | } 44 | signature[i] = type; 45 | } 46 | } 47 | return signature; 48 | }; 49 | 50 | var inheritanceComparator = function(type1, type2) { 51 | if (type1 == type2) { 52 | return 0; 53 | } else if (type2 == Overload.Any) { 54 | return 1; 55 | } else if (type1 == Overload.Any) { 56 | return -1; 57 | } else if (type1.prototype instanceof type2) { 58 | return 1; 59 | } else if (type2.prototype instanceof type1) { 60 | return -1; 61 | } else { 62 | return 0; 63 | } 64 | }; 65 | 66 | var overloadComparator = function(overload1, overload2) { 67 | var signature1Better = false; 68 | var signature2Better = false; 69 | var signature1 = overload1.signature; 70 | var signature2 = overload2.signature; 71 | if (!signature1.more && signature2.more) { 72 | /* Function.more only exists in the second signature */ 73 | signature1Better = true; 74 | signature1 = copySignature(signature1); 75 | signature1.length = signature2.length; 76 | } else if (signature1.more && !signature2.more) { 77 | /* Function.more only exists in the first signature */ 78 | signature2Better = true; 79 | signature2 = copySignature(signature2); 80 | signature2.length = signature1.length; 81 | } else if(signature1.more && signature2.more) { 82 | /* Function.more exisits in both signature */ 83 | if (signature1.length > 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 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | QUnit Test Suite 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 33 | 34 | 35 |

QUnit Test Suite

36 |

37 |
38 |

39 |
    40 |
    test markup
    41 | 42 | 43 | -------------------------------------------------------------------------------- /test/javascripts/central.js: -------------------------------------------------------------------------------- 1 | function testCentral() { 2 | module("central core"); 3 | 4 | var argument = {}; 5 | 6 | test("central existence", function() { 7 | expect(4); 8 | 9 | ok(Central, "Central exists"); 10 | ok(Central.listen, "Central.listen exists"); 11 | ok(Central.call, "Central.call exists"); 12 | ok(Central.extend, "Central.extend exists"); 13 | }); 14 | 15 | test("listen only", function() { 16 | expect(0); 17 | 18 | Central.listen("listen-only", function(e) { 19 | ok(false, "nobody calls this"); 20 | }); 21 | }); 22 | 23 | test("call only", function() { 24 | expect(0); 25 | 26 | Central.call("call-only", argument); 27 | }); 28 | 29 | test("single call single listen", function() { 30 | expect(2); 31 | 32 | Central.listen("single-call-single-listen", function(e) { 33 | ok(true, "listener called"); 34 | equals(e, argument, "argument"); 35 | }); 36 | 37 | Central.call("single-call-single-listen", argument); 38 | }); 39 | 40 | test("multiple call single listen", function() { 41 | expect(4); 42 | 43 | Central.listen("multiple-call-single-listen", function(e) { 44 | ok(true, "listener called"); 45 | equals(e, argument, "argument"); 46 | }); 47 | 48 | Central.call("multiple-call-single-listen", argument); 49 | Central.call("multiple-call-single-listen", argument); 50 | }); 51 | 52 | test("single call multiple listen", function() { 53 | expect(4); 54 | 55 | Central 56 | .listen("single-call-multiple-listen", function(e) { 57 | ok(true, "first listener called"); 58 | equals(e, argument, "argument"); 59 | }) 60 | .listen("single-call-multiple-listen", function(e) { 61 | ok(true, "second listener called"); 62 | equals(e, argument, "argument"); 63 | }); 64 | 65 | Central.call("single-call-multiple-listen", argument); 66 | }); 67 | 68 | test("multiple call multiple listen", function() { 69 | expect(8); 70 | 71 | Central 72 | .listen("multiple-call-multiple-listen", function(e) { 73 | ok(true, "first listener called"); 74 | equals(e, argument, "argument"); 75 | }) 76 | .listen("multiple-call-multiple-listen", function(e) { 77 | ok(true, "second listener called"); 78 | equals(e, argument, "argument"); 79 | }); 80 | 81 | Central.call("multiple-call-multiple-listen", argument); 82 | Central.call("multiple-call-multiple-listen", argument); 83 | }); 84 | 85 | test("multiple command", function() { 86 | expect(8); 87 | 88 | Central 89 | .listen("command-one", function(e) { 90 | ok(true, "command-one listener called"); 91 | equals(e, argument, "argument"); 92 | }) 93 | .listen("command-two", function(e) { 94 | ok(true, "command-two listener called"); 95 | equals(e, argument, "argument"); 96 | }); 97 | 98 | Central 99 | .listen("command-one", function(e) { 100 | ok(true, "command-one listener called"); 101 | equals(e, argument, "argument"); 102 | }) 103 | .listen("command-two", function(e) { 104 | ok(true, "command-two listener called"); 105 | equals(e, argument, "argument"); 106 | }); 107 | 108 | Central.call("command-one", argument); 109 | Central.call("command-two", argument); 110 | }); 111 | 112 | test("empty command", function() { 113 | expect(2); 114 | 115 | Central.listen("", function(e) { 116 | ok(true, "listener called"); 117 | equals(e, argument, "argument"); 118 | }); 119 | 120 | Central.call("", argument); 121 | }); 122 | 123 | module("central extension"); 124 | 125 | test("central extension", function() { 126 | expect(2); 127 | 128 | var extended = Central.extend({}); 129 | 130 | ok(extended.listen, "extended.listen exists"); 131 | ok(extended.call, "extended.call exists"); 132 | }); 133 | 134 | test("extension multiple call multiple listen", function() { 135 | expect(8); 136 | 137 | var extended = Central.extend({}); 138 | 139 | extended 140 | .listen("command-one", function(e) { 141 | ok(true, "command-one listener called"); 142 | equals(e, argument, "argument"); 143 | }) 144 | .listen("command-two", function(e) { 145 | ok(true, "command-two listener called"); 146 | equals(e, argument, "argument"); 147 | }); 148 | 149 | extended 150 | .listen("command-one", function(e) { 151 | ok(true, "command-one listener called"); 152 | equals(e, argument, "argument"); 153 | }) 154 | .listen("command-two", function(e) { 155 | ok(true, "command-two listener called"); 156 | equals(e, argument, "argument"); 157 | }); 158 | 159 | extended.call("command-one", argument); 160 | extended.call("command-two", argument); 161 | }); 162 | } 163 | -------------------------------------------------------------------------------- /test/stylesheets/qunit.css: -------------------------------------------------------------------------------- 1 | /** Font Family and Sizes */ 2 | 3 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 4 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; 5 | } 6 | 7 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 8 | #qunit-tests { font-size: smaller; } 9 | 10 | 11 | /** Resets */ 12 | 13 | #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | 19 | /** Header */ 20 | 21 | #qunit-header { 22 | padding: 0.5em 0 0.5em 1em; 23 | 24 | color: #8699a4; 25 | background-color: #0d3349; 26 | 27 | font-size: 1.5em; 28 | line-height: 1em; 29 | font-weight: normal; 30 | 31 | border-radius: 15px 15px 0 0; 32 | -moz-border-radius: 15px 15px 0 0; 33 | -webkit-border-top-right-radius: 15px; 34 | -webkit-border-top-left-radius: 15px; 35 | } 36 | 37 | #qunit-header a { 38 | text-decoration: none; 39 | color: #c2ccd1; 40 | } 41 | 42 | #qunit-header a:hover, 43 | #qunit-header a:focus { 44 | color: #fff; 45 | } 46 | 47 | #qunit-banner { 48 | height: 5px; 49 | } 50 | 51 | #qunit-testrunner-toolbar { 52 | padding: 0em 0 0.5em 2em; 53 | } 54 | 55 | #qunit-userAgent { 56 | padding: 0.5em 0 0.5em 2.5em; 57 | background-color: #2b81af; 58 | color: #fff; 59 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 60 | } 61 | 62 | 63 | /** Tests: Pass/Fail */ 64 | 65 | #qunit-tests { 66 | list-style-position: inside; 67 | } 68 | 69 | #qunit-tests li { 70 | padding: 0.4em 0.5em 0.4em 2.5em; 71 | border-bottom: 1px solid #fff; 72 | list-style-position: inside; 73 | } 74 | 75 | #qunit-tests li strong { 76 | cursor: pointer; 77 | } 78 | 79 | #qunit-tests ol { 80 | margin-top: 0.5em; 81 | padding: 0.5em; 82 | 83 | background-color: #fff; 84 | 85 | border-radius: 15px; 86 | -moz-border-radius: 15px; 87 | -webkit-border-radius: 15px; 88 | 89 | box-shadow: inset 0px 2px 13px #999; 90 | -moz-box-shadow: inset 0px 2px 13px #999; 91 | -webkit-box-shadow: inset 0px 2px 13px #999; 92 | } 93 | 94 | /*** Test Counts */ 95 | 96 | #qunit-tests b.counts { color: black; } 97 | #qunit-tests b.passed { color: #5E740B; } 98 | #qunit-tests b.failed { color: #710909; } 99 | 100 | #qunit-tests li li { 101 | margin: 0.5em; 102 | padding: 0.4em 0.5em 0.4em 0.5em; 103 | background-color: #fff; 104 | border-bottom: none; 105 | list-style-position: inside; 106 | } 107 | 108 | /*** Passing Styles */ 109 | 110 | #qunit-tests li li.pass { 111 | color: #5E740B; 112 | background-color: #fff; 113 | border-left: 26px solid #C6E746; 114 | } 115 | 116 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 117 | #qunit-tests .pass .test-name { color: #366097; } 118 | 119 | #qunit-tests .pass .test-actual, 120 | #qunit-tests .pass .test-expected { color: #999999; } 121 | 122 | #qunit-banner.qunit-pass { background-color: #C6E746; } 123 | 124 | /*** Failing Styles */ 125 | 126 | #qunit-tests li li.fail { 127 | color: #710909; 128 | background-color: #fff; 129 | border-left: 26px solid #EE5757; 130 | } 131 | 132 | #qunit-tests .fail { color: #000000; background-color: #EE5757; } 133 | #qunit-tests .fail .test-name, 134 | #qunit-tests .fail .module-name { color: #000000; } 135 | 136 | #qunit-tests .fail .test-actual { color: #EE5757; } 137 | #qunit-tests .fail .test-expected { color: green; } 138 | 139 | #qunit-banner.qunit-fail, 140 | #qunit-testrunner-toolbar { background-color: #EE5757; } 141 | 142 | 143 | /** Footer */ 144 | 145 | #qunit-testresult { 146 | padding: 0.5em 0.5em 0.5em 2.5em; 147 | 148 | color: #2b81af; 149 | background-color: #D2E0E6; 150 | 151 | border-radius: 0 0 15px 15px; 152 | -moz-border-radius: 0 0 15px 15px; 153 | -webkit-border-bottom-right-radius: 15px; 154 | -webkit-border-bottom-left-radius: 15px; 155 | } 156 | 157 | /** Fixture */ 158 | 159 | #qunit-fixture { 160 | position: absolute; 161 | top: -10000px; 162 | left: -10000px; 163 | } 164 | --------------------------------------------------------------------------------