From 0745e6bd9efce4624c886a5c5b3a65d9842e80f9 Mon Sep 17 00:00:00 2001 From: Jeffrey Zhao Date: Sat, 21 Jul 2012 14:08:24 +0800 Subject: [PATCH] remove docs from jscex code repository --- doc/README.md | 1 - doc/package.json | 17 - doc/src/documents/index.html.md | 739 ------------------ .../scripts/sorting-animations.js.jscex | 92 --- doc/src/documents/zh-cn/index.html.md | 199 ----- .../zh-cn/manuals/async/index.html.md | 604 -------------- .../zh-cn/manuals/async/powerpack.html.md | 378 --------- .../manuals/async/samples/comments.html.md | 119 --- .../manuals/async/samples/copy-dir.html.md | 520 ------------ .../async/samples/jquery-bindings.html.md | 104 --- .../async/samples/modal-dialog.html.md | 124 --- .../zh-cn/manuals/builderbase/index.html.md | 46 -- .../zh-cn/manuals/core/index.html.md | 32 - .../documents/zh-cn/manuals/importing.html.md | 68 -- doc/src/documents/zh-cn/manuals/index.html.md | 22 - .../documents/zh-cn/manuals/jit/index.html.md | 46 -- .../zh-cn/manuals/parser/index.html.md | 46 -- doc/src/files/scripts/highlight.pack.js | 1 - doc/src/files/scripts/jquery-1.7.2.min.js | 4 - .../files/scripts/jscex-async.bundle.min.js | 28 - doc/src/files/scripts/manual.js | 53 -- doc/src/files/styles/highlight-vs.css | 84 -- doc/src/files/styles/images/blacktocat.png | Bin 1428 -> 0 bytes doc/src/files/styles/images/manual.png | Bin 320 -> 0 bytes doc/src/files/styles/main.css | 48 -- doc/src/files/styles/manual.css | 236 ------ doc/src/layouts/main-zh-cn.html.eco | 71 -- doc/src/layouts/main.html.eco | 45 -- doc/src/layouts/manual-zh-cn.html.eco | 50 -- 29 files changed, 3777 deletions(-) delete mode 100644 doc/README.md delete mode 100644 doc/package.json delete mode 100644 doc/src/documents/index.html.md delete mode 100644 doc/src/documents/scripts/sorting-animations.js.jscex delete mode 100644 doc/src/documents/zh-cn/index.html.md delete mode 100644 doc/src/documents/zh-cn/manuals/async/index.html.md delete mode 100644 doc/src/documents/zh-cn/manuals/async/powerpack.html.md delete mode 100644 doc/src/documents/zh-cn/manuals/async/samples/comments.html.md delete mode 100644 doc/src/documents/zh-cn/manuals/async/samples/copy-dir.html.md delete mode 100644 doc/src/documents/zh-cn/manuals/async/samples/jquery-bindings.html.md delete mode 100644 doc/src/documents/zh-cn/manuals/async/samples/modal-dialog.html.md delete mode 100644 doc/src/documents/zh-cn/manuals/builderbase/index.html.md delete mode 100644 doc/src/documents/zh-cn/manuals/core/index.html.md delete mode 100644 doc/src/documents/zh-cn/manuals/importing.html.md delete mode 100644 doc/src/documents/zh-cn/manuals/index.html.md delete mode 100644 doc/src/documents/zh-cn/manuals/jit/index.html.md delete mode 100644 doc/src/documents/zh-cn/manuals/parser/index.html.md delete mode 100644 doc/src/files/scripts/highlight.pack.js delete mode 100644 doc/src/files/scripts/jquery-1.7.2.min.js delete mode 100644 doc/src/files/scripts/jscex-async.bundle.min.js delete mode 100644 doc/src/files/scripts/manual.js delete mode 100644 doc/src/files/styles/highlight-vs.css delete mode 100644 doc/src/files/styles/images/blacktocat.png delete mode 100644 doc/src/files/styles/images/manual.png delete mode 100644 doc/src/files/styles/main.css delete mode 100644 doc/src/files/styles/manual.css delete mode 100644 doc/src/layouts/main-zh-cn.html.eco delete mode 100644 doc/src/layouts/main.html.eco delete mode 100644 doc/src/layouts/manual-zh-cn.html.eco diff --git a/doc/README.md b/doc/README.md deleted file mode 100644 index b907743..0000000 --- a/doc/README.md +++ /dev/null @@ -1 +0,0 @@ -The source of Jscex documentation generated by DocPad. \ No newline at end of file diff --git a/doc/package.json b/doc/package.json deleted file mode 100644 index 4330147..0000000 --- a/doc/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "jscex-doc", - "version": "0.6.0", - "author": "Jeffrey Zhao (http://zhaojie.me/)", - "description": "The source of Jscex documentation generated by DocPad.", - "homepage": "https://github.com/JeffreyZhao/jscex", - "main": "nothing.js", - "bugs": { - "url": "https://github.com/JeffreyZhao/jscex/issues", - "email": "jeffz@live.com" - }, - "dependencies": { - "docpad-plugin-eco": "x", - "docpad-plugin-markdown": "x", - "docpad-plugin-jscexc": "x" - } -} diff --git a/doc/src/documents/index.html.md b/doc/src/documents/index.html.md deleted file mode 100644 index 47e2ba2..0000000 --- a/doc/src/documents/index.html.md +++ /dev/null @@ -1,739 +0,0 @@ ---- -layout: main ---- - -### 中文用户请[点此访问中文版](./zh-cn/)。 - -"Jscex" is short for "JavaScript Computation EXpressions". It provides a monadic extensions for JavaScript language and would significantly improve your programming life in certain scenarios. The project is written in JavaScript completely, which mean it can be used in any execution engines support [ECMAScript 3](http://www.ecma-international.org/publications/standards/Ecma-262.htm), including mainstream browsers or server side JavaScript environments (e.g., [Node.js](http://nodejs.org/)). - -Currently features: - -* A JIT (just-in-time) compiler which generates codes **at runtime**, mainly used in development environment. -* An AOT (ahead-of-time) compiler which generates code **before runtime**. The AOT compiled code are independent of JIT compiler, which is target in production environment. -* An async library (with powerpack) which simplified async programming significantly. - -### Jscex asynchronous library - -Asynchronous programming is essential, especially in JavaScript. JavaScript doesn't provide any primitives for writing codes can block the execution. Any operations which take a period of time have to be written in async ways. Async operations usually send back the results by callback functions when finished, and we could do the rest of work in the callback. - -Here comes the problem. We usually express our work linearly, but async tasks with callbacks require logical division of algorithms. We cannot write conditions with if or loops with while/for/do statements. It's also very difficult to combine multiple asynchronous operations or handle exceptions and cancellation. - -So Jscex comes to the rescue with its asynchronous library. - -#### How to use - -##### Node.js - -If you want to use Jscex with Node.js, please install the following packages from npm: - - npm install jscex jscex-jit jscex-async jscex-async-powerpack - -then initialize these packages in your `js` file: - - var Jscex = require("jscex"); - require("jscex-jit").init(Jscex); - require("jscex-async").init(Jscex); - require("jscex-async-powerpack").init(Jscex); - -You can also copy all the files under "src" folder to get the latest build. - -##### Web page - -If you want to use Jscex with in web page, you can references the files under "src" folder in the pge: - - - - - - - - - - - - - - - -All the files list above are uncompressed version mainly used for developement. The minified version of them are in "bin" folder, which is suitable for production use (if you're not using AOT compile mode, see the "AOT compiler" section for more detail). Furthermore, the dev version of Jscex compiler would produce programmer-friendly code, which is easily for debugging and the minified version would run a little faster. - -##### Define an async function - -Now everything is prepared, here's how we compile a normal JavaScript function with async builder: - - var somethingAsync = eval(Jscex.compile("async", function (a, b) { - // implementation - })); - -Please visit the following sections for more details. - -#### Samples: - -All the following samples can be found in samples/async folder. - -##### Clock: - -We are going to draw a clock with HTML5 canvas on the page ([samples/async/clock.html](http://files.zhaojie.me/jscex/samples/async/clock.html)). It's rather easy for most front-end programmers: - - function drawClock(time) { - // clear and canvas and draw a clock on it. - } - - setInterval(1000, function () { - drawClock(new Date()); - }); - -That's the implementation with callback, but in Jscex we could write code like this: - - var drawClockAsync = eval(Jscex.compile("async", function (interval) { - while (true) { - drawClock(new Date()); - // wait for an async operation to complete - $await(Jscex.Async.sleep(interval)); - } - })); - - drawClockAsync(1000).start(); - -We wrote an infinite loop in the async method drawClockAsync, in each iteration we draw a clock with current time and call Jscex.Async.sleep method, the sleep operation **blocks** the code for 1 seconds. - -How can we block the code without the support of runtime? The magic here is: we are not actually executing the code we wrote, the Jscex.compile method accept the function we provide and convert it into another (it's not necessory for us to understand the code currently): - - (function (interval) { - var $$_builder_$$_0 = Jscex.builders["async"]; - return $$_builder_$$_0.Start(this, - $$_builder_$$_0.Loop( - function () { - return true; - }, - null, - $$_builder_$$_0.Delay(function () { - drawClock(new Date()); - return $$_builder_$$_0.Bind(Jscex.Async.sleep(interval), function () { - return $$_builder_$$_0.Normal(); - }); - }), - false - ) - ); - }) - -The string form of the new function generated by Jscex compiler will be dynamicly executed by eval method, preserving the current scope and context (variables, closures, etc.). - -The $await method is the "bind" operation of async builder, it tells the compiler to put the code after that in the callback for the builder's "Bind" method. The $await method accepts an async task generated by Jscex.Async.sleep, provides a semantic of "waiting the operation to complete". The Bind method also start the task if it's not running. - -It seems the implementation with Jscex is a bit longer in this simple case - please look at the following samples. They will tell you the real power of Jscex. - -##### Animations - -Animations are important for rich user interfaces. Let's build an animation like "move the element from here to there in a period of time" ([samples/async/move.html](http://files.zhaojie.me/jscex/samples/async/move.html)). The traditional version of the move could be: - - var moveTraditionally = function (e, startPos, endPos, duration, callback) { - - var t = 0; - - // move a bit - function move() { - e.style.left = startPos.x + (endPos.x - startPos.x) * t / duration; - e.style.top = startPos.y + (endPos.y - startPos.y) * t / duration; - - t += 50; - if (t < duration) { - setTimeout(50, move); - } else { // finished - e.style.left = endPos.x; - e.style.top = endPos.y; - callback(); - } - } - - setTimeout(50, move); - } - -Can someone tell me the algorithm used to move the element? Maybe we would understand the implementation after reading the code again and again, but it's really difficult and uncomfortable for the programmer to read and write codes like that. But everything would be changed with Jscex: - - var moveAsync = eval(Jscex.compile("async", function (e, startPos, endPos, duration) { - for (var t = 0; t < duration; t += 50) { - e.style.left = startPos.x + (endPos.x - startPos.x) * t / duration; - e.style.top = startPos.y + (endPos.y - startPos.y) * t / duration; - $await(Jscex.Async.sleep(50)); - } - - e.style.left = endPos.x; - e.style.top = endPos.y; - })); - -We could express our algorithm in normal way (linearly): loop with a for statement, sleep for 50 milliseconds in each iteration and move the element again. It's just simple and elegant. - -Now we got an async method moveAsync, we can use it when building another async method, just like the Jscex.Async.sleep method in the core library. They both return the async tasks implementing the same protocal for outside. So check out the method below: - - var moveSquareAsync = eval(Jscex.compile("async", function(e) { - $await(moveAsync(e, {x:100, y:100}, {x:400, y:100}, 1000)); - $await(moveAsync(e, {x:400, y:100}, {x:400, y:400}, 1000)); - $await(moveAsync(e, {x:400, y:400}, {x:100, y:400}, 1000)); - $await(moveAsync(e, {x:100, y:400}, {x:100, y:100}, 1000)); - })); - -We can easily find out how moveSquareAsync method works: it moves an element with a square routine. It's really easy to combine multiple async operations into another in Jscex. - -##### Sorting animations: - -Every programmer learns sorting algorithms, like bubble sort: - - var compare = function (x, y) { - return x - y; - } - - var swap = function (array, i, j) { - var t = array[x]; - array[x] = array[y]; - array[y] = t; - } - - var bubbleSort = function (array) { - for (var x = 0; x < array.length; x++) { - for (var y = 0; y < array.length - x; y++) { - if (compare(array[y], array[y + 1]) > 0) { - swap(array, y, y + 1); - } - } - } - } - -The basic idea of represent an sorting algorithm with animations is simple: repaint the graph after each swap and wait for a little time. But the implementation is not as easy as the idea looks like, we cannot use for loops anymore if we "stop" the code execution for some time using setTimeout. But let's check out the Jscex version: - - var compareAsync = eval(Jscex.compile("async", function (x, y) { - $await(Jscex.Async.sleep(10)); // each "compare" takes 10 ms. - return x - y; - })); - - var swapAsync = eval(Jscex.compile("async", function (array, x, y) { - var t = array[x]; - array[x] = array[y]; - array[y] = t; - - repaint(array); // repaint after each swap - - $await(Jscex.Async.sleep(20)); // each "swap" takes 20 ms. - })); - - var bubbleSortAsync = eval(Jscex.compile("async", function (array) { - for (var x = 0; x < array.length; x++) { - for (var y = 0; y < array.length - x; y++) { - var r = $await(compareAsync(array[y], array[y + 1])); - if(r > 0) { - $await(swapAsync(array, y, y + 1)); - } - } - } - })); - -I believe there's no need to explain more - it's just the standard "bubble sort" algorithm. And of course we can create animation for "quick sort": - - var _partitionAsync = eval(Jscex.compile("async", function (array, begin, end) { - var i = begin; - var j = end; - var pivot = array[Math.floor((begin + end) / 2)]; - - while (i <= j) { - while (true) { - var r = $await(compareAsync(array[i], pivot)); - if (r < 0) { i++; } else { break; } - } - - while (true) { - var r = $await(compareAsync(array[j], pivot)); - if (r > 0) { j--; } else { break; } - } - - if (i <= j) { - $await(swapAsync(array, i, j)); - i++; - j--; - } - } - - return i; - })); - - var _quickSortAsync = eval(Jscex.compile("async", function (array, begin, end) { - var index = $await(_partitionAsync(array, begin, end)); - - if (begin < index - 1) { - $await(_quickSortAsync(array, begin, index - 1)); - } - - if (index < end) { - $await(_quickSortAsync(array, index, end)); - } - })); - - var quickSortAsync = eval(Jscex.compile("async", function (array) { - $await(_quickSortAsync(array, 0, array.length - 1)); - })); - -The complete sample is in "[samples/async/sorting-animations.html](http://files.zhaojie.me/jscex/samples/async/sorting-animations.html)". After opening the page with browser support HTML5 canvas, you can click the links to view the animation of three sorting algorithms: [bubble sort](http://files.zhaojie.me/jscex/samples/async/sorting-animations.html?quick), [selection sort](http://files.zhaojie.me/jscex/samples/async/sorting-animations.html?selection) and [quick sort](http://files.zhaojie.me/jscex/samples/async/sorting-animations.html?quick). - -##### Tower of Hanoi - -[Tower of Hanoi](http://en.wikipedia.org/wiki/Tower_of_Hanoi) is a puzzle of moving discs. It has a simple, recursive solution: - -1. move n−1 discs from A to B (with the help of C). This leaves disc n alone on peg A -2. move disc n from A to C -3. move n−1 discs from B to C (which the help of A) so they sit on disc n - -which in code: - - var hanoi = function (n, from, to, mid) { - if (n > 0) hanoi(n - 1, from, mid, to); - moveDisc(n, from, to); - if (n > 0) hanoi(n - 1, mid, to from); - } - - hanoi(5, "A", "C", "B"); - -If we need to show the algorithm in animation ([samples/async/hanoi.html](http://files.zhaojie.me/jscex/samples/async/hanoi.html)), we could re-write the code just like the sample above: - - var hanoiAsync = eval(Jscex.compile("async", function(n, from, to, mid) { - if (n > 0) { - $await(hanoiAsync(n - 1, from, mid, to)); - } - - $await(moveDiscAsync(n, from, to)); - - if (n > 0) { - $await(hanoiAsync(n - 1, mid, to, from)); - } - })); - - hanoiAsync(5, "A", "C", "B").start(); - -If you open the sample page in the browser, you'll find the discs is moving one by one, resolving the puzzle automatically. Maybe it's not quite easy for someone to follow each step, so let's just make a little change: - - var hanoiAsync = eval(Jscex.compile("async", function(n, from, to, mid) { - if (n > 0) { - $await(hanoiAsync(n - 1, from, mid, to)); - } - - // wait for the button's being clicked - var btnNext = document.getElementById("btnNext"); - $await(Jscex.Async.onEvent(btnNext, "click")); - - $await(moveDiscAsync(n, from, to)); - - if (n > 0) { - $await(hanoiAsync(n - 1, mid, to, from)); - } - })); - -Before each moveDiscAsync operation, the program would wait for the button's "click" event. In Jscex async library, "async task" only means "operation would be finished in the future". In the example above, the button's "click" event is also an async task - the task would be finished [when the button is clicked](http://files.zhaojie.me/jscex/samples/async/hanoi-2.html), then the program keeps going, move a disc and wait for another click. - -People can write async programs without callbacks, that's how Jscex improve productivity and maintainability. - -##### Simple web-server with Node.js - -Jscex works for any execution engines support ECMAScript 3, not only browsers but also server-side environment like Node.js. Node.js is a server-side JavaScript environment that uses an asynchronous event-driven model. This allows Node.js to get excellent performance based on the architectures of many internet applications. - -Here's a simple file server build with Node.js: - - var http = require("http"); - var fs = require("fs"); - var url = require("url"); - var path = require("path"); - - var transferFile = function (request, response) { - var uri = url.parse(request.url).pathname; - var filepath = path.join(process.cwd(), uri); - - // check whether the file is exist and get the result from callback - path.exists(filepath, function (exists) { - if (!exists) { - response.writeHead(404, {"Content-Type": "text/plain"}); - response.write("404 Not Found\n"); - response.end(); - } else { - // read the file content and get the result from callback - fs.readFile(filepath, "binary", function (error, data) { - if (error) { - response.writeHead(500, {"Content-Type": "text/plain"}); - response.write(error + "\n"); - } else { - response.writeHead(200); - response.write(data, "binary"); - } - - response.end(); - }); - } - }); - } - - http.createServer(function (request, response) { - transferFile(request, response); - }).listen(8124, "127.0.0.1"); - -There're two async method used above: path.exists and fs.readFile. Most I/O api in Node.js are asynchronous, which brings great scalability but low programmability. But with the help of Jscex, we can write async programs as easy as normal code (samples/async/node-server.js): - - require("../../lib/uglifyjs-parser.js"); - require("../../src/jscex.js"); - require("../../src/jscex.async.js"); - require("../../src/jscex.async.node.js"); - - Jscex.Async.Node.Path.extend(path); - Jscex.Async.Node.FileSystem.extend(fs); - - var transferFileAsync = eval(Jscex.compile("async", function (request, response) { - var uri = url.parse(request.url).pathname; - var filepath = path.join(process.cwd(), uri); - - var exists = $await(path.existsAsync(filepath)); - if (!exists) { - response.writeHead(404, {"Content-Type": "text/plain"}); - response.write("404 Not Found\n"); - } else { - - try { - var data = $await(fs.readFileAsync(filepath)); - response.writeHead(200); - response.write(data, "binary"); - } catch (ex) { - response.writeHead(500, {"Content-Type": "text/plain"}); - response.write(ex + "\n"); - } - } - - response.end(); - })); - - http.createServer(function (request, response) { - transferFileAsync(request, response).start(); - }).listen(8125, "127.0.0.1"); - -All the Jscex files are compatible with [CommonJS](http://commonjs.org/) module specification, which can be loaded by require method in Node.js. Jscex async library has a simple model of "async tasks", everyone can easily write extensions/adaptors for existing async operations. Please check the following section for more details. - -#### Limitations: - -There're three limitations of the current version of Jscex - none of them becomes a real problem in my experiences. - -##### Need separate $await statement - -Jscex compiler can only handle explicit $await operation: - -* Simple: $await(...); -* Local declaration: var result = $await(...); -* Assignment: this.result = $await(...); -* Directly return: return $await(...); - -Other kinds of usages could not be compiled: - - f(g(1), $await(...)) - - if (x > y && $await(...)) { ... } - -We should put $await in separate statement: - - var a1 = g(1); - var a2 = $await(...); - f(a1, a2); - - if (x > y) { - var flag = $await(...); - if (flag) { ... } - } - -##### Nested Jscex functions - -If you write nested Jscex functions, the inner function can also be compiled with outside one. For example: - - var outerAsync = eval(Jscex.compile("async", function () { - - var innerAsync = eval(Jscex.compile("async", function () { - // inner implementations - })); - - })); - -At runtime, outerAsync would be compiled to: - - (function () { - var $$_builder_$$_0 = Jscex.builders["async"]; - return $$_builder_$$_0.Start(this, - $$_builder_$$_0.Delay(function () { - - var innerAsync = (function () { - var $$_builder_$$_1 = Jscex.builders["async"]; - return $$_builder_$$_1.Start(this, - // compiled inner implementations - $$_builder_$$_1.Normal() - ); - }); - - return $$_builder_$$_0.Normal(); - }) - ); - }) - -But the compilation (of inner function) is based on static code parsing and generation, so the compiler can only recognize the "standard pattern": eval(Jscex.compile("builderName", function () { ... })). Please see the section "AOT Compiler - Limitation" for more details. - -##### Language support: - -Jscex compiler could generate code for almost all the features of ECMAScript 3 except: - -* break or continue to label. -* Conditional break in switch block (e.g., if (a > 0) break;). - -### AOT compiler - -The AOT (ahead-of-time) compiler is a piece of JavaScript code (scripts/jscexc.js) which generates code before runtime. - -#### Why we need an AOT compiler. - -The JIT compiler works just fine. The function would be compiled only once for each page load or node.js execution, so the performance cost of compiler is really small. And the size of compiler is only around 10K when minified and gzipped - acceptable for me. - -But the problem comes with "script compressing". Normally, the script used for websites would be compressed by tools like UglifyJS, Closure Compiler or YUI compressor. Jscex compiler works fine when the compress strategy is just removing the whitespaces and shortening the name of variables, but modern compressors would rewrite the statement structures for getting minimal size: - - var bubbleSortAsync=eval(Jscex.compile("async",function(a){for(var b=0;b0&&$await(swapAsync(a,c,c+1))}})) - -The code above is the code of bubble sort animation compressed by UglifyJS. Please notice that Jscex cannot handle code like d>0&&$await(...), so we have to compile the code before compressing. That the main reason I build an AOT compiler. - -#### Usage - -The AOT compiler runs with node.js: - - node scripts/jscexc.js --input input_file --output output_file - -For the bubbleSortAsync method above, it would be compiled into: - - (function (array) { - var $$_builder_$$_0 = Jscex.builders["async"]; - return $$_builder_$$_0.Start(this, - $$_builder_$$_0.Delay(function () { - var x = 0; - return $$_builder_$$_0.Loop( - function () { - return x < array.length; - }, - function () { - x++; - }, - $$_builder_$$_0.Delay(function () { - var y = 0; - return $$_builder_$$_0.Loop( - function () { - return y < (array.length - x); - }, - function () { - y++; - }, - $$_builder_$$_0.Delay(function () { - return $$_builder_$$_0.Bind(compareAsync(array[y], array[y + 1]), function (r) { - if (r > 0) { - return $$_builder_$$_0.Bind(swapAsync(array, y, y + 1), function () { - return $$_builder_$$_0.Normal(); - }); - } else { - return $$_builder_$$_0.Normal(); - } - }); - }), - false - ); - }), - false - ); - }) - ); - }) - -The AOT compiler would keep the code others than Jscex functions. The code generated by AOT compiler could be compressed safely. Futhermore, the compiled code could execute without "jscex-parser.js" and "jscex-jit.js". The async methods could be executed properly with only "jscex-async.js" and "jscex-async-powerpack", which is only 3KB when gzipped. - -#### Limitation - -The AOT compiler would parse the input scripts **statically** and generate new code, so it can only recognize the standard pattern: eval(Jscex.compile("builderName", function () { ... })). The follow codes work fine with JIT compiler but cannot be compiled by AOT compiler. - - var compile = Jscex.compile; - var builderName = "async"; - var func = function () { ... }; - var newCode = compile(builderName, func); - var funcAsync = eval(newCode); - -Luckily, the standard pattern is quite enough in my experiences, so this limitation won't be an issue in real world. - -### Beyond async - -Jscex is not only for async programming. The Jscex compiler turns the input function into a standard monadic form, the rest work are done by "Jscex builder". Jscex releases with a build-in "async builder" could simplify async programming, we can also define a "seq builder" to help constructing "iterators" in JavaScript (ECMAScript 3) - like the ["generator" feature in JavaScript 1.7](https://developer.mozilla.org/en/JavaScript/Guide/Iterators_and_Generators) or Python/C#, etc. - - var rangeSeq = eval(Jscex.compile("seq", function (minInclusive, maxExclusive) { - for (var i = minInclusive; i < maxExclusive; i++) { - $yield(i); - } - })); - -Jscex builders register themselves in Jscex.builders dictionary. The builder would be retrieved by name at runtime, you can get the following code by watching the console.log output or use the AOT compiler: - - (function (minInclusive, maxExclusive) { - var $$_builder_$$_0 = Jscex.builders["seq"]; - return $$_builder_$$_0.Start(this, - $$_builder_$$_0.Delay(function () { - var i = minInclusive; - return $$_builder_$$_0.Loop( - function () { - return i < maxExclusive; - }, - function () { - i++; - }, - $$_builder_$$_0.Delay(function () { - return $$_builder_$$_0.Bind(i, function () { - return $$_builder_$$_0.Normal(); - }); - }), - false - ); - }) - ); - }) - -Unfortunately, the "seq builder" is not part of Jscex at this moment. It's one of the future plans of Jscex project. - -### Comparison to other projects - -There're several other projects has the same purpose. We can put these project into two categories. - -#### Library extensions: - -Most projects build libraries to [simplify async programming in JavaScript](http://www.infoq.com/articles/surviving-asynchronous-programming-in-javascript): - - // async.js (https://github.com/fjakobs/async.js) - async.list([ - asncFunction1, - asncFunction2, - asncFunction3, - asncFunction4, - asncFunction5, - ]).call().end(function(err, result) { - // do something useful - }); - - // Step (https://github.com/creationix/step) - Step( - function readSelf() { - fs.readFile(__filename, this); - }, - function capitalize(err, text) { - if (err) throw err; - return text.toUpperCase(); - }, - function showIt(err, newText) { - if (err) throw err; - console.log(newText); - } - ); - -People use these solutions need to follow the programming patterns defined by the library, but Jscex just follows JavaScript idioms, programming with Jscex is just programming in JavaScript. So Jscex has a really gentle learning curve. - -#### Language extensions: - -A few projects are "Language extensions" - for these projects like [StratifiedJS](http://www.infoq.com/articles/stratifiedjs) and [Narrative JavaScript](http://www.neilmix.com/narrativejs/doc/), people write "JavaScript-like" codes and compile them to "real JavaScripts": - - // Narrative JavaScript code - function sleep(millis) { - var notifier = new EventNotifier(); - setTimeout(notifier, millis); - notifier.wait->(); - } - - // StratifiedJS code - var result; - waitfor { - result = performGoogleQuery(query); - } - or { - result = performYahooQuery(query); - } - -Although these languages could be quite similar to JavaScript, but they're really not, especially the syntax and semantic related to async programming. Programmers have to learn "new languages" when using them. And these new languages may also break the JavaScript editors. Jscex is nothing more than JavaScript and even the $await operations are simple method calls - the only semantic related to that is "wait until finished", everyone knows how to programming in Jscex in minutes. - -And in traditional JavaScript programming, people modify the code and refresh the page to see whether things got right. But these "new languages" cannot being executed in browsers (or other ECMASCript 3 engines) directly, it should be compiled into JavaScript before runtime. Project like Narrative and StratifiedJS also provide JIT compiler which load and produce code as runtime. But the way they use have obvious limitations: - -If the sources are loaded by XMLHttpRequest, these source files have to be host in the same domain with website. JSONP can be used to load sources from other domains, but it's not loading remote sources directly, it loads the content in "method call" style - the source files have to be processed before sending to the client. - -These project may also load script blocks written inside the page. The compiler would load these code snippets from the DOM and compile them when page loaded. But in the case like: - - - - - - -People cannot use the async methods defined in previous. It's not the behavior people are currently using in JavaScript programming. - -But Jscex is nothing more than JavaScript: - -* There's no clear separation between compile-time and runtime, code are compiled by the JIT compiler at runtime when develop. -* Jscex codes are standard JavaScript, people can use normal JavaScript editors to write and format Jscex functions. -* Jscex source files are JavaScript source files. They can be hosted in different domains and used in webpage as normal extenal scripts files. -* Jscex functions defined in browser's script browser can be used by the code next to it immediately. - -Jscex just keeps nearly everything as usual for JavaScript programmers. - -### Jscex internals - -(more details in the future) - -### Futures - -* Better async builder - * Better performance - * Support "cancellation" for async tasks - * More primitives in Jscex.Async -* Support "seq builder" -* Extensions for popular JavaScript libraries/frameworks (jQuery, Node.js, etc.) - -### Related projects - -* F#: the whole Jscex project are inspired by F#'s genis features "[Computation Expressions](http://msdn.microsoft.com/en-us/library/dd233182.aspx)" and "[Asynchronous Workflow](http://msdn.microsoft.com/en-us/library/dd233250.aspx)". -* C### vNext: the future version of C### provide great async programming support just like F### Async Workflow and Jscex. the name "$await" is borrowed from the "await" keyword in C### vNext. -* UglifyJS: the Jscex compiler use the parser of UglifyJS. It's simple, fast and works fine in ECMAScript 3 engines. -* [Narcissus](https://github.com/mozilla/narcissus/): Narcissus is a JavaScript interpreter written in JavaScript, using the [SpiderMonkey](http://www.mozilla.org/js/spidermonkey/) engine. Its parser is much slower than UglifyJS's and dependent on SpiderMonkey extensions. But it produces an AST carries more information which is really helpful for AOT compiler. -* [Closure Compiler](http://code.google.com/closure/compiler/): used to compress scripts. - -### Bugs or Feedbacks? - -Feel free to contact me for any bugs or feedbacks, please [use the Google Groups](http://groups.google.com/group/jscex) or email me directly. - -### License - -Jscex is released under the BSD license: - -
Copyright 2011 (c) Jeffrey Zhao <jeffz@live.com>
-Based on UglifyJS (https://github.com/mishoo/UglifyJS).
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-    * Redistributions of source code must retain the above
-      copyright notice, this list of conditions and the following
-      disclaimer.
-
-    * Redistributions in binary form must reproduce the above
-      copyright notice, this list of conditions and the following
-      disclaimer in the documentation and/or other materials
-      provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
-THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
\ No newline at end of file diff --git a/doc/src/documents/scripts/sorting-animations.js.jscex b/doc/src/documents/scripts/sorting-animations.js.jscex deleted file mode 100644 index 9d900d9..0000000 --- a/doc/src/documents/scripts/sorting-animations.js.jscex +++ /dev/null @@ -1,92 +0,0 @@ -var SortingAnimations = function (canvas) { - - var ctx = canvas.getContext ? canvas.getContext("2d") : null; - - var randomArray = function () { - var array = []; - var length = Math.floor(canvas.width / 3); - - for (var i = 1; i < canvas.width / 3; i++) { - array.push(i); - } - - array.sort(function() { return (Math.round(Math.random()) - 0.5); }); - return array; - } - - var paint = function (array) { - ctx.clearRect(0, 0, canvas.width, canvas.height); - - ctx.beginPath(); - - for (var i = 0; i < array.length; i++) - { - var x = 2 + i * 3; - var height = array[i] * 3 * canvas.height / canvas.width; - - ctx.moveTo(x, canvas.height); - ctx.lineTo(x, canvas.height - height); - } - - ctx.stroke(); - } - - var compareAsync = eval(Jscex.compile("async", function (x, y) { - $await(Jscex.Async.sleep(10)); - return x - y; - })); - - var swapAsync = eval(Jscex.compile("async", function (array, x, y) { - var t = array[x]; - array[x] = array[y]; - array[y] = t; - - paint(array); - - $await(Jscex.Async.sleep(20)); - })); - - var partitionAsync = eval(Jscex.compile("async", function (array, begin, end) { - var i = begin; - var j = end; - var pivot = array[Math.floor((begin + end) / 2)]; - - while (i <= j) { - while (true) { - var r = $await(compareAsync(array[i], pivot)); - if (r < 0) { i++; } else { break; } - } - - while (true) { - var r = $await(compareAsync(array[j], pivot)); - if (r > 0) { j--; } else { break; } - } - - if (i <= j) { - $await(swapAsync(array, i, j)); - i++; - j--; - } - } - - return i; - })); - - var quickSortAsync = eval(Jscex.compile("async", function (array, begin, end) { - var index = $await(partitionAsync(array, begin, end)); - - if (begin < index - 1) - $await(quickSortAsync(array, begin, index - 1)); - - if (index < end) - $await(quickSortAsync(array, index, end)); - })); - - this.supported = !!ctx; - this.randomArray = randomArray; - this.paint = paint; - - this.quickSortAsync = eval(Jscex.compile("async", function (array) { - $await(quickSortAsync(array, 0, array.length - 1)); - })); -}; diff --git a/doc/src/documents/zh-cn/index.html.md b/doc/src/documents/zh-cn/index.html.md deleted file mode 100644 index 7a02ac8..0000000 --- a/doc/src/documents/zh-cn/index.html.md +++ /dev/null @@ -1,199 +0,0 @@ ---- -layout: main-zh-cn -title: 首页 ---- - - - - -### 新闻 - -目前已为Jscex设立专项资金。自2012年起,每月1号将为Jscex拨款1024元人民币,用于鼓励个人对Jscex的研究及使用,包括但不限于推广建议,研究,案例,或是在开源项目中使用Jscex。 - -### 什么是Jscex? - -Jscex是JavaScript Computation EXpressions的缩写,它为JavaScript语言提供了一个monadic扩展,能够显著提高一些常见场景下的编程体验(例如异步编程)。Jscex项目完全使用JavaScript编写,能够在任意支持[ECMAScript 3](http://www.ecma-international.org/publications/standards/Ecma-262.htm)的执行引擎里使用,包括各浏览器及服务器端JavaScript环境(例如[Node.js](http://nodejs.org/))。 - -Jscex有如下特点: - -* 无需学习额外的API或新的语言,直接使用JavaScript语言本身进行编程。 -* 功能强大的异步任务模型,支持并行,取消等常用异步编程模式,经过多种技术平台验证。 -* 在支持JavaScript语言的环境里直接使用(如浏览器或Node.js),无需额外的编译或转化步骤。 -* 基础组件及异步运行库共计4K大小(Minified + GZipped),适合开发网页应用。 - -### 快速入门 - -Jscex的核心功能之一,便是对异步编程进行了极大程度的简化,帮助让开发人员摆脱异步编程方面烦恼,将注意力尽可能多地放在逻辑的表现上,而非异步编程过程中的各类奇技淫巧。 - -请尝试解决以下问题,并与基于Jscex的实现进行比较。 - -#### 排序算法动画演示 - -人人都会排序算法,那么能否使用动画来演示排序算法的运作过程?例如以下是快速排序的动画演示: - - - - - 您的浏览器不支持Canvas绘图,请使用IE9+,Chrome,Firefox,Safari等现代浏览器。 - - - - -#### 问题分析 - -我们就以最简单的“冒泡排序”进行分析: - - var compare = function (x, y) { - return x - y; - } - - var swap = function (a, i, j) { - var t = a[i]; a[i] = a[j]; a[j] = t; - } - - var bubbleSort = function (array) { - for (var x = 0; x < array.length; x++) { - for (var y = 0; y < array.length - x; y++) { - if (compare(array[y], array[y + 1]) > 0) { - swap(array, y, y + 1); - } - } - } - } - -所谓冒泡排序,便是使用双重循环,两两比较相邻的元素,如果顺序不对,则交换两者。我们有了基本算法,想要将其用动画表现出来,其实只要运用以下两点策略即可: - -* 增加交换和比较操作的耗时,因为排序算法的性能主要取决于交换和比较操作的次数多少。 -* 每次交换元素后重绘数组,这样便能表现出排序的动画效果。 - -看上去很简单,不是吗? - -#### 异步编程之殇 - -在其他一些语言里,我们往往可以使用`sleep`函数让当前线程停止一段时间,这样便起到了“等待”的效果。但是,在JavaScript中我们无法做到这一点,唯一的“延时”操作只能使用setTimeout来实现,但它却需要一个回调函数,我们无法这样让`compare`方法“暂停”一段时间: - - var compare = function (x, y) { - setTimeout(function () { - return x - y; - }, 10); // compare方法依然会立即返回 - } - -我们只能把`compare`改造为带有回调函数的方法: - - var compare = function (x, y, callback) { - setTimeout(function () { - callback(x - y); // 通过回调函数传递结果 - }, 10); - } - -同理,`swap`方法也需要通过回调函数传递结果。此时我们会发现,我们很难在`bubbleSort`中使用异步的`compare`和`swap`方法,而且如果要配合循环和判断一齐使用则更加困难。这就是异步编程在流程控制方面的难点所在:我们无法使用传统的JavaScript进行表达,算法会被回调函数分解地支离破碎。 - -#### Jscex实现 - -为了解决异步编程中的流程控制问题,人们设计构造了[各式各样的辅助类库](https://github.com/joyent/node/wiki/modules#wiki-async-flow)来简化开发。但我们认为,流程控制是一个语言层面上的问题,JavaScript已经提供了流程控制需要的所有关键字(例如`if`、`for`、`try`等等),开发人员也早已无数次证明了这种方式的灵活及高效。如果我们可以“修复”这些流程控制机制对异步操作“无效”的问题,则开发人员无需学习新的API,不会引入额外的噪音,一切都是最简单,最熟悉的JavaScript代码。 - -Jscex便做到了这一点。例如,使用Jscex来实现冒泡排序动画,则只需要: - - var compareAsync = eval(Jscex.compile("async", function (x, y) { - $await(Jscex.Async.sleep(10)); // 暂停10毫秒 - return x - y; - })); - - var swapAsync = eval(Jscex.compile("async", function (a, i, j) { - $await(Jscex.Async.sleep(20)); // 暂停20毫秒 - var t = a[i]; a[i] = a[j]; a[j] = t; - paint(a); // 重绘数组 - })); - - var bubbleSortAsync = eval(Jscex.compile("async", function (array) { - for (var x = 0; x < array.length; x++) { - for (var y = 0; y < array.length - x; y++) { - // 异步比较元素 - var r = $await(compareAsync(array[y], array[y + 1])); - // 异步交换元素 - if (r > 0) $await(swapAsync(array, y, y + 1)); - } - } - })); - -与之前的代码相比,基于Jscex编写的代码只有两个变化: - -1. 与传统的`function () { ... }`方式不同,我们使用`eval(Jscex.compile("async", function () { ... }))`来定义一个“异步函数”。这样的函数定义方式是“模板代码”,没有任何变化,可以认做是“异步函数”与“普通函数”的区别。 -2. 对于“异步操作”,如上面代码中的`Jscex.Async.sleep`内置函数(其中封装了setTimeout函数),则可以使用`$await(...)`来等待其完成,方法会在该异步操作结束之后才继续下去,其执行流程与普通JavaScript没有任何区别。 - -完整代码请参考“[排序算法动画](manuals/async/samples/sorting-algorithms.html)”示例,其中实现了“冒泡排序”,“选择排序”以及“快速排序”三种排序算法的动画。 - -### 总结 - -JavaScript的异步及非阻塞特性,让程序员无法使用传统方式表达代码,导致语义丢失,算法被分解地支离破碎。例如,由于只能使用setTimeout回调来实现“延迟”效果,即便是要做到“暂停”这样的简单功能,也已经让人难以看出这是一个“冒泡排序”的实现。 - -Jscex的哲学,是真正将异步编程中的流程控制**回归JavaScript本身**。您可以尝试使用其他任何方式解决这个问题,并与上述基于Jscex的做法进行比较。使用Jscex,让程序员可以在异步的、非阻塞的JavaScript执行环境里使用传统的“阻塞”表达方式编写代码。并让异步任务的协作,取消以及错误处理等常见需求变得前所未有的简单。 - -更多内容请参考[开发文档](manuals/)以及Jscex[异步模块](manuals/async/) 。 - -### 错误及反馈 - -请在[GitHub上汇报错误](https://github.com/JeffreyZhao/jscex/issues)。如果您对Jscex有任何建议或意见,请加入[邮件列表](http://groups.google.com/group/jscex)或直接[与我联系](mailto:jeffz@live.com)。 - -### 授权 - -Jscex使用BSD授权协议。 - - Copyright 2011 (c) Jeffrey Zhao jeffz@live.com - Based on UglifyJS (https://github.com/mishoo/UglifyJS). - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the following - disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - SUCH DAMAGE. - - \ No newline at end of file diff --git a/doc/src/documents/zh-cn/manuals/async/index.html.md b/doc/src/documents/zh-cn/manuals/async/index.html.md deleted file mode 100644 index 6c834e1..0000000 --- a/doc/src/documents/zh-cn/manuals/async/index.html.md +++ /dev/null @@ -1,604 +0,0 @@ ---- -layout: manual-zh-cn -title: 异步模块 ---- - -## 简介 - -Jscex从诞生开始,便注定会在异步编程方面进行全方面的支持,因为其背后的理论,以及这种理论在C#,F#或是Scala中的实践,都是以异步编程为核心的。异步编程是各个平台都会遇到的问题,而Jscex便是将其他平台针对此类问题所做的探索,支持以及编程模式,引入到传统JavaScript开发过程中。 - -### 依赖 - -* 动态依赖:无 -* 静态依赖:[构造器基础模块](../builderbase/) - -## 引入Jscex异步模块 - -如果您要使用Jscex异步模块,首先必须引入[核心组件](../core/),之后再基于这个对象初始化异步模块。在开发环境里,您还需要引入[JIT编译器](../jit/)。 - -### Node.js - -如果您使用的是Node.js,可以直接使用[Node Package Manager](http://npmjs.org/)(即npm命令)安装最新的jscex-async包: - - npm install jscex-async - -然后便可以在脚本中引入并初始化这个模块: - - var Jscex = require("jscex"); // 引入核心组件 - require("jscex-async").init(); // 引入并初始化Jscex异步模块 - -不过,如果您是在开发环境使用Jscex,还需要安装及引入[JIT编译器模块](../jit/): - - require("jscex-jit").init() // 引入并初始化Jscex JIT编译器 - -此类方式也适合非[Node.js](http://nodejs.org/),但实现了[CommonJS规范](http://www.commonjs.org/)的JavaScript运行环境。 - -### 浏览器 - -在浏览器环境中使用Jscex异步模块,您同样需要引入[核心组件](../core/)。此时在浏览器根(即window对象)上会出现一个Jscex对象,之后再依次引入[构造器基础](../builderbase/)及异步模块的jscex-async-x.y.z.js文件即可: - - - - - - - - - -至此,Jscex根对象已经包括了使用异步模块所需的所有成员。不过,如果您是在开发环境使用,还需要引入[语法解析器模块](../parser/)及[JIT编译器模块](../jit/): - - - - - - -由于后两者会显著增加加载体积,因此在生产环境中应该使用[预编译器](../aot/)处理后并去除这两个模块。这种方式也适合各类**没有**实现CommonJS等包加载规范的JavaScript运行环境。 - -### 其他环境 - -Jscex自动支持一些其他的包加载规范。假如当前JavaScript运行环境实现了这些规范,则Jscex会自动采用这些规范。详细信息请参考“[包引入](../importing.html)”相关内容。 - -## 定义异步方法 - -在JavaScript里定义一个普通方法很容易,例如: - - var print = function (text) { - console.log(text); - } - -而创建一个Jscex里的“异步方法”,其实就是使用“异步构造器”(即`"async"`)来创建一个Jscex方法,即: - - var printAsync = eval(Jscex.compile("async", function (text) { - console.log(text); - })); - -其中`eval(Jscex.compile("async", …))`部分只是一个包装,一种标识,让程序员可以清楚的意识到这不是一个普通方法。在使用Jscex的时候,我们不会在其他任何地方,其他任何一种方式使用`eval`或是`Jscex.compile`,完全不会表现出其“邪恶”的一面,更不会出现在生产环境中。更多信息,请参考“[Jscex调试](../debugging.html)”相关内容。 - -## 使用异步方法 - -### 直接使用 - -与普通使用不同的是,异步方法在执行后并不会立即启动方法体内的代码,而是会返回一个`Jscex.Async.Task`类型的对象(后文也会称做“任务对象”或是Task对象): - - // 输出“Hello World” - print("Hello World"); - - // 得到Jscex.Async.Task对象 - var task = printAsync("Hello World"); - -如果要启动这个task对象,只需调用其`start()`方法即可: - - task.start(); // 输出“Hello World” - -关于`Jscex.Async.Task`类型成员及其功能,请参考后文“Jscex.Async.Task类型详解”部分。任何一个Jscex异步方法,都可以使用`start()`方法启动,无需依赖其他异步方法(见下节)。这点经常被人忽略,但对于那些需要在已有项目中逐步引入Jscex的情况来说却十分重要。 - -### 在其他异步方法内使用 - -对于一个Jscex异步方法返回的Task对象来说,最常见的使用方式便是在另一个异步方法内,通过`$await`命令来执行。例如: - - var printAllAsync = eval(Jscex.compile("async", function (texts) { - for (var i = 0; i < texts.length; i++) { - $await(printAsync(texts[i])); // 使用$await命令执行一个Task对象 - } - })); - -当然,对于这里的`printAsync`方法来说,由于其内部并没有其他异步操作(您可以简单的理解为“没有`$await`命令”),因此它所返回的Task对象,其执行效果和普通的`print`方法没有什么区别。但是在实际使用时,我们使用的是更有意义的异步操作,例如在[异步增强模块](./powerpack.html)中提供的`Jscex.Async.sleep`方法: - - var printEverySecond = eval(Jscex.compile("async", function (texts) { - for (var i = 0; i < texts.length; i++) { - $await(Jscex.Async.sleep(1000)); // “暂停”一秒 - console.log(text[i]); - } - })); - -我们知道,JavaScript环境并没有提供“暂停”或是“阻塞”一段时间这样的方法,唯一能提供“延迟执行”功能的只有`setTimeout`。而`setTimeout`只能做到在一段时间后执行一个回调方法,因此便无法在一个使用JavaScript中的`for`或是`while`关键字来实现循环,也无法靠`try…catch`来捕获到回调函数出错时抛出的异常。 - -而在Jscex异步方法中,这一切都不是问题。`$await`将为您保证异步操作的执行顺序,您可以使用最传统的编程方式来表达算法,由Jscex来帮您搞定异步操作的各种问题。 - -### $await指令的语义 - -Jscex函数是标准的JavaScript,支持JavaScript语言几乎所有特性:条件判断就用`if…else`或`switch`,错误捕获就用`try…catch…finally`,循环就用`while`、`for`、`do`,其他包括`break`,`continue`,`return`等等,都是最普通的JavaScript写法,而在Jscex异步方法中,唯一新增的便是`$await`指令。 - -`$await`指令的使用形式便是普通的方法调用,但事实上在上下文中并没有这个方法。它的作用与`eval(Jscex.compile("async", …))`一样,仅仅是个占位符,让Jscex知道在这个地方需要进行“特殊处理”。 - -`$await`指令的参数只有一个,便是一个`Jscex.Async.Task`类型的对象,这个对象可以是一个异步方法的返回结果,或是由其他任何方式得到。例如,之前所演示的`Jscex.Async.sleep(1000)`,其实便是返回一个表示“等待1秒钟”的Task对象。因此这句代码: - - $await(Jscex.Async.sleep(1000)); - -在需要时也可以写作: - - var task = Jscex.Async.sleep(1000); - $await(task) - -`$await`指令的确切语义是:“**等待该Task对象结束(返回结果或抛出错误);如果它尚未启动,则启动该任务;如果已经完成,则立即返回结果(或抛出错误)**”。因此,我们也可以在需要的时候灵活使用`$await`指令。例如在一个Node.js应用程序中,时常会实现下面的逻辑: - - var getUserItemsAsync = eval(Jscex.compile("async", function (userId) { - - var user = $await(queryUserAsync(userId)); - var items = $await(queryItemsAsync(userId)); - - return { - user: user, - items: items - }; - }); - -在上面的代码中,`queryUserAsync`与`queryItemsAsync`是依次执行的,如果前者耗时200毫秒,后者耗时300毫秒,则总共需要500毫秒才能完成。但是,在某些情况下,我们可以让两个操作“并发”执行,例如: - - var getUserItemsAsync = eval(Jscex.compile("async", function (userId) { - - var queryUserTask = queryUserAsync(userId); - // 手动启动queryUserAsync任务,start方法调用将立即返回。 - queryUserTask.start(); - - var items = $await(queryItemsAsync(userId)); - var user = $await(queryUserTask); // 等待之前的任务完成 - - return { - user: user, - items: items - }; - }); - -在`$await(queryUserTask)`时,如果该任务已经完成,则会立即返回结果,否则便会等待其完成。因此,当这两个互不依赖的查询操作并发执行的情况下,总耗时将会减少到300毫秒。 - -## 任务模型 - -`$await`指令的参数是`Jscex.Async.Task`类型的对象,这个对象这个对象可以是一个异步方法的返回结果,或是由其他任何方式得到。在Jscex异步模块眼中,一个异步任务便是指“**能在未来某个时刻返回的操作**”,它可以是一个`setTimeout`的绑定(如之前演示过的`sleep`方法),甚至是一个用户事件: - - var btnNext = document.getElementById("btnNext"); - var ev = $await(Jscex.Async.onEvent(btnNext, "click")); - console.write(ev.clientX, ev.clientY); - -因为在Jscex异步模块眼中,用户的点击行为,也是“能在未来某个时刻返回的操作”,这便是一个异步任务。您可以在后文“[模态对话框](samples/modal-dialog.html)”以及“[汉诺塔](samples/hanoi.html)”示例中了解这个模型的威力。 - -除了上文提到的`sleep`及`onEvent`以外,[异步增强模块](./powerpack.html)里还包含了更多有用的辅助方法,来应对常见的异步协作问题。例如之前的“并发”执行示例,在实际情况下往往会使用异步增强模块中的`whenAll`辅助方法: - - var Task = Jscex.Task; - - var getUserItemsAsync = eval(Jscex.compile("async", function (userId) { - - return $await(Task.whenAll({ - user: queryUserAsync(userId), - items: queryItemsAsync(userId) - })); - }); - -`whenAll`辅助方法会将输入的多个任务包装为一个整体,并同样以Task对象的形式返回。新的Task对象只有在所有输入任务都结束的情况下才会完成,并使用相同的结构(“键值”或“数组”)返回其结果。 - -Jscex的异步模型经过C#,F#及Scala等多种语言平台的检验,可以说拥有非常灵活而丰富的使用模式。 - -## 取消模型 - -取消操作也是异步编程中十分常见但也十分麻烦的部分。因此,Jscex异步模块在任务模型中融入一个简单的取消功能,丰富其潜在功能及表现能力。 - -但是,Jscex对Task对象上并没有一个类似`cancel`这样的方法,这点可能会出乎某些人的意料。在实现“取消模型”这个问题上,我们首先必须清楚一点的是:**并非所有的异步操作均可撤销**。有的任务一旦发起,就只能等待其安全结束。因此,我们要做的,应该是“要求取消该任务”,至于任务会如何响应,便由其自身来决定了。在Jscex的异步模型中,这个“通知机制”便是由`Jscex.Async.CancellationToken`类型(下文也会称作CancellationToken)提供的。 - -CancellationToken的cancel方法便用于“取消”一个或一系列的异步操作。更准确地说,它是将自己标识为“要求取消”并“通知”相关相关的异步任务。这方面的细节将在后续章节中讲解,目前我们先来了解一下Jscex异步模块中的任务取消模型。如果您要取消一个任务,怎么需要先准备一个CancellationToken对象: - - var ct = new Jscex.Async.CancellationToken(); - -然后,对于支持取消的异步任务,都会接受一个CancellationToken作为参数,并根据其状态来行动。这里我们还是以异步增强模块中的`sleep`方法进行说明: - - var printEverySecondAsync = eval(Jscex.compile("async", function (ct) { - var i = 0; - while (true) { - $await(Jscex.Async.sleep(1000, ct)); - console.log(i++); - } - })); - - printEverySecondAsync(ct).start(); - -如果您在浏览器或是Node.js的JavaScript交互式控制台上运行上述代码,将会从0开始,每隔一秒打印一个数字,永不停止,直到有人调用`ct.cancel()`为止。 - -在一个Jscex异步方法中,“取消”的表现形式为“异常”。例如,在`ct.cancel()`调用之后,上述代码的中的`$await(Jscex.Async.sleep(1000, ct))`语句将会抛出一个`Jscex.Async.CanceledError`错误,我们可以使用`try…catch`进行捕获: - - var ct = new CancellationToken(); - var task = Jscex.Async.sleep(5000, ct); - try { - setTimeout(function () { ct.cancel() }, 1000); // 1秒后调用ct.cancel(); - $await(task); - } catch (ex) { - console.log(CancelledError.isTypeOf(ex)); // true - console.log(task.status); // canceled - } - -如果某个Task对象抛出了`CancelledError`错误对象,则它的`status`字段会返回字符串`"canceled"`,表明其已被取消。同理,对于一个Jscex异步方法来说,如果从内部抛出一个未被捕获的`CancelledError`错误对象,则它的状态也会标识为“已取消”。试想,在很多情况下,我们不会用`try…catch`来捕获一个`$await`指令所抛出的异常,于是这个异常会继续顺着“调用栈”继续向调用者传递,于是相关路径上所有的Task对象都会成为`canceled`状态。这是一个**简单而统一**的模型。 - -有些情况下我们也需要手动操作CancellationToken对象,向外抛出一个`CanceledError`错误,以表示当前异步方法已被取消: - - if (ct.isCancellationRequested) { - throw new Jscex.Async.CanceledError(); - } - -或直接: - - ct.throwIfCancellationRequested(); - -`throwIfCancellationRequested`是CancellationToken对象上的辅助方法,其实就是简单地检查`isCancellationRequested`字段是否为true,并抛出一个`CanceledError`对象。 - -有时候我们也需要手动判断`isCancellationRequested`,因为可能我们需要在取消的时候做一些“收尾工作”,于是便可以: - - if (ct.isCancellationRequested) { - - // 做些收尾工作 - - throw new Jscex.Async.CanceledError(); - } - -或是: - - try { - $await(…); // 当任务被取消时 - } catch (ex) { - if (CancelledError.isTypeOf(ex)) { // 取消引发的异常 - // 做些收尾工作 - } - - throw ex; // 重新抛出异常 - } - -值得注意的是,由于JavaScript的单线程特性,一般只需在异步方法刚进入的时候,或是某个`$await`指令之后才会使用`isCancellationRequested`或是`throwIfCancellationRequested`。我们没有必要在其他时刻,例如两个`$await`指令之间反复访问这些成员,因为它们的行为不会发生任何改变。 - -## 将任意异步操作绑定为Task对象 - -世界上有无数种异步模型,从最简单的回调函数传递结果,用户行为引发的事件,到相对复杂的Promise模型。而在Jscex的异步模块种,能够被`$await`指令识别的,便是用`Jscex.Async.Task`类型来表达的异步任务。任何的异步方法,在执行后都能得到一个Task对象,但如果是其他平台或是环境所提供异步模型,便需要经过绑定才能被`$await`使用。 - -### 绑定简单操作 - -将任何一个异步操作Task对象,会需要用到`Jscex.Async.Task`类型的`create`静态方法。方便起见,通常我们可以使用`Task`来指向这个全命名: - - var Task = Jscex.Async.Task; - -例如在Node.js中[内置的Path模块中的`exists`方法](http://nodejs.org/docs/v0.6.5/api/path.html#path.exists),便是一个十分简单的异步操作,它会将结果通过回调函数返回: - - path.exists('/etc/passwd', function (exists) { - util.debug(exists ? "it's there" : "no passwd!"); - }); - -但如果我们要在Jscex异步方法里使用这个函数,则需要将其进行简单绑定: - - path.existsAsync = function (p) { - return Task.create(function (t) { - path.exists(p, function (exists) { - t.complete("success", exists); - }); - }); - } - -于是`existsAsync`就会返回一个Task对象,它在`start`以后,便会调用原来的`exists`方法获得结果。我们也可以将其用在某个Jscex异步方法中: - - // 某Jscex异步方法内 - var exists = $await(path.existsAsync("/etc/passwd")); - util.debug(exists ? "it's there" : "no passwd!"); - -绑定一个异步方法的基本方式可以分为以下几点: - -1. 边写一个新方法,其中返回`Task.create`的执行结果(一个Task对象)。 -2. `Task.create`方法的参数为一个回调函数(下文称为委托方法),它会在这个Task对象的`start`方法调用时执行,发起被绑定的异步操作。 -3. 委托方法的参数是当前的Task对象(也是之前`Task.create`创建的对象),在异步操作完成后,使用其`complete`方法通知Task对象“已完成”。 -4. `complete`方法的第一个参数为字符串`"success"`,表示该异步操作执行成功,并可以通过第二个参数传回该异步操作的结果(亦可空缺)。 - -### 引发异常 - -并非所有的异步操作都会成功,在平时“非异步”的编程方式中,我们往往会在出错的情况下抛出异常。如果一个异步操作引发了异常,我们只需要在调用Task对象的`complete`方法时,将第一个参数从`"success"`替换为`"failure"`,并将第二个参数设为错误对象即可。例如Node.js中内置的[File System模块的`readFile`方法](http://nodejs.org/docs/v0.6.5/api/fs.html#fs.readFile)便可能会失败: - - fs.readFile('/etc/passwd', function (err, data) { - if (err) { - util.debug("Error occurred: " + err); - } else { - util.debug("File length: " + data.length); - } - }); - -而将其绑定为Task对象时只需: - - fs.readFileAsync = function (path) { - return Task.create(function (t) { - fs.readFile(path, function (err, data) { - if (err) { - t.complete("failure", err); - } else { - t.complete("success", data); - } - }); - }); - } - -于是在一个Jscex异步方法中使用时: - - // 某Jscex异步方法内 - try { - var data = $await(fs.readFileAsync(path)); - util.debug("File length: " + data.length); - } catch (ex) { - util.debug("Error occurred: " + ex); - } - -错误处理也是异步编程的主要麻烦之处之一。在异步环境中,我们往往需要在“每个”异步操作的回调函数里判断是否出现错误,一旦有所遗漏,在出现问题之后就很难排查了。例如: - - fs.readFile(file0, function (err0, data0) { - if (err0) { - // 错误处理 - } else { - fs.readFile(file1, function (err1, data1) { - if (err1) { - // 错误处理 - } else { - fs.readFile(file2, function (err2, data2) { - if (err2) { - // 错误处理 - } else { - // 使用data0,data1和data 2 - } - }); - } - }); - } - }); - -如今Jscex改变了这个窘境,只需要一个`try…catch`便可以捕获到任意多个异步操作的异常,保留了传统编程过程中的实践: - - try { - var data0 = $await(fs.readFileAsync(file0)); - var data1 = $await(fs.readFileAsync(file1)); - var data2 = $await(fs.readFileAsync(file2)); - // 使用data0,data1和data2 - } catch (ex) { - // 错误处理 - } - -甚至,在编写绝大多数Jscex异步方法的时候,我们并不需要显式地进行`try…catch`,我们可以让异常直接向方法外抛出,由统一的地方进行处理。 - -### 取消操作 - -从上文的“取消模型”中我们得知,所谓“取消”只不过是引发一个`isCancellation`为true的异常而已。因此,要表示当前异常操作被取消,也只需要向`complete`方法传入`"failure"`即可。不过问题的关键是,我们如果要绑定一个现有的异步操作,往往还需要在取消时实现一些“清理”工作。这里,我们便以异步增强模块中的`sleep`方法来演示“取消”操作的实现方式。 - -`sleep`方法绑定了JavaScript运行环境中的`setTimeout`及`clearTimeout`函数,它们的基本使用方式为: - -* `var seed = setTimeout(fn, delay);`:表示在`delay`毫秒以后执行`fn`方法,并返回`seed`作为这次操作的标识,供`clearTimeout`使用。 -* `clearTimeout(seed);`:在`fn`被执行之前,可以使用`clearTimeout`取消这次操作。这样即便到了时间,也不会执行fn方法了。 - -基于这两个功能,我们便可以实现`sleep方法`及其取消功能了。实现支持取消的异步操作绑定往往分三步进行: - - var Task = Jscex.Async.Task; - var CanceledError = Jscex.Async.CanceledError; - - var sleep = function (delay, /* CancellationToken */ ct) { - return Task.create(function (t) { - // 第一步 - if (ct && ct.isCancellationRequested) { - t.complete("failure", new CanceledError()); - } - - // 第二步 - var seed; - var cancelHandler; - - if (ct) { - cancelHandler = function () { - clearTimeout(seed); - t.complete("failure", new CanceledError()); - } - } - - // 第三步 - var seed = setTimeout(function () { - if (ct) { - ct.unregister(cancelHandler); - } - - t.complete("success"); - }, delay); - - if (ct) { - ct.register(cancelHandler); - } - }); - } - -**第一步:判断CancellationToken状态。**取消操作由CancellationToken类型对象来提供,但由于其往往是可选操作,因此`ct`参数可能为`undefined`。在sleep方法一开始,我们先判断`ct.isCancellationRequested`是否为true,“是”便直接将Task对象传递“取消”信息。这是因为在某些特殊情况下,该CancellationToken已经被标识为取消了,作为支持取消操作的异步绑定,这可以算作是一个习惯或是规范。 - -**第二步:准备取消方法。**在这里我们准备两个变量`seed`和`cancelHandler`,前者将在稍后发起`setTimeout`时赋值。我们只在用户传入`ct`时才创建`cancelHandler`方法,该方法执行时会使用`clearTimeout(seed)`来取消已经发起的`setTimeout`操作,并通过`complete`方法将该Task对象传递“取消”信息。 - -**第三步:发起异步操作并注册取消方法。**接着便要发起我们绑定的异步函数了。我们将`setTimeout`后得到的标识符保留在seed变量里,供之前的`cancelHandler`使用。在`delay`毫秒后会执行的方法中,我们将注册在`ct`上的取消方法去除,并通过`complete`方法将该Task对象标识为`"success"`。发起异步操作之后,再讲取消方法注册到`ct`上。当有人调用`ct`的`cancel`方法时,该取消方法便会被执行。 - -将一个支持取消的异步操作绑定为Task对象是最为麻烦的工作,幸好这样的操作并不多见,并且也有十分规则的模式可以遵循。 - -### 辅助方法 - -似乎将已有的异步操作绑定为Task对象是十分耗时的工作,但事实上它的工作量并不一定由我们想象中那么大。这是因为在相同的环境,类库或是框架里,它们各种异步操作都具有相同的模式。例如在Node.js中,基本都是`path.exists`和`fs.readFile`这种模式下的异步操作。因此在实际开发过程中,我们不会为各个异步操作各实现一份绑定方法,而是使用[异步增强模块](./powerpack.html)里的辅助方法,例如: - - var Jscex = require("jscex-jit"); - require("jscex-async").init(); - require("./jscex-async-powerpack").init(); - - var path = require("path"), - fs = require("fs"); - Jscexify = Jscex.Async.Jscexify; - - path.existsAsync = Jscexify.fromCallback(path.exists); - - fs.readAsync = Jscexify.fromStandard(fs.read, "bytesRead", "buffer"); - fs.writeAsync = Jscexify.fromStandard(fs.write, "written", "buffer"); - - fs.readFileAsync = Jscexify.fromStandard(fs.readFile); - fs.writeFileAsync = Jscexify.fromStandard(fs.writeFile); - -这便是JavaScript语言的威力。 - -## Jscex.Async.Task 类型详解 - -`Jscex.Async.Task`是Jscex异步模块内的标准异步模型。异步方法产生的Task对象,除了可以交给`$await`指令使用之外,也可以直接使用这个对象。这种做法在某些场合十分重要,例如要在系统中逐步引入Jscex的情况。 - -### 静态 create(delegate) - -该方法是Task类型上的静态方法,用于创建一个Task对象,多在将普通异步操作绑定为Task的时候使用。 - -参数`delegate`方法会在Task启动时(即`start`方法被调用时)执行,签名为`function (t)`,其中`t`即为此次`create`调用所返回的Task对象。 - -使用示例: - - Task.create(function (t) { - console.log(t.status); // running - }); - -### start() - -该方法用于启动任务,只可调用一次。 - -使用示例: - - var task = someAsyncMethod(); - task.start(); - -### addEventListener(ev, listener) - -该方法用于添加一个事件处理器,只能在Task对象状态为`ready`或`running`的时候添加。 - -参数`ev`为是以字符串表示的事件名,可以为: - -* **success**:任务执行成功时触发,此时该任务的`status`字段为`succeeded`,且`result`字段为执行结果。 -* **failure**:任务执行失败时触发,此时该任务的`status`字段为`faulted`或`canceled`(视错误对象的`isCancallation`字段而定),且`error`字段为错误对象。 -* **complete**:无论任务成功还是失败,都会触发该事件。 - -参数`listener`为事件处理方法,无参数,执行时`this`为触发事件的Task对象。 - -使用示例: - - var task = someAsyncMethod(); - - task.addEventListener("success", function () { - console.log("Task " + this.id + " is succeeded with result: " + this.result); - }); - - task.addEventListener("failure", function (t) { - console.log("Task " + this.id + " is failed with error: " + this.error); - }); - - task.addEventListener("complete", function (t) { - console.log("Task " + this.id + " is completed with status: " + this.status); - }); - -### removeEventListener(ev, listener) - -该方法用于去除一个事件处理器,提供与`addEventListener`功能相反的操作。值得注意的是,在`complete`方法调用之后,Task对象会自动释放对事件处理器,不会继续保持对它们的引用。 - -### complete(type, value) - -该方法用于通知该Task对象已“完成”(无论结果如何),多在将普通异步操作绑定为Task的时候使用。根据不同情况,参数的值应分别为: - -* **成功**:参数`type`为`"success"`,`value`为任务的执行结果。 -* **出错**:参数`type`为`"failure"`,`value`为错误对象,其`isCancellation`字段为false。 -* **取消**:参数`type`为`"failure"`,`value`为错误对象,其`isCancellation`字段为true。 - -使用示例: - - fs.readFileAsync = function (path) { - return Task.create(function (t) { - fs.readFile(path, function (err, data) { - if (err) { - t.complete("failure", err); // 出错 - } else { - t.complete("success", data); // 成功 - } - }); - }); - } - -### id - -该字段为Task对象的标识符,为全局唯一的自增整型。 - -### status - -该字段标识Task对象的状态,可分为以下几种情况: - -* **ready**:创建完成,等待启动。 -* **running**:正在执行。 -* **succeeded**:执行成功。 -* **faulted**:执行出错。 -* **canceled**:执行已取消。 - -### result - -该字段保存了Task对象执行**成功**后得到的结果,在异步方法内将作为`$await`指令的返回值。 - -### error - -该字段保存了Task对象执行失败(**出错**或**取消**)后的错误对象,在异步方法内将作为异常抛出。 - -## Jscex.Async.CancellationToken - -实现任务取消功能时离不开`Jscex.Async.CancellationToken`对象,它有以下成员: - -### register(handler) - -该方法用于注册一个取消时的回调方法`handler`,它会在`cancel`方法调用时执行。如果`cancel`已经调用过,则`handler`会立即执行。 - -### unregister(handler) - -该方法用于去除取消时的回调方法`handler`,它是`register`方法的相反操作。 - -### cancel() - -该方法用于发出取消请求,调用后会将`isCancellationRequested`字段设为true,并执行所有已注册的取消回调方法。取消后,所有的回调方法会被释放,CancellationToken对象不会保留对取消回调方法的引用。 - -### isCancellationRequested - -该字段表示是否已经提出取消请求。 - -### throwIfCancellationRequested() - -该方法用于在取消请求已经提出的情况下抛出一个`isCancellation`为true的错误对象。它是一个方便开发者使用的辅助方法,简单等价为: - - if (this.isCancellationRequested) { - throw new Jscex.Async.CancelledError(); - } - -## 示例 - -### 浏览器示例 - -* [时钟](samples/clock.html):演示最基础的使用方式。 -* [排序算法动画](samples/sorting-algorithms.html):各类排序算法(冒泡,选择,快速)的演示动画。 -* [模态对话框](samples/modal-dialog.html):演示Jscex对于前端用户交互编写方式的改进。 -* [汉诺塔](samples/hanoi.html):汉诺塔解决方案的动画演示,同时涉及用户前端交互。 - -### Node.js示例 - -* [复制完整目录](samples/copy-dir.html):使用Node.js编写复制完整目录的功能 -* [静态文件服务器](samples/static-server.html):演示Node.js环境中最基础的使用方式。 -* [使用Express开发网站](samples/express-server.html):使用Jscex改善业务逻辑表现方式,并增强程序并发性。 - -### 其他 - -* [jQuery异步操作绑定](samples/jquery-bindings.html):提供部分jQuery及其相关插件中异步操作的绑定。 - -## 相关链接 - -* [源代码](https://github.com/JeffreyZhao/jscex/blob/master/src/jscex-async.js) -* [异步增强模块](./powerpack.html) -* [JIT编译器](../jit/) -* [AOT编译器](../aot/) diff --git a/doc/src/documents/zh-cn/manuals/async/powerpack.html.md b/doc/src/documents/zh-cn/manuals/async/powerpack.html.md deleted file mode 100644 index fcff651..0000000 --- a/doc/src/documents/zh-cn/manuals/async/powerpack.html.md +++ /dev/null @@ -1,378 +0,0 @@ ---- -layout: manual-zh-cn -title: 异步增强模块 ---- - -## 简介 - -Jscex[异步模块](./)中提供了Jscex支持异步编程的核心类库,例如Task类型以及异步构造器。有了异步模块,我们就可以编写异步方法,或是将某种异步操作封装为Task对象等等。但是,异步模块除了提供了核心功能以外,并没有对日常异步开发中的常见任务给与足够的支持。而[异步增强模块](https://github.com/JeffreyZhao/jscex/blob/master/src/jscex-async-powerpack.js)便对常见的异步开发模式,或是异步操作的绑定模式提供了支持。 - -### 依赖 - -* 动态依赖:[异步模块](./) -* 静态依赖:无 - -## 引入Jscex异步增强模块 - -如果您要使用Jscex异步增强模块,首先必须引入Jscex[异步模块](./),此时您会得到了一个Jscex根对象,之后再初始化异步增强模块。 - -### Node.js - -如果您使用的是Node.js,可以直接使用[Node Package Manager](http://npmjs.org/)(即npm命令)安装最新的jscex-async-powerpack模块: - - npm install jscex-async-powerpack - -然后便可以在脚本中引入并初始化这个模块: - - // ... 已引入Jscex异步模块 ... - - require("jscex-async-powerpack").init(); // 引入并初始化Jscex异步模块 - -### 浏览器 - -如果您要在浏览器里使用Jscex异步增强模块,则需要在页面中引入jscex-async-powerpack-x.y.z.js文件: - - - -此时异步增强模块会自动为根上的Jscex对象添加异步增强模块相关的成员。该方法也适用于各类**没有**实现CommonJS规范的JavaScript运行环境。 - -## 异步方法 - -异步增强模块中提供了一些常见的异步方法,可直接使用。这些异步方法都定义在`Jscex.Async`模块之上。 - -### sleep(delay, [ct]) - -`sleep`方法用于将当前的异步方法暂停一段时间。该方法接受两个参数: - -1. `delay`:表示暂停时长,单位为毫秒。 -2. `ct`:可选参数,用于取消暂停操作的CancellationToken对象。 - -该异步方法没有返回值。 - -使用示例: - - var ct = new Jscex.Async.CancellationToken(); - - var printEverySecondAsync = eval(Jscex.compile("async", function (texts, ct) { - for (var i = 0; i < texts.length; i++) { - $await(Jscex.Async.sleep(1000, ct)); - console.log(texts[i]); - } - })); - -### onEvent(target, eventName, [ct]) - -`onEvent`方法用于监听某个对象上某个事件的“下一次”触发。该方法接受三个参数: - -1. `target`:目标对象。 -2. `eventName`:事件名。 -3. `ct`:可选参数,用于取消监听操作的CancellationToken对象。 - -为了保证兼容性,`onEvent`会使用目标对象上的`addEventListener`、`addListener`或是`attachEvent`方法来监听事件,并在操作结束或是取消之后使用`removeEventListener`、`removeListener`或是`detachEvent`方法来取消监听。`onEvent`将会返回事件对象,即事件触发时传递给监听器的第一个参数。 - -使用示例: - - var Async = Jscex.Async; - var ct = new Async.CancellationToken(); - - var drawLinesAsync = eval(Jscex.compile("async", function (board, ct) { - var currPoint = $await(Async.onEvent(board, "click", ct)); - - while (true) { - var nextPoint = $await(Async.onEvent(board, "click", ct)); - - drawLine( - { x: currPoint.clientX, y: currPoint.clientY }, - { x: nextPoint.clientX, y: nextPoint.clientY }); - - currPoint = nextPoint; - } - })); - -## 任务协作 - -Jscex异步增强模块也包含了一些常见的任务协作方式,它们作为静态方法定义在`Jscex.Async.Task`类型上。 - -### whenAll(tasks) - -`whenAll`方法接受一个Task对象的集合,该集合可以是一个数组或是键值对,甚至是数组与键值对的任意组合。`whenAll`会返回一个新的Task对象,该新对象只有在所有Task都完成(无论成功与否)之后才会结束。假如所有的输入Task对象都成功执行完毕,则新的Task对象会返回一个结果集合,其结果与输入Task集合一一对应。 - -使用示例: - - var Task = Jscex.Async.Task; - - var getFollowersAsync = eval(Jscex.compile("async", function (userId) { - - // 获取所有追随者的ID - var followerIds = $await(getFollowerIds(userId)); - - return $await(Task.whenAll({ - user: getUserAsync(userId), - followers: followerIds.map(function (id) { - return getUserAsync(id); - }) - })); - - // 返回结果: - // { - // user: - // followers: [ , , … ] - // } - }); - -在启动`whenAll`返回的新Task对象时,会立即启动所有输入中尚未开始执行的Task对象。假如有一个或多个输入任务失败,则新Task对象也会处于失败状态。在这种情况下,新Task对象的`error`属性是一个`Jscex.Async.AggregateError`对象,其`children`属性是一个数组,包含了所有出错Task的错误对象,但顺序不定。例如: - - var Task = Jscex.Async.Task; - var AggregateError = Jscex.Async.AggregateError; - - var executeInParallel = eval(Jscex.compile("async", function (t0, t1) { - - try { - return $await(Task.whenAll([ t0, t1 ])); - } catch (ex) { - console.log(AggregateError.isTypeOf(ex)); // true - for (var i = 0; i < ex.children.length; i++) { - console.log(ex.children[i]); - } - } - }); - -### whenAll(t0[, t1[, t2[, …]]]) - -`whenAll`方法可以接受一系列的Task对象,并返回一个新的Task对象,该新对象只有在所有Task都成功结束之后才会结束,并使用数组返回所有Task对象的结果,其顺序与输入Task的顺序一一对应。 - -使用示例: - - var Task = Jscex.Async.Task; - - var getFollowersAsync = eval(Jscex.compile("async", function (userId) { - - // 获取所有追随者的ID - var followerIds = $await(getFollowerIds(userId)); - - var results = $await(Task.whenAll( - getUserAsync(userId), - getFollowerListAsync(followerIds) - )); - - return { - user: results[0], - followers: results[1] - }; - }); - -总而言之,以下这条语句: - - Task.whenAll(t1, t2, t3, …, tN) - -等价于: - - Task.whenAll([ t1, t2, t3, …, tN ]) - -### whenAny(t0[, t1[, t2[, …]]]) - -`whenAny`方法可以接受一系列的Task对象,并返回一个新的Task对象,该新对象会在输入的Task中任意一个完成时(无论成功失败)结束。该方法的返回值为一个对象,其`key`字段为任务在输入时的下标,其`task`对象即为第一个完成的任务。 - -例如,我们可以在Node.js中使用`pipe`方法传递数据流内的数据: - - var fs = require("fs"); - - var streamIn = fs.createReadStream("input.txt"); - var streamOut = fs.createWriteStream("output.txt"); - streamIn.pipe(streamOut); - -此时可能会有三种情况发生: - -1. `streamOut`的`close`事件触发,表示正常结束。 -2. `streamOut`的`error`事件触发,表示输出流异常。 -3. `streamIn`的`error`事件触发,表示输入流异常。 - -理论上来说,三种情况每次只会发生一种,因此我们可以编写这样的代码: - - var fs = require("fs"); - var Async = Jscex.Async; - var Task = Async.Task; - - var copyFileAsync = eval(Jscex.compile("async", function (src, target) { - var streamIn = fs.createReadStream("input.txt"); - var streamOut = fs.createWriteStream("output.txt"); - streamIn.pipe(streamOut); - - var any = $await(Task.whenAny( - Async.onEvent(streamOut, "close"), - Async.onEvent(streamOut, "error"), - Async.onEvent(streamIn, "error") - )); - - // 如果不是第一个输入完成,则意味着出错了 - if (any.key != 0) { - throw any.task.result; - } - })); - -在启动`whenAny`返回的新Task对象时,会立即启动所有输入中尚未开始执行的Task对象。由于“完成”不分成功失败,因此`whenAny`返回的新对象永远不会抛出异常。 - -### whenAny(taskArray) - -`whenAny`方法亦可接受一个Task对象数组`taskArray`作为参数,其余表现与上述重载完全相同。 - -使用示例: - - var fs = require("fs"); - var Async = Jscex.Async; - var Task = Async.Task; - - var copyFileAsync = eval(Jscex.compile("async", function (src, target) { - var streamIn = fs.createReadStream("input.txt"); - var streamOut = fs.createWriteStream("output.txt"); - streamIn.pipe(streamOut); - - var tasks = [ - Async.onEvent(streamOut, "close"), - Async.onEvent(streamOut, "error"), - Async.onEvent(streamIn, "error")]; - - var any = $await(Task.whenAny(tasks)); - - // 如果不是第一个输入完成,则意味着出错了 - if (any.key != 0) { - throw any.task.result; - } - })); - -### whenAny(taskMap) - -`whenAny`方法的第三个重载可以接受一个对象`taskMap`作为参数,该对象上的每个字段都对应一个Task对象。与之前的两个重载相比,新Task对象的返回值中的`key`字段保存了完成的那个Task对象所对应的键值。该方法的其他表现与之前的两个重载完全相同。 - -使用示例: - - var fs = require("fs"); - var Async = Jscex.Async; - var Task = Async.Task; - - var copyFileAsync = eval(Jscex.compile("async", function (src, target) { - var streamIn = fs.createReadStream("input.txt"); - var streamOut = fs.createWriteStream("output.txt"); - streamIn.pipe(streamOut); - - var any = $await(Task.whenAny({ - end: Async.onEvent(streamOut, "close"), - errorOut: Async.onEvent(streamOut, "error"), - errorIn: Async.onEvent(streamIn, "error") - })); - - // 如果完成的任务不是end,则意味着出错了 - if (any.key != "end") { - throw any.task.result; - } - })); - -## 异步操作绑定 - -如果要在Jscex异步方法中使用现有的异步操作,则需要将其绑定为Jscex中标准的Task对象。一般来说,绑定一个异步操作是一件简单直观的事情,但是像Node.js中提供了大量的异步操作,将其一一绑定也是一件不小的工作量。幸运的是,同一平台内的异步操作都基本具有相同的模式,我们可以通过编写一些的简单的辅助方法,来统一完成绑定操作。 - -Jscex异步增强模块也包含了一些绑定常见异步接口的辅助方法,它们都定义在`Jscex.Async.Binding`模块下面。请注意:在之前的版本中,这个模块也被称为Jscexify,现在您也可以继续使用Jscexify这个别名来代替Binding,但是在未来的版本中将其去除。 - -### fromCallback(fn) - -某些异步操作会直接使用回调函数返回结果,例如Node.js中Path模块的`exists`方法: - - var path = require("path"); - path.exists("/etc/passwd", function (exists) { - // exists参数表示是否存在 - }); - -此时,便可以使用`fromCallback`将此类异步操作绑定为一个返回Task对象的异步方法: - - var Binding = Jscex.Async.Binding; - path.existsAsync = Binding.fromCallback(path.exists); - - // 某Jscex异步方法内部 - var exists = $await(path.existsAsync("/etc/passwd")); - -某些异步操作会为回调函数传入多个参数,例如: - - var split = function (numbers, n, callback) { - var smaller = 0, larger = 0, equals = 0; - for (var i = 0; i < numbers.length; i++) { - var num = numbers[i]; - if (num == n) equals ++; - else if (num > n) larger ++; - else smaller ++; - } - - callback(equals, larger, smaller); - } - -此时,也可以在`fromCallback`内逐个指定参数名称,这样最后Task对象的结果将会是一个包含这些字段的对象: - - var splitAsync = Binding.fromCallback(split, "equals", "larger", "smaller"); - - // 某Jscex异步方法内部 - var result = $await(splitAsync([1, 2, 3, 4, 5, 6], 3)); - console.log(result.equals); // 1 - console.log(result.smaller); // 2 - console.log(result.larger); // 3 - -当然,如果您只关注回调函数的第一个参数,甚至一个都不关注,自然也可以在使用`fromCallback`的时候不列出参数名称。 - -### fromStandard(fn) - -`fromCallback`支持的是以回调形式返回结果的异步操作,而`fromStandard`返回的便是“可能会失败”的“标准接口”,它会将回调函数的第一个参数作为错误对象,如果存在这个对象,则意味着该异步操作出现了错误,于是将其当作异常对象抛出。而真正的“返回值”,则是回调函数收到的第二个参数。 - -例如在Node.js中,绝大部分接口都遵守了这样的模式,例如File System模块下的许多方法: - - var fs = require("fs"); - - fs.readdir(path, function (err, files) { … }); - fs.stat(path, function (err, stats) { … }); - fs.mkdir("./world", function (err) { … }); - -此时我们便可以使用`fromStandard`创建这些异步操作的绑定: - - fs.readdirAsync = Binding.fromStandard(fs.readdir); - fs.statAsync = Binding.fromStandard(fs.stat); - fs.mkdirAsync = Binding.fromStandard(fs.mkdir); - -绑定后的异步方法在使用时可能会抛出异常: - - try { - $await(fs.mkdir("./hello-world")); - } catch (ex) { - // 出错了 - } - -与`fromCallback`一样,如果回调函数拥有多个返回值(即除了第一个表示错误的参数以外,还拥有两个或以上的参数),也可以在`fromStandard`后列出参数名,例如Node.js中的Child Processes模块有个`exec`方法: - - var exec = require('child_process').exec; - - exec('ls -l', function (error, stdout, stderr) { - if (error) { - console.log('exec error: ' + error); - } else { - console.log('stdout: ' + stdout); - console.log('stderr: ' + stderr); - } - }); - -我们可以将其绑定为: - - var execAsync = Binding.fromStandard(exec, "stdout", "stderr"); - - // 某Jscex异步方法内 - try { - var result = $await(execAsync("ls -l")); - console.log("stdout: " + result.stdout); - console.log("stderr: " + result.stderr); - } catch (ex) { - console.log('exec error: ' + exec); - } - -当然,如果您只关注回调函数除了错误外的第一个参数,甚至一个都不关注,自然也可以在使用`fromStandard`的时候不列出参数名称。 - -## 相关链接 - -* [源代码](https://github.com/JeffreyZhao/jscex/blob/master/src/jscex-async-powerpack.js) -* [异步模块](./) -* [Node.js异步增强模块](node.html) -* [浏览器异步增强模块](browser.html) \ No newline at end of file diff --git a/doc/src/documents/zh-cn/manuals/async/samples/comments.html.md b/doc/src/documents/zh-cn/manuals/async/samples/comments.html.md deleted file mode 100644 index b70c379..0000000 --- a/doc/src/documents/zh-cn/manuals/async/samples/comments.html.md +++ /dev/null @@ -1,119 +0,0 @@ ---- -layout: manual-zh-cn -title: 评论管理(异步编程示例) ---- - -## 描述 - -如今前端应用中的信息管理场景并不罕见,例如评论管理便是常见的一例:用户可以在页面上通过页面中的一系列行为来添加,删除或是修改评论,同时页面中使用AJAX异步操作与服务器端进行交互,避免页面刷新,以此带来优秀的用户体验。 - -由于JavaScript和HTML的限制,各种用户交互的参与都必须以回调函数(或是事件,而事件其实也可以认为是回调函数的一种)。但实际上,这种做法在很多情况下不能说是最直接且最易用的表达方式。例如,一个统一的事务原本可以连贯的表现,却被异步操作将原本连贯一致的逻辑拆分成小段方法来处理。 - -此外,由于异步操作都会消耗一定时间,开发人员还必须防备一些意外的情况。例如,一个AJAX请求发起之后尚未返回,此时用户继续在页面上进行操作,如果缺少有效的防护措施,页面便会进入不一致的状态,甚至必须重新加载页面才能恢复过来。自然,错误处理也是一个不可忽视的环节。 - -本文将通过实现一个评论管理功能,演示如何使用[Jscex异步模块](../)来轻松直接地表达一些关系紧密的事务操作,并解决以上所提到的各类问题。 - -## 需求 - -评论管理是如今前端应用中典型的案例,用户可以添加评论,删除或是更新现有的评论。其中整个过程都是AJAX及DOM操作,页面不会刷新,用户体验良好。 - -打开页面后,页面上显示若干已有评论。页面下方有一个文本框,可以由用户输入评论内容。还有一个“Add”按钮,点击后将会发起一个AJAX操作向服务器端添加数据。此时可能出现两种情况: - -* **成功:**将评论内容显示在上方列表中,并清空文本框。 -* **失败:**将错误信息提示给用户,其余不变,并等待用户再次提交。 - -每条评论均包含一个“Edit”按钮,点击之后将发起一个AJAX请求获取服务器端的评论内容,此时也可能发生两种情况: - -* **成功:**将评论内容显示在下方文本框内,隐藏“Add”按钮,显示“Update”和“Cancel”两个按钮(即进入“编辑模式”)。 -* **失败:**将错误信息提示给用户,其余不变。 - -用户可以点击“Cancel”按钮取消这次编辑,此时需要清空文本框,隐藏“Update”和“Cancel”按钮,并重新显示“Add”按钮。当用户编辑评论内容之后,也可以点击“Update”按钮发送AJAX请求至服务器端提交更改,此时还是可能发生两种情况: - -* **成功:**将评论更新至页面上对应的评论项,清空文本框,隐藏“Update”和“Cancel”按钮,并重新显示“Add”按钮。 -* **失败:**将错误信息提示给用户,其余不变,并等待用户再次提交(或是取消)。 - -最后,每条评论也都包含一个“Remove”按钮,点击后将要求用户确认删除,确认后发送AJAX请求至服务器端删除数据,同样可能发生两种情况: - -* **成功:**将页面上的评论项删除。 -* **失败:**将错误信息提示给用户,其余不变。 - -还有一些额外的要求: - -1. 发起AJAX请求时,需要显示“Loading”提示信息,AJAX操作完毕后(无论成功失败)隐藏。 -2. 在AJAX请求过程中,用户其他任何操作都无效,直至AJAX操作完毕,例如: - * 用户反复点击“Update”按钮。 - * 用户点击“Cancel”按钮。 - * 用户点击某条评论的“Edit”或“Remove”按钮。 -3. 只要不是在AJAX请求过程中,用户可以随时点击某条评论项的“Edit”按钮进入“编辑模式”,即使已经在编辑某条评论了。 - -## 相关页面元素及操作 - -页面上已经准备了一系列DOM元素及内置的方法,您可以直接用其进行开发。 - -### AJAX模拟操作 - -页面上定义了一系列模拟AJAX的异步操作,它们均有一定几率失败。 - - // 向服务器端提交评论,text为评论内容 - addServerComment(text, function (error, id, text) { - if (error) { - // 失败,error为错误信息 - } else { - // 成功,id为服务器端生成的评论标识,text为内容 - } - }); - - // 删除服务器端评论,id为评论标识 - removeServerComment(id, function (error) { - if (error) { - // 失败,error为错误信息 - } else { - // 成功 - } - }); - - var updateServerComment(id, function (error) { - if (error) { - // 失败,error为错误信息 - } else { - // 成功 - } - }); - - var loadServerComment(id, function (error, text) { - if (error) { - // 失败,error为错误信息 - } else { - // 成功,text为评论内容 - } - }); - -### DOM操作 - -页面上同样定义了一系列操作DOM的方法。 - - // 在页面上添加评论,id为标识,text为内容 - addClientComment(id, text); - - // 从页面上删除评论,id为标识 - removeClientComment(id); - - // 更新页面上的评论,id为标识,text为内容 - updateClientComment(id, text); - -### 辅助操作 - -页面上还定义了一些其他辅助方法。 - - // 显示Loading提示信息 - showLoadingMessage(); - - // 隐藏Loading提示信息 - hideLoadingMessage(); - -## 相关链接 - -* [在线演示](http://repository.jscex.info/samples/async/comments.html) -* [完整代码](https://github.com/JeffreyZhao/jscex/blob/master/samples/async/comments.html) -* [Jscex异步模块](../) -* [Jscex异步增强模块](../powerpack.html) \ No newline at end of file diff --git a/doc/src/documents/zh-cn/manuals/async/samples/copy-dir.html.md b/doc/src/documents/zh-cn/manuals/async/samples/copy-dir.html.md deleted file mode 100644 index 5730269..0000000 --- a/doc/src/documents/zh-cn/manuals/async/samples/copy-dir.html.md +++ /dev/null @@ -1,520 +0,0 @@ ---- -layout: manual-zh-cn -title: 复制完整目录(异步编程示例) ---- - -## 描述 - -[Node.js](http://nodejs.org/)是目前最流行的JavaScript开发技术,通过异步IO这一卖点吸引了许多技术人员的目光。Node.js的绝大部分API都是异步的,通过回调函数或是事件(也可以视为一种回调函数)进行交互,这虽然提高了应用程序的性能及伸缩性,却也显著提高了程序复杂度。 - -Jscex对Node.js提供了直接的支持。本文将通过实现一个目录复制的功能,演示如何使用[Jscex异步模块](../)辅助Node.js开发。我们会发现,无论是条件判断也好,递归调用也罢,使用Jscex开发异步程序与普通的程序编写模式几乎没有区别。 - -## 需求 - -除了网络相关的支持以外,Node.js也提供了一些文件操作的API,我们可以用其进行创建、删除、修改等常见的文件操作。不过Node.js目前并没有提供文件的复制的API,更不论复制整个目录了。这便是我们这次要完成的功能。 - -总体而言,我们是要编写一个`copy-dir.js`模块,在执行之后: - - node copy-dir.js ./hello ./world - -会将“./hello”目录下的内容完整地复制到“./world”目录去,包括子目录里面的内容。在复制过程中需要打印出一定信息,例如: - - File "./world/skip.txt" exists, overwrite? > no - Copying "./hello/abc.txt" to "./world/abc.txt" ... done. - File "./world/error.txt" exists, overwrite? > yes - Copying "./hello/error.txt" to "./world/error.txt" ... ERROR!!! - Copying "./hello/large-files/1.zip" to "./world/large-files/1.zip" ... - -规则如下: - -1. 假如目标文件已存在,则提示用户是否覆盖: - * 用户输入“yes”或“y”(不区分大小写):覆盖目标文件。 - * 用户输入“no”或“n”(不区分大小写):跳过该文件。 -2. 开始复制文件是,打印“Copying”信息。 - * 复制结束,打印“done”字样。 - * 复制失败,打印“ERROR!!!”字样。 - -## 相关API - -要完成以上操作,可能涉及到以下一些API。由于我们这个模块可能会到服务器或是远程环境下,因此每一步操作都需要是异步的,降低任何阻塞的可能性: - -### 复制文件 - -在Node.js中复制一个文件有两种方式,一种是使用File System模块的[open](http://nodejs.org/docs/latest/api/fs.html#fs.open)方法打开文件,[read](http://nodejs.org/docs/latest/api/fs.html#fs.read)和[write](http://nodejs.org/docs/latest/api/fs.html#fs.write)方法读写文件,并使用[close](http://nodejs.org/docs/latest/api/fs.html#fs.close)方法关闭文件,如下: - - var fs = require("fs"); - - // 打开一个文件,得到文件标识符fd - fs.open(path, flags, function (err, fd) { - - }); - - // 读文件 - fs.read(fd, buffer, offset, length, position, function (err, bytesRead, buffer) { - - }); - - // 写文件 - fs.write(fd, buffer, offset, length, position, function (err, written, buffer) { - - }); - - // 关闭文件 - fs.close(fd, function () { - - }) - -或是使用数据流传输的方式,打开两个Stream并使用[pipe](http://nodejs.org/docs/latest/api/streams.html#stream.pipe)方法传输数据。数据传输完成之后,会触发目标数据流的`close`事件,在出错时则会在两个Stream对象上引发error事件: - - var fs = require("fs"); - - var streamIn = fs.createReadStream("./input.txt"); - var streamOut = fs.createWriteStream("./output.txt"); - - streamIn.pipe(streamOut); - - streamIn.on("error", function (error) { - // 输入流异常 - }); - - streamOut.on("error", function (error) { - // 输出流异常 - }); - - streamOut.on("close", function () { - // 数据传输完毕 - }); - -通常来说,第二种方法的性能相对更高一些。此外,在Node.js的util模块中也提供了`pump`辅助方法来实现两个数据流的传输: - - var util = require("util"); - util.pump(streamIn, streamOut, function (err) { - if (err) { - // error occurred - } - }); - -使用这种方法,在处理错误的时候会更方便一些。 - -### 其他操作 - -向屏幕打印文字: - - var util = require("util"); - util.print("Hello World"); - -读取用户操作: - - var rl = require("readline").createInterface(process.stdin, process.stdout); - rl.question("Please answer the question: ", function (answer) { - // 用户回车后获得答案 - }); - -检查目录或文件是否存在: - - var path = require("path"); - path.exists("/etc/passwd", function (exists) { - // exists参数表示是否存在 - }); - -创建目录: - - var fs = require("fs"); - fs.mkdir("./world", function (err) { - if (err) { - // 出错 - } - }); - -列出指定里的内容: - - var fs = require("fs"); - fs.readdir(path, function (err, files) { - // files为目录里的文件及子目录(不包含子目录里的内容) - }); - -判断该路径是文件还是目录: - - var fs = require("fs"); - fs.stat(path, function (err, stats) { - if (stats.isFile()) { - // 文件 - } else if (stats.isDirectory()) { - // 目录 - } - }); - -## 实现 - -### 引入组件 - -首先我们引入实现功能所需要的Node.js组件: - - var fs = require("fs"); - var path = require("path"); - var util = require("util"); - var rl = require("readline").createInterface(process.stdin, process.stdout); - -并为相关的方法创建Jscex绑定: - - var Jscex = require("../../src/jscex"); - require("../../src/jscex-jit").init(); - require("../../src/jscex-async").init(); - require("../../src/jscex-async-powerpack").init(); - - var Async = Jscex.Async; - var Task = Async.Task; - var Binding = Async.Binding; - - // path bindings - path.existsAsync = Binding.fromCallback(path.exists); - - // util bindings - util.pumpAsync = Binding.fromStandard(util.pump); - - // rl bindings - rl.questionAsync = Binding.fromCallback(rl.question); - - // fs bindings - fs.mkdirAsync = Binding.fromStandard(fs.mkdir); - fs.readdirAsync = Binding.fromStandard(fs.readdir); - fs.statAsync = Binding.fromStandard(fs.stat); - fs.closeAsync = Binding.fromStandard(fs.close); - fs.openAsync = Binding.fromStandard(fs.open); - fs.readAsync = Binding.fromStandard(fs.read); - fs.writeAsync = Binding.fromStandard(fs.write); - -由此可见,虽然涉及的方法众多,但都被[异步增强模块](../powerpack.html)提供的绑定辅助方法`fromCallback`及`fromStandard`一网打尽。 - -### 目录复制 - -目录复制是个异步方法(因为其中必然涉及到大量的异步IO操作),它的工作是将源目录`srcDir`里的所有内容复制到目标目录`targetDir`中去签名如下: - - var copyDirAsync = eval(Jscex.compile("async", function (srcDir, targetDir) { - ... - })); - -首先,它将判断目标目录是否已经存在,如果不存在则创建一个,其中会用到`path.exists`和`fs.mkdir`两个异步方法: - - var copyDirAsync = eval(Jscex.compile("async", function (srcDir, targetDir) { - - var exists = $await(path.existsAsync(targetDir)); - if (!exists) { - $await(fs.mkdirAsync(targetDir)); - } - - ... - })); - -然后,使用`fs.readdir`异步方法获取源目录内所有的文件及目录,并使用循环遍历每一项。对每一项使用`fs.stat`异步方法判断其种类,如果是文件,则使用`copyFileAsync`复制文件,否则便递归调用自身以复制子目录: - - var copyDirAsync = eval(Jscex.compile("async", function (srcDir, targetDir) { - - ... - - var files = $await(fs.readdirAsync(srcDir)); - for (var i = 0; i < files.length; i++) { - var srcPath = path.join(srcDir, files[i]); - var targetPath = path.join(targetDir, files[i]); - - var stat = $await(fs.statAsync(srcPath)); - if (stat.isFile()) { - $await(copyFileAsync(srcPath, targetPath)); - } else { - $await(copyDirAsync(srcPath, targetPath)); - } - } - })); - -### 文件复制 - -文件复制也是个异步方法,它的工作是将源文件`srcFile`复制为目标文件`targetFile`,签名如下: - - var copyFileAsync = eval(Jscex.compile("async", function (srcFile, targetFile) { - ... - })); - -首先使用`path.exists`异步方法判断目标文件是否存在,如果存在则提示用户做出覆盖与否的选择,直到用户给出正确的输入之后才会继续进行下去: - - var copyFileAsync = eval(Jscex.compile("async", function (srcFile, targetFile) { - - var exists = $await(path.existsAsync(targetFile)); - if (exists) { - var message = util.format('File "%s" exists, overwirte? (yes/no) > ', targetFile); - while (true) { - var answer = $await(rl.questionAsync(message)); - if (/^(?:yes|y)$/i.test(answer)) { - break; - } else if (/^(?:no|n)$/i.test(answer)) { - return; - } - } - } - - ... - })); - -假如确定要复制目标文件,则进行复制操作。我们使用`try…catch`捕获复制操作可能出现的异常,一旦发生错误,则以为着文件复制失败,打印“ERROR”字样: - - var copyFileAsync = eval(Jscex.compile("async", function (srcFile, targetFile) { - - ... - - util.print(util.format('Copying "%s" to "%s" ... ', srcFile, targetFile)); - - try { - $await(copyFileByLoopAsync(srcFile, targetFile)); - // $await(copyFileByPumpAsync(srcFile, targetFile)); - // $await(copyFileByPipeAsync(srcFile, targetFile)); - - util.print("done\n"); - } catch (ex) { - util.print("ERROR!!!\n"); - } - - })); - -这里我们会实现之前所提到的所有三种复制方式,您可以选择任意一种来进行文件复制操作。 - -#### 使用file.read,file.write方法复制文件 - -第一种复制文件的方式基于`file.read`和`file.write`方法,循环读写,直至复制完毕: - - var copyFileByLoopAsync = eval(Jscex.compile("async", function (srcFile, targetFile) { - var fdIn = $await(fs.openAsync(srcFile, "r")); - var fdOut = $await(fs.openAsync(targetFile, "w")); - - var bufferSize = 10240; - var buffer = new Buffer(bufferSize); - - try { - while (true) { - var lengthRead = $await(fs.readAsync(fdIn, buffer, 0, bufferSize, null)); - if (lengthRead <= 0) break; - $await(fs.writeAsync(fdOut, buffer, 0, lengthRead, null)); - } - } finally { - $await(fs.closeAsync(fdIn)); - $await(fs.closeAsync(fdOut)); - } - })); - -当读写操作出错的时候,自然会有异常向方法外抛出,而`finally`则保证即便在出错的情况下,两个文件描述符`fdIn`和`fdOut`均能正常关闭,不会发生泄露。 - -#### 使用stream.pipe复制文件 - -第二种复制文件的方式,则是开启两个数据流,并使用`pipe`方法传递数据: - - var copyFileByPipeAsync = eval(Jscex.compile("async", function (srcFile, targetFile) { - var streamIn = fs.createReadStream(srcFile); - var streamOut = fs.createWriteStream(targetFile); - streamIn.pipe(streamOut); - - var any = $await(Task.whenAny({ - errorIn: Async.onEvent(streamIn, "error"), - errorOut: Async.onEvent(streamOut, "error"), - end: Async.onEvent(streamOut, "close") - })) - - if (any.key != "end") { - throw any.task.result; - } - })); - -在发起`pipe`之后会有三种可能,一种是引发`streamOut`的`close`事件,表示传输完毕。另一种可能是引发`streamIn`或`streamOut`的`error`事件,则意味着传输过程中出现了错误。多者取一,因此我们使用异步增强库中的`whenAny`方法进行辅助。假如完成的不是代表了成功的任务,则将其结果(即错误对象)作为异常抛出。 - -#### 使用util.pump传输数据 - -与`pipe`相比,`util.pump`是个更为易用的接口: - - var copyFileByPumpAsync = eval(Jscex.compile("async", function (srcFile, targetFile) { - var streamIn = fs.createReadStream(srcFile); - var streamOut = fs.createWriteStream(targetFile); - $await(util.pumpAsync(streamIn, streamOut)); - })); - -由于`util.pump`方法使用了标准的传递错误的方式,因此使用`fromStandard`创建的绑定,已经能够正确的抛出异常。 - -## 相关链接 - -* [完整代码](https://github.com/JeffreyZhao/jscex/blob/master/samples/async/copy-dir.js) -* [Jscex异步模块](../) -* [Jscex异步增强模块](../powerpack.html) - -## 附录 - -### 原生实现方式 - -对比所需,在此附上原生实现方式(感谢[王宇](http://yue.st/)的支持)。 - - var rl = require('readline').createInterface(process.stdin, process.stdout) - , fs = require('fs') - , path = require('path') - , util = require('util') - - function copyFileByPump (file, remainFiles, destDir) { - var destFile = path.join(destDir, file) - , streamIn = fs.createReadStream(file) - , streamOut = fs.createWriteStream(destFile) - util.pump(streamIn, streamOut, function (err) { - if (err) { - console.log('ERROR!!!'); - copyDir(root, remainFiles, destDir) - } else { - console.log('done.'); - copyDir(root, remainFiles, destDir) - } - }); - } - - function copyFileByPipe (file, remainFiles, destDir) { - var destFile = path.join(destDir, file) - , streamIn = fs.createReadStream(file) - , streamOut = fs.createWriteStream(destFile) - - streamIn.pipe(streamOut); - - var onError, onClose; - var cleanUp = function () { - streamIn.removeListener("error", onError); - streamOut.removeListener("error", onError); - streamOut.removeListener("close", onClose); - }; - onError = function (err) { - cleanUp(); - console.log("ERROR!!!"); - copyDir(root, remainFiles, destDir); - }; - onClose = function () { - cleanUp(); - console.log('done.'); - copyDir(root, remainFiles, destDir); - }; - - streamIn.on("error", onError); - streamOut.on("error", onError); - streamOut.on("close", onClose); - } - - function copyFileByLoop (file, root, remainFiles, destDir) { - var destFile = path.join(destDir, file) - fs.open(path.join(root, file), 'r', function(err, fdIn) { - if (err) { - console.log('ERROR!!!'); - return copyDir(root, remainFiles, destDir) - } - fs.open(destFile, 'w', function(err, fdOut) { - if (err) { - console.log(err); - console.log('ERROR!!!'); - return copyDir(root, remainFiles, destDir) - } - var bs = 10240 - , buffer = new Buffer(bs) - function readcb (err, bytesRead) { - if (err) { - console.log('ERROR!!!'); - return copyDir(root, remainFiles, destDir) - } else if (bytesRead <= 0) { - fs.close(fdIn, function() { - fs.close(fdOut, function() { - console.log('done.') - copyDir(root, remainFiles, destDir) - }) - }) - } else { - fs.write(fdOut, buffer, 0, bytesRead, null, writecb); - } - } - function writecb (err, written) { - if (err) { - console.log('ERROR!!!'); - return copyDir(root, remainFiles, destDir) - } else { - fs.read(fdIn, buffer, 0, bs, null, readcb) - } - } - fs.read(fdIn, buffer, 0, bs, null, readcb) - }) - }) - } - - function copyDir (root, files, destDir) { - if (!Array.isArray(files)) { - return copyDir(root, [files], destDir) - } else if (!files.length) { - rl.close() - process.stdin.destroy() - return - } - - var firstFile = files[0] - , remainFiles = files.slice(1) - , destFile = path.join(destDir, firstFile) - fs.stat(path.join(root, firstFile), function(err, stat) { - if (stat.isFile()) { - path.exists(destFile, function(exists) { - if (exists) { - rl.question('File "' + destFile + '" exists, overwrite? > ', function (answer) { - if (answer.match(/yes/i) || answer.match(/y/i)) { - util.print('Copying "' + path.join(root, firstFile) + '" to "' + destFile + '" ... '); - //copyFileByPump(firstFile, root, remainFiles, destDir); - // copyFileByPipe(firstFile, root, remainFiles, destDir); - copyFileByLoop(firstFile, root, remainFiles, destDir); - } else if (answer.match(/no/i) || answer.match(/n/i)) { - copyDir(root, remainFiles, destDir) - } else { - copyDir(root, files, destDir) - } - }); - } else { - util.print('Copying "' + path.join(root, firstFile) + '" to "' + destFile + '" ... '); - //copyFileByPump(firstFile, root, remainFiles, destDir); - // copyFileByPipe(firstFile, root, remainFiles, destDir); - copyFileByLoop(firstFile, root, remainFiles, destDir); - } - }) - } else if (stat.isDirectory()) { - fs.readdir(path.join(root, firstFile), function(err, dirFiles) { - var next = function() { - copyDir(root, dirFiles.map(function(file) { - return path.join(firstFile, file) - }).concat(remainFiles), destDir) - } - path.exists(destFile, function(exists) { - if (!exists) { - fs.mkdir(destFile, function(err) { - next() - }) - } else { - next() - } - }) - }) - } - }) - } - - var src = process.argv[2] - , dest = process.argv[3] - if (dest) { - path.exists(dest, function(exists) { - if (!exists) { - fs.mkdir(dest, function(err) { - copyDir(src, '.', dest) - }) - } else { - fs.stat(dest, function(err, stat) { - if (err || !stat.isDirectory()) { - console.log('error on destDir') - } else { - copyDir(path.dirname(src), path.basename(src), dest) - } - }) - } - }) - } else { - console.log('usage: node copy-dir.js origDir destDir') - } - -如果您其他实现方式,请及时联系我。 \ No newline at end of file diff --git a/doc/src/documents/zh-cn/manuals/async/samples/jquery-bindings.html.md b/doc/src/documents/zh-cn/manuals/async/samples/jquery-bindings.html.md deleted file mode 100644 index 4ab95e6..0000000 --- a/doc/src/documents/zh-cn/manuals/async/samples/jquery-bindings.html.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -layout: manual-zh-cn -title: jQuery绑定(异步编程示例) ---- - -[jQuery](http://jquery.com)类库提供了丰富的异步操作,为了方便jQuery用户使用[Jscex异步模块](../),并演示常见的异步操作绑定模式。在绑定已有的异步操作时,我们通常会保持原有的参数输入方式不变,而只关注操作的“完成点”,这样使用者便可以快速地了解新方法的使用方式。 - -## jQuery异步操作 - -jQuery提供了AJAX等典型的异步操作。 - -### $.ajax - -AJAX方法是典型的异步操作,在jQuery中,发起一个AJAX请求使用的是`$.ajax`方法,例如: - - $.ajax({ - url: "./getSomething", - dataType: "text", - success: function (data, textStatus, jqXHR) { - ... - }, - error: function (jqXHR, textStatus, errorThrow) { - ... - } - }); - -以上代码会向地址`./getSomething`发起一个AJAX请求(`url: "./getSomething"`),用于获取文本内容(`dataType: "text"`),如果成功则执行`success`回调函数,其中`data`参数便是获得的文本内容,在出错时则执行`error`回调函数。因此,`success`和`error`便是该方法的两个完成点,绑定方式如下: - - $.ajaxAsync = function (options) { - return Task.create(function (t) { - - options.success = function (data, textStatus, jqXHR) { - t.complete("success", { - data: data, - textStatus: textStatus, - jqXHR: jqXHR - }); - } - - options.error = function (jqXHR, textStatus, errorThrow) { - t.complete("failure", { - jqXHR: jqXHR, - textStatus: textStatus, - errorThrow: errorThrow - }); - }; - - $.ajax(options); - }); - }; - -## jQuery UI异步操作 - -[jQuery UI](http://jqueryui.com/)提供了许多常见的UI组件,它们也可以视为异步操作。“异步操作”是指那些“在未来某一时刻结束的操作”,因此,如对话框的“关闭”即可视为该“显示对话框”异步操作的结束标志。 - -### $.fn.dialog - -jQuery UI提供了现成的[模态对话框组件](http://jqueryui.com/demos/dialog/),例如其最简单的使用方式: - -
-

Dialog messages.

-
- - - -`$("#some-id").dialog(…)`方法用于将某一个页面上的元素显示为对话框。我们设置`modal`参数为true,则它会成为一个模态窗口。`close`是一个回调函数,它会在窗体关闭的时候执行。如果我们要为对话框添加按钮,则可以这么实现: - - $("#dialog-demo").dialog({ - modal: true, - close: function () { … }, - buttons: { - "OK": function () { - $(this).dialog("close"); - }, - "Cancel": function () { - $(this).dialog("close"); - } - } - }); - -对于`dialog`方法来说,其“完成点”显然是其`close`回调函数,因此它的绑定为: - - $.fn.dialogAsync = function (options) { - var _this = this; - - return Task.create(function (t) { - - options.close = function () { - t.complete("success"); - } - - _this.dialog(options); - }); - } - -## 相关链接 - -* [完整代码](https://github.com/JeffreyZhao/jscex/blob/master/samples/async/jquery-bindings.js) -* [Jscex异步模块](../) \ No newline at end of file diff --git a/doc/src/documents/zh-cn/manuals/async/samples/modal-dialog.html.md b/doc/src/documents/zh-cn/manuals/async/samples/modal-dialog.html.md deleted file mode 100644 index 48cd998..0000000 --- a/doc/src/documents/zh-cn/manuals/async/samples/modal-dialog.html.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -layout: manual-zh-cn -title: 模态对话框(异步编程示例) ---- - -## 描述 - -如今前端应用的交互特性越来越多,而模态对话框是其中最常见的使用案例。由于JavaScript和HTML的限制,各种用户交互的参与都必须以回调函数(或是事件,而事件其实也可以认为是回调函数的一种)。但实际上,这种做法在很多情况下不能说是最直接且最易用的表达方式。 - -本文将通过一个模态对话框与AJAX操作交互相结合的示例,演示如何使用[Jscex异步模块](../)来轻松直接地表达结合紧密的一系列异步逻辑。 - -## 需求 - -“删除”是前端应用中常见的操作。由于包含一定危险性,在进行删除之前,我们经常会向用户进行确认,然后再向服务器端发送一个AJAX请求删除数据。这个示例的需求便由此而来,如下: - -1. 点击“清空”按钮,弹出确认对话框。 -2. 用户选择“确定”或“取消” - * 选择“确定” - 1. 发送AJAX请求至服务器端,完成后, - 2. 给用户以提示信息。 - * 选择“取消”,则给用户以提示信息。 - -为了保证用户体验,无论是“确定/取消”对话框,还是提示给用户看的信息,都不允许使用浏览器内置的`alert`和`confim`方法。也正是这点,增加了实现这一功能的复杂度。 - -## 相关API - -方便起见,我们使用jQuery来实现模态对话框及AJAX操作,相关用法及绑定方式请参考“[jQuery绑定](jquery-bindings.html)”。 - -## 实现 - -由于逻辑从按钮点击开始,我们便在按钮的`onclick`事件里调用一个异步方法: - - - -其中`emptyAsync`的实现是: - - var emptyAsync = eval(Jscex.compile("async", function () { - - // 弹出“确定/取消”对话框 - var ok = false; - $await($("#dialog-confirm").dialogAsync({ - modal: true, - buttons: { - "OK": function () { - ok = true; - $(this).dialog("close"); - }, - "Cancel": function () { - $(this).dialog("close"); - } - } - })); - - if (ok) { - // 用户选择“确定”,则发出AJAX请求 - var response = $await($.ajaxAsync({ - url: "modal-dialog.html", - dataType: "text" - })); - - // 给用户以信息提示 - $("#emptyLength").text(response.data.length); - $await($("#dialog-emptied").dialogAsync({ modal: true })); - } else { - // 用户选择“取消”,则给用户以提示信息 - $await($("#dialog-canceled").dialogAsync({ modal: true })); - } - - console.log("done"); - })) - -首先,我们使用`dialogAsync`函数弹出一个对话框,这是一个异步方法,将在对话框关闭时完成。在用户点击“确认”按钮时,我们将`ok`变量设为true。对话框关闭之后,如果`ok`变量的值为true,发起一个AJAX请求,将返回结果显示在页面上,再显示下一个模态对话框。如果`ok`为false,这意味着用户没有点击“确认”按钮,则用另一个模态对话框提示用户。 - -使用Jscex之后,程序员只需使用最传统的方式编写逻辑,而不会由于异步函数所需要的各式回调将逻辑打散,使程序既易写,又易读。 - -## 相关链接 - -* [在线演示](http://repository.jscex.info/samples/async/modal-dialog.html) -* [完整代码](https://github.com/JeffreyZhao/jscex/blob/master/samples/async/modal-dialog.html) -* [jQuery绑定](jquery-bindings.html) -* [Jscex异步模块](../) - -## 附录 - -### 原生实现方式 - -对比所需,在此附上原生实现方式: - - // 弹出“确定/取消”对话框 - $("#dialog-confirm").dialog({ - modal: true, - buttons: { - "OK": function () { - // 用户选择“确定”,则发出AJAX请求 - $(this).dialog("close"); - $.ajax({ - url: "modal-dialog.html", - dataType: "text", - success: function (data) { - // 给用户以信息提示 - $("#emptyLength").text(data.length); - $("#dialog-emptied").dialog({ - modal: true, - close: function () { - console.log("done"); - } - }); - } - }); - }, - "Cancel": function () { - // 用户选择“取消”,则给用户以提示信息 - $(this).dialog("close"); - $("#dialog-canceled").dialog({ - modal: true, - close: function () { - console.log("done"); - } - }); - } - } - }); - -如果您其他实现方式,请随时联系我。 \ No newline at end of file diff --git a/doc/src/documents/zh-cn/manuals/builderbase/index.html.md b/doc/src/documents/zh-cn/manuals/builderbase/index.html.md deleted file mode 100644 index 28ed429..0000000 --- a/doc/src/documents/zh-cn/manuals/builderbase/index.html.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -layout: manual-zh-cn -title: 构造器基础模块 ---- - -## 简介 - -Jscex构造器基础模块提供了Jscex各式构造器(如[异步模块](../jit/)中的异步构造器)所需的基本功能。理论上编写一个构造器不需要依赖该基础模块,但使用该基础模块中提供的功能,便可以在几十行代码里完成一个Jscex构造器。 - -### 依赖 - -* 动态依赖:无 -* 静态依赖:无 - -## 引入Jscex构造器基础模块 - -首先我们必须[引入核心组件](../core/),之后再基于这个对象初始化构造器基础模块。 - -### Node.js - -如果您使用的是Node.js,可以直接使用[Node Package Manager](http://npmjs.org/)(即npm命令)安装最新的jscex-builderbase包: - - npm install jscex-builderbase - -然后便可以在脚本中引入该组件: - - var Jscex = require("jscex"); // 引入核心组件 - require("jscex-builderbase").init(); // 引入并初始化Jscex解析器模块 - -不过一般说来,在Node.js环境中我们无需手动引入并初始化该模块,在定义其他模块时它可以被Jscex核心组件内置包管理器自动加载,具体内容请参考[核心组件](../core/)相关内容。 - -### 浏览器 - -如果您要在浏览器里使用Jscex构造器基础,则需要在页面中引入jscex-builderbase-x.y.z.js文件: - - - - - - - -至此,Jscex根对象已经包括了使用构造器基础模块所需的所有成员。该做法也适用于各类**没有**实现CommonJS等包加载规范的JavaScript运行环境。 - -### 其他环境 - -Jscex自动支持一些其他的包加载规范。假如当前JavaScript运行环境实现了这些规范,则Jscex会自动采用这些规范。详细信息请参考“[包引入](../importing.html)”相关内容。 \ No newline at end of file diff --git a/doc/src/documents/zh-cn/manuals/core/index.html.md b/doc/src/documents/zh-cn/manuals/core/index.html.md deleted file mode 100644 index ae0276f..0000000 --- a/doc/src/documents/zh-cn/manuals/core/index.html.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -layout: manual-zh-cn -title: 核心组件 ---- - -Jscex核心组件提供了Jscex开发中所需要的辅助方法、日志记录、模块定义等基础功能。在使用其他任何Jscex模块之前,都需要首先加载该核心组件。 - -## 引入Jscex核心组件 - -引入核心组件后将会得到一个Jscex根对象,该对象**必须**赋给上下文中名为`Jscex`的变量。 - -### Node.js - -如果您使用的是Node.js,可以直接使用[Node Package Manager](http://npmjs.org/)(即npm命令)安装最新的jscex包: - - npm install jscex - -然后便可以在脚本中引入该组件: - - var Jscex = require("jscex"); // 一定要使用Jscex作为变量名 - -### 浏览器 - -如果您要在浏览器里使用Jscex异步增强模块,则需要在页面中引入jscex-x.y.z.js文件: - - - -此时异步增强模块会自动为浏览器根对象(即window对象)添加一个名为Jscex的对象。该做法也适用于各类**没有**实现CommonJS等包加载规范的JavaScript运行环境。 - -### 其他环境 - -Jscex自动支持一些其他的包加载规范。假如当前JavaScript运行环境实现了这些规范,则Jscex会自动采用这些规范。详细信息请参考“[包引入](../importing.html)”相关内容。 \ No newline at end of file diff --git a/doc/src/documents/zh-cn/manuals/importing.html.md b/doc/src/documents/zh-cn/manuals/importing.html.md deleted file mode 100644 index 84426b3..0000000 --- a/doc/src/documents/zh-cn/manuals/importing.html.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -layout: manual-zh-cn -title: 包引入 ---- - -Jscex使用高度模块化设计,模块之间互有依赖,并且在开发和生产环境中对模块的需求可能也各不相同。同时,Jscex也直接支持一些常见的包加载环境,例如[CommonJS](http://wiki.commonjs.org/wiki/Modules/1.1.1)及[AMD](http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition)环境,开发人员在使用时无需自行封装。 - -## 依赖 - -Jscex模块之间会有依赖,这些依赖信息会在模块定义时注册给[核心组件]。因此,在引入其他模块之前,必须首先加载[核心组件](./core/)。然后,再根据依赖种类的不同以及各自的需要,选择性地加载不同的模块。 - -### 动态依赖 - -Jscex中的“动态依赖”,表示那些**不会**在模块初始化过程中自动加载的依赖。假设模块A和模块B有动态依赖,则意味着在初始化模块A之前需要手动初始化模块B。动态依赖的目的,主要是为了显式地强调被依赖模块的独立性,例如[异步增强模块](./async/powerpack.html)动态依赖于[异步模块](./async/),便强调了异步模块在实际开发过程中一般是可以被单独使用的。 - -### 静态依赖 - -Jscex中的“静态依赖”,表示那些会在模块初始化过程中**自动加载**的依赖。假设模块A对模块B有静态依赖,则意味着初始化模块A时,Jscex会自动加载并初始化模块B。例如,[异步模块](./async/)静态依赖于[构造器基础模块](./builderbase/),则意味着在使用异步模块时,我们只需要手动初始化异步模块即可,而构造器基础模块会被加载进来。 - -值得注意的是,这种“自动加载”需要一个包加载环境。在没有实现如CommonJS等包加载机制的JavaScript执行环境而言,我们仍需手动地加载Jscex各个模块文件。 - -## 浏览器 - -在浏览器环境里使用Jscex其实最为清晰明了,因为我们无论如何必须手动加载所有的Jscex模块文件,完全不用区分“静态依赖”或是“动态依赖”。例如: - - - - - - - - - -可见,即便异步模块静态依赖于构造器基础模块,但我们仍需手动引入构造器基础模块的JavaScript文件。当引入所有文件之后,浏览器的根对象(即window对象)上将会出现一个`Jscex`变量,它便是所有Jscex功能的根对象。这种方法也适合没有实现任何包加载机制的普通JavaScript执行环境。 - -## Node.js - -由于Node.js实现了CommonJS包加载机制,因此我们只需使用[Node Package Manager](http://npmjs.org/)(即npm命令)安装必要的包即可,例如: - - $ npm install jscex jscex-jit - ... - jscex@x.y.z ./node_modules/jscex - jscex-jit@x.y.z ./node_modules/jscex-jit - └── jscex-parser@x.y.z - -由于jscex-jit模块对jscex-parser有着静态依赖,因此我们无需手动安装jscex-parser模块,再使用时也是如此: - - var Jscex = require("jscex"); // 核心组件 - require("jscex-jit").init(); // 加载并初始化jscex-jit模块 - -在使用任何模块之前,我们都必须先初始化[核心组件](./core/),并将其赋值给`Jscex`变量。由于jscex-jit对jscex-parser有静态依赖,我们只需加载并初始化jscex-jit即可,无需手动加载jscex-parser模块。 - -而对于动态依赖的情况,我们则都必须手动进行模块的安装、加载及初始化工作,例如: - - $ npm install jscex jscex-async jscex-async-powerpack - ... - jscex@x.y.z ./node_modules/jscex - jscex-async@x.y.z ./node_modules/jscex-async - └── jscex-builderbase@x.y.z - jscex-async-powerpack@x.y.z ./node_modules/jscex-async-powerpack - -在使用时: - - var Jscex = require("jscex"); // 核心组件 - require("jscex-async").init(); // 异步模块 - require("jscex-async-powerpack").init(); // 异步增强模块 - -假如我们一不小心遗漏了某些模块,则核心组件将会提示所需模块的名称和版本号,我们只需简单地查漏补缺即可。 \ No newline at end of file diff --git a/doc/src/documents/zh-cn/manuals/index.html.md b/doc/src/documents/zh-cn/manuals/index.html.md deleted file mode 100644 index 17c6321..0000000 --- a/doc/src/documents/zh-cn/manuals/index.html.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -layout: manual-zh-cn -title: 首页 ---- - -## 模块 - -Jscex以模块化形式分发,目前主要有以下几个模块: - -* [解析器模块](./parser/) -* [JIT编译器模块](./jit/) -* [构造器基础模块](builderbase/) -* [异步模块](./async/) - * [异步增强模块](./async/powerpack.html) - -在使用任何Jscex模块之前,我们必须引入[核心组件](./core/),然后再[加载所需模块](./importing.html)。 - -## 工具 - -在开发Jscex过程中还会用到一些工具: - -* [预编译器](./aot/)。 \ No newline at end of file diff --git a/doc/src/documents/zh-cn/manuals/jit/index.html.md b/doc/src/documents/zh-cn/manuals/jit/index.html.md deleted file mode 100644 index 35fa698..0000000 --- a/doc/src/documents/zh-cn/manuals/jit/index.html.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -layout: manual-zh-cn -title: JIT编译器模块 ---- - -## 简介 - -Jscex的JIT编译器提供了使用Jscex过程中所需的代码变换能力,能将一段带有绑定标志的普通JavaScript方法变为Monad形式,这便是Jscex最为核心的功能。 - -### 依赖 - -* 动态依赖:无 -* 静态依赖:[解析器模块](../parser/) - -## 引入Jscex编译器模块 - -首先我们必须[引入核心组件](../core/),之后再基于这个对象初始化JIT编译器模块。 - -### Node.js - -如果您使用的是Node.js,可以直接使用[Node Package Manager](http://npmjs.org/)(即npm命令)安装最新的jscex-jit包: - - npm install jscex-jit - -然后便可以在脚本中引入该组件: - - var Jscex = require("jscex"); // 引入核心组件 - require("jscex-jit").init(); // 引入并初始化Jscex编译器模块 - -### 浏览器 - -在浏览器环境中使用JIT编译器模块,您同样需要引入[核心组件](../core/)。此时在浏览器根(即window对象)上会出现一个Jscex对象,之后再依次引入[解析器](../parser/)及JIT编译器的jscex-jit-x.y.z.js文件即可: - - - - - - - - - -至此,Jscex根对象已经包括了使用JIT编译器所需的所有成员。这种方式也适合各类**没有**实现CommonJS等包加载规范的JavaScript运行环境。 - -### 其他环境 - -Jscex自动支持一些其他的包加载规范。假如当前JavaScript运行环境实现了这些规范,则Jscex会自动采用这些规范。详细信息请参考“[包引入](../importing.html)”相关内容。 \ No newline at end of file diff --git a/doc/src/documents/zh-cn/manuals/parser/index.html.md b/doc/src/documents/zh-cn/manuals/parser/index.html.md deleted file mode 100644 index f77bc2b..0000000 --- a/doc/src/documents/zh-cn/manuals/parser/index.html.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -layout: manual-zh-cn -title: 解析器模块 ---- - -## 简介 - -Jscex解析器模块提供了Jscex编译器(如[JIT编译器](../jit/)和[预编译器](../aot/))所需要的JavaScript语法解析功能,目前使用的是UglifyJS的语法解析器。该模块一般情况下只在开发环境中里使用。 - -### 依赖 - -* 动态依赖:无 -* 静态依赖:无 - -## 引入Jscex解析器模块 - -首先我们必须[引入核心组件](../core/),之后再基于这个对象初始化解析器模块。 - -### Node.js - -如果您使用的是Node.js,可以直接使用[Node Package Manager](http://npmjs.org/)(即npm命令)安装最新的jscex-parser包: - - npm install jscex-parser - -然后便可以在脚本中引入该组件: - - var Jscex = require("jscex"); // 引入核心组件 - require("jscex-parser").init(); // 引入并初始化Jscex解析器模块 - -不过一般说来,在Node.js环境中我们无需手动引入并初始化该模块,在定义其他模块时它可以被Jscex核心组件内置包管理器自动加载,具体内容请参考[核心组件](../core/)相关内容。 - -### 浏览器 - -如果您要在浏览器里使用Jscex解析器模块,则需要在页面中引入jscex-parser-x.y.z.js文件: - - - - - - - -至此,Jscex根对象已经包括了使用解析器模块所需的所有成员。该做法也适用于各类**没有**实现CommonJS等包加载规范的JavaScript运行环境。 - -### 其他环境 - -Jscex自动支持一些其他的包加载规范。假如当前JavaScript运行环境实现了这些规范,则Jscex会自动采用这些规范。详细信息请参考“[包引入](../importing.html)”相关内容。 \ No newline at end of file diff --git a/doc/src/files/scripts/highlight.pack.js b/doc/src/files/scripts/highlight.pack.js deleted file mode 100644 index 5a7b070..0000000 --- a/doc/src/files/scripts/highlight.pack.js +++ /dev/null @@ -1 +0,0 @@ -var hljs=new function(){function m(p){return p.replace(/&/gm,"&").replace(/"}while(y.length||z.length){var v=u().splice(0,1)[0];w+=m(x.substr(r,v.offset-r));r=v.offset;if(v.event=="start"){w+=s(v.node);t.push(v.node)}else{if(v.event=="stop"){var p,q=t.length;do{q--;p=t[q];w+=("")}while(p!=v.node);t.splice(q,1);while(q'+L[0]+""}else{N+=L[0]}P=O.lR.lastIndex;L=O.lR.exec(M)}return N+M.substr(P,M.length-P)}function K(r,M){if(M.sL&&d[M.sL]){var L=e(M.sL,r);t+=L.keyword_count;return L.value}else{return F(r,M)}}function I(M,r){var L=M.cN?'':"";if(M.rB){q+=L;M.buffer=""}else{if(M.eB){q+=m(r)+L;M.buffer=""}else{q+=L;M.buffer=r}}C.push(M);B+=M.r}function E(O,L,Q){var R=C[C.length-1];if(Q){q+=K(R.buffer+O,R);return false}var M=z(L,R);if(M){q+=K(R.buffer+O,R);I(M,L);return M.rB}var r=w(C.length-1,L);if(r){var N=R.cN?"":"";if(R.rE){q+=K(R.buffer+O,R)+N}else{if(R.eE){q+=K(R.buffer+O,R)+N+m(L)}else{q+=K(R.buffer+O+L,R)+N}}while(r>1){N=C[C.length-2].cN?"":"";q+=N;r--;C.length--}var P=C[C.length-1];C.length--;C[C.length-1].buffer="";if(P.starts){I(P.starts,"")}return R.rE}if(x(L,R)){throw"Illegal"}}var H=d[J];var C=[H.dM];var B=0;var t=0;var q="";try{var y,v=0;H.dM.buffer="";do{y=s(D,v);var u=E(y[0],y[1],y[2]);v+=y[0].length;if(!u){v+=y[1].length}}while(!y[2]);if(C.length>1){throw"Illegal"}return{r:B,keyword_count:t,value:q}}catch(G){if(G=="Illegal"){return{r:0,keyword_count:0,value:m(D)}}else{throw G}}}function f(t){var r={keyword_count:0,r:0,value:m(t)};var q=r;for(var p in d){if(!d.hasOwnProperty(p)){continue}var s=e(p,t);s.language=p;if(s.keyword_count+s.r>q.keyword_count+q.r){q=s}if(s.keyword_count+s.r>r.keyword_count+r.r){q=r;r=s}}if(q.language){r.second_best=q}return r}function h(r,q,p){if(q){r=r.replace(/^((<[^>]+>|\t)+)/gm,function(t,w,v,u){return w.replace(/\t/g,q)})}if(p){r=r.replace(/\n/g,"
")}return r}function o(u,x,q){var y=g(u,q);var s=a(u);var w,r;if(s=="no-highlight"){return}if(s){w=e(s,y)}else{w=f(y);s=w.language}var p=b(u);if(p.length){r=document.createElement("pre");r.innerHTML=w.value;w.value=l(p,b(r),y)}w.value=h(w.value,x,q);var t=u.className;if(!t.match("(\\s|^)(language-)?"+s+"(\\s|$)")){t=t?(t+" "+s):s}if(/MSIE [678]/.test(navigator.userAgent)&&u.tagName=="CODE"&&u.parentNode.tagName=="PRE"){r=u.parentNode;var v=document.createElement("div");v.innerHTML="
"+w.value+"
";u=v.firstChild.firstChild;v.firstChild.cN=r.cN;r.parentNode.replaceChild(v.firstChild,r)}else{u.innerHTML=w.value}u.className=t;u.result={language:s,kw:w.keyword_count,re:w.r};if(w.second_best){u.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function k(){if(k.called){return}k.called=true;var r=document.getElementsByTagName("pre");for(var p=0;p|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\.",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BINARY_NUMBER_MODE={cN:"number",b:this.BINARY_NUMBER_RE,r:0};this.inherit=function(p,s){var r={};for(var q in p){r[q]=p[q]}if(s){for(var q in s){r[q]=s[q]}}return r}}();hljs.LANGUAGES.javascript={dM:{k:{keyword:{"in":1,"if":1,"for":1,"while":1,"finally":1,"var":1,"new":1,"function":1,"do":1,"return":1,"void":1,"else":1,"break":1,"catch":1,"instanceof":1,"with":1,"throw":1,"case":1,"default":1,"try":1,"this":1,"switch":1,"continue":1,"typeof":1,"delete":1},literal:{"true":1,"false":1,"null":1}},c:[hljs.ASM,hljs.QSM,hljs.CLCM,hljs.CBLCLM,hljs.CNM,{b:"("+hljs.RSR+"|case|return|throw)\\s*",k:{"return":1,"throw":1,"case":1},c:[hljs.CLCM,hljs.CBLCLM,{cN:"regexp",b:"/",e:"/[gim]*",c:[{b:"\\\\/"}]}],r:0},{cN:"function",b:"\\bfunction\\b",e:"{",k:{"function":1},c:[{cN:"title",b:"[A-Za-z$_][0-9A-Za-z$_]*"},{cN:"params",b:"\\(",e:"\\)",c:[hljs.ASM,hljs.QSM,hljs.CLCM,hljs.CBLCLM]}]}]}};hljs.LANGUAGES.xml=function(){var b="[A-Za-z0-9\\._:-]+";var a={eW:true,c:[{cN:"attribute",b:b,r:0},{b:'="',rB:true,e:'"',c:[{cN:"value",b:'"',eW:true}]},{b:"='",rB:true,e:"'",c:[{cN:"value",b:"'",eW:true}]},{b:"=",c:[{cN:"value",b:"[^\\s/>]+"}]}]};return{cI:true,dM:{c:[{cN:"pi",b:"<\\?",e:"\\?>",r:10},{cN:"doctype",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"|$)",e:">",k:{title:{style:1}},c:[a],starts:{cN:"css",e:"",rE:true,sL:"css"}},{cN:"tag",b:"|$)",e:">",k:{title:{script:1}},c:[a],starts:{cN:"javascript",e:"<\/script>",rE:true,sL:"javascript"}},{cN:"vbscript",b:"<%",e:"%>",sL:"vbscript"},{cN:"tag",b:"",c:[{cN:"title",b:"[^ />]+"},a]}]}}}(); \ No newline at end of file diff --git a/doc/src/files/scripts/jquery-1.7.2.min.js b/doc/src/files/scripts/jquery-1.7.2.min.js deleted file mode 100644 index 16ad06c..0000000 --- a/doc/src/files/scripts/jquery-1.7.2.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/*! jQuery v1.7.2 jquery.com | jquery.org/license */ -(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"":"")+""),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;e=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="
"+""+"
",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="
t
",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="
",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function( -a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&j.push({elem:this,matches:d.slice(e)});for(k=0;k0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f -.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(;d1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]===""&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file diff --git a/doc/src/files/scripts/jscex-async.bundle.min.js b/doc/src/files/scripts/jscex-async.bundle.min.js deleted file mode 100644 index 3f1b327..0000000 --- a/doc/src/files/scripts/jscex-async.bundle.min.js +++ /dev/null @@ -1,28 +0,0 @@ -(function(){var b={ALL:0,TRACE:1,DEBUG:2,INFO:3,WARN:4,ERROR:5,OFF:100},c=function(){this.level=b.DEBUG};c.prototype={log:function(a,b){if(this.level<=a)try{console.log(b)}catch(d){}},trace:function(a){this.log(b.TRACE,a)},debug:function(a){this.log(b.DEBUG,a)},info:function(a){this.log(b.INFO,a)},warn:function(a){this.log(b.WARN,a)},error:function(a){this.log(b.ERROR,a)}};var d=function(a){a.Logging={Logger:c,Level:b};a.logger=new c;a.modules={};a.binders={};a.builders={}},e=typeof define==="function"&& -!define.amd,f=typeof require==="function"&&typeof define==="function"&&define.amd;typeof require==="function"&&typeof module!=="undefined"&&module.exports?d(module.exports):e?define("jscex",function(a,b,c){d(c.exports)}):f?define("jscex",function(){var a={};d(a);return a}):(typeof Jscex=="undefined"&&(Jscex={}),d(Jscex))})(); - -(function(){var j=function(){};j.prototype={For:function(b,d,a){return{next:function(c,e){var g=function(f){try{d&&!f&&d.call(c),!b||b.call(c)?a.next(c,function(c,a){if(c=="normal"||c=="continue")g(!1);else if(c=="throw"||c=="return")e(c,a);else if(c=="break")e("normal");else throw Error('Invalid type for "Loop": '+c);}):e("normal")}catch(h){e("throw",h)}};g(!0)}}},ForIn:function(b,d){return{next:function(a,c){var e=[],g;for(g in b)e.push(g);var f=function(b){try{b=0&&this._handlers.splice(b,1))},cancel:function(){if(!this.isCancellationRequested){this.isCancellationRequested=!0;var b=this._handlers;delete this._handlers;for(var e=0;e=0&&c.splice(l,1);return this}};d.create=function(a){return new d(a)};d.isTask=m;var h=function(){};h.prototype={Start:function(a,e){return d.create(function(c){e.next(a,function(a,b){if(a=="normal"||a=="return")c.complete("success",b);else if(a=="throw")c.complete("failure",b);else throw Error("Unsupported type: "+a);})})},Bind:function(a, -e){return{next:function(c,d){var f=function(){if(this.error)d("throw",this.error);else{var a;try{a=e.call(c,this.result)}catch(b){d("throw",b);return}a.next(c,d)}};a.status=="ready"?(a.addEventListener("complete",f),a.start()):a.status=="running"?a.addEventListener("complete",f):f(a)}}}};for(var g in a.BuilderBase.prototype)h.prototype[g]=a.BuilderBase.prototype[g];if(!a.Async)a.Async={};g=a.Async;g.CancellationToken=f;g.CanceledError=j;g.Task=d;g.AsyncBuilder=h;if(!a.builders)a.builders={};a.binders.async= -"$await";a.builders.async=new h;a.modules.async=!0}},n=typeof define==="function"&&!define.amd,o=typeof require==="function"&&typeof define==="function"&&define.amd;if(typeof require==="function"&&typeof module!=="undefined"&&module.exports)module.exports.init=function(a){if(!a.modules.builderbase){if(typeof __dirname==="string")try{require.paths.unshift(__dirname)}catch(f){try{module.paths.unshift(__dirname)}catch(d){}}require("jscex-builderbase").init(a)}i(a)};else if(n)define("jscex-async",["jscex-builderbase"], -function(a,f,d){d.exports.init=function(d){d.modules.builderbase||a("jscex-builderbase").init(d);i(d)}});else if(o)define("jscex-async",["jscex-builderbase"],function(a){return{init:function(f){f.modules.builderbase||a.init(f);i(f)}}});else{if(typeof Jscex==="undefined")throw Error('Missing the root object, please load "jscex" module first.');if(!Jscex.modules.builderbase)throw Error('Missing essential components, please initialize "builderbase" module first.');i(Jscex)}})(); - -(function(){var l=function(g){this.children=[];if(g)for(var b=0;b0?a.complete("failure",new l(c)):a.complete("success",f)},m=0;for(b in d)d.hasOwnProperty(b)&&(h=d[b],f=e[h],f.status=="running"&&(m++,f.addEventListener("complete",function(){--m==0&&g()})));m==0&&g()})};c.whenAny=function(){var e={};if(arguments.length==1){var a=arguments[0];c.isTask(a)?e[0]=a:e=a}else for(a=0;a").attr("href", "#" + name).text($(h).text()); - } - - var addToList = function () { - var name = getName(this); - if (!name) return; - - var level = getLevel(this); - if (level > currentLevel) { - var children = currentList.children(); - var lastItem = children[children.length - 1]; - - var newList = $("
    ").appendTo(lastItem); - currentList = newList; - } else if (level < currentLevel) { - currentList = currentList.parent().parent(); - } - - currentLevel = level; - - $("
  • ").append(getLink(this, name)).appendTo(currentList); - } - - $("#container > h2, #container > h3, #container > h4").each(addToList); -}); \ No newline at end of file diff --git a/doc/src/files/styles/highlight-vs.css b/doc/src/files/styles/highlight-vs.css deleted file mode 100644 index 3b9a33a..0000000 --- a/doc/src/files/styles/highlight-vs.css +++ /dev/null @@ -1,84 +0,0 @@ -/* - -Visual Studio-like style based on original C# coloring by Jason Diamond - -*/ -pre code { - display: block; padding: 0.5em; -} - -pre .comment, -pre .annotation, -pre .template_comment, -pre .diff .header, -pre .chunk, -pre .apache .cbracket { - color: rgb(0, 128, 0); -} - -pre .keyword, -pre .id, -pre .title, -pre .built_in, -pre .aggregate, -pre .smalltalk .class, -pre .winutils, -pre .bash .variable, -pre .tex .command { - color: rgb(0, 0, 255); -} - -pre .string, -pre .title, -pre .parent, -pre .tag .value, -pre .rules .value, -pre .rules .value .number, -pre .ruby .symbol, -pre .ruby .symbol .string, -pre .ruby .symbol .keyword, -pre .ruby .symbol .keymethods, -pre .instancevar, -pre .aggregate, -pre .template_tag, -pre .django .variable, -pre .addition, -pre .flow, -pre .stream, -pre .apache .tag, -pre .date, -pre .tex .formula { - color: rgb(163, 21, 21); -} - -pre .ruby .string, -pre .decorator, -pre .filter .argument, -pre .localvars, -pre .array, -pre .attr_selector, -pre .pseudo, -pre .pi, -pre .doctype, -pre .deletion, -pre .envvar, -pre .shebang, -pre .preprocessor, -pre .userType, -pre .apache .sqbracket, -pre .nginx .built_in, -pre .tex .special, -pre .input_number { - color: rgb(43, 145, 175); -} - -pre .phpdoc, -pre .javadoc, -pre .xmlDocTag { - color: rgb(128, 128, 128); -} - -pre .vhdl .type { font-weight: bold; } -pre .vhdl .string { color: #666666; } -pre .vhdl .literal { color: rgb(163, 21, 21); } - diff --git a/doc/src/files/styles/images/blacktocat.png b/doc/src/files/styles/images/blacktocat.png deleted file mode 100644 index 6e264fe57a2e35a2855405ac7d4102c3f6ddcdae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1428 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1|+Qw)-3{3k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xa^B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxKsVXI%s|1+P|wiV z#N6CmN5ROz&_Lh7NZ-&%*U;R`*vQJjKmiJrfVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8EkR}&8R-I5=oVMzl_XZ^<`pZ$OmImpPAEg{v+u2}(t{7puX=A(aKG z`a!A1`K3k4z=%sz23b{L-^Aq1JP;qO z-q+X4Gq1QLF)umQ)5TT^Xo6m5W{Q=eg`=5?o13Glvx}*rp{t>#shg3DvyriZv5}jZ ztD`wguSMv>2~2MaLa!4}y`ZF!TL84#CABECEH%ZgC_h&L>}9J=EN(GzcCm0X zaRr%YgxxI=y(w7S0@dq`Q?EYIG5Vm0MT%&c5HR(CnDAr^T6f1avxRvmvnsN+?-j}Z~1)Zr#rqzrt`edmo44*B<0=C4>mrxHF6$p zVws~UocMfeI`gB8pYMLYTzA87`NOI2w2B*JM5L`^AkN4AFQu&S+6ULTPjv;vzl4& z-eaK_F|D4~l3hzBSF~icNT@MID=v+_X`vpuvf=8+S(|^vlRdHe0<)v-^wiVR3w=TQ)uFA9F z>vmq a.octothorpe, - dt:hover > a.octothorpe, - dd:hover > a.octothorpe, - h1:hover > a.octothorpe, - h2:hover > a.octothorpe, - h3:hover > a.octothorpe, - h4:hover > a.octothorpe, - h5:hover > a.octothorpe, - h6:hover > a.octothorpe { - opacity: 1; - } - -/* custom style by lifesinger */ -pre.wrong { - background: #fff6f6 !important; - border-color: #ffc6c6; -} - -blockquote { - margin: 1em 20px; - padding-left: 50px; - background: transparent url(quote.gif) no-repeat; -} diff --git a/doc/src/layouts/main-zh-cn.html.eco b/doc/src/layouts/main-zh-cn.html.eco deleted file mode 100644 index efda48a..0000000 --- a/doc/src/layouts/main-zh-cn.html.eco +++ /dev/null @@ -1,71 +0,0 @@ - - - - - -<%= @document.title %> - Jscex - 回归 JavaScript 的异步流程控制 - - - - - - - - - - - - - -
    -
    - - - - - -

    Jscex

    -

    回归 JavaScript 的异步流程控制

    - -
    - Download this project as a .zip file - Download this project as a tar.gz file -
    -
    -
    - - -
    -
    - <%- @content %> -
    -
    - - - - - - \ No newline at end of file diff --git a/doc/src/layouts/main.html.eco b/doc/src/layouts/main.html.eco deleted file mode 100644 index 4db6e25..0000000 --- a/doc/src/layouts/main.html.eco +++ /dev/null @@ -1,45 +0,0 @@ - - - - - -Jscex - Async flow control in JavaScript, with JavaScript - - - - - - -
    -
    - View on GitHub - -

    Jscex

    -

    Async flow control in JavaScript, with JavaScript

    - -
    - Download this project as a .zip file - Download this project as a tar.gz file -
    -
    -
    - - -
    -
    - <%- @content %> -
    -
    - - - - - - \ No newline at end of file diff --git a/doc/src/layouts/manual-zh-cn.html.eco b/doc/src/layouts/manual-zh-cn.html.eco deleted file mode 100644 index cc31852..0000000 --- a/doc/src/layouts/manual-zh-cn.html.eco +++ /dev/null @@ -1,50 +0,0 @@ - - - - - -Jscex 开发指南 - <%= @document.title %> - - - - - - - - - - - - -
    -

    Jscex 开发指南 - <%= @document.title %>

    - -
    -
    - -
    -

    索引

    - -
      - -
      -
      - - <%- @content %> - - - \ No newline at end of file