├── .gitignore
├── version.json
├── tools
├── jsmake
│ ├── js.jar
│ ├── jsmake.cmd
│ ├── jsmaked.cmd
│ ├── bootstrap.js
│ └── jsmake.js
├── jasmine
│ ├── jasmine_favicon.png
│ ├── MIT.LICENSE
│ ├── jasmine.css
│ ├── jasmine-html.js
│ └── jasmine.js
├── uglifyjs
│ ├── package.json
│ ├── uglify-js.js
│ └── lib
│ │ ├── squeeze-more.js
│ │ └── parse-js.js
└── jsmake.javascript.JavascriptUtils.js
├── LICENSE
├── src
├── jsonrpc.Observable.js
├── jsonrpc.CallStack.js
├── jsonrpc.DelayedTask.js
└── jsonrpc.JsonRpc.js
├── test
├── jsonrpc.ObservableTest.js
├── jsonrpc.CallStackTest.js
├── jsonrpc.DelayedTaskTest.js
└── jsonrpc.JsonRpcTest.js
├── SpecRunner.html
├── README.markdown
└── specrunner.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /build/
2 | /.idea/
3 | /*.iml
--------------------------------------------------------------------------------
/version.json:
--------------------------------------------------------------------------------
1 | {"major":0,"minor":1,"patch":8}
--------------------------------------------------------------------------------
/tools/jsmake/js.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gimmi/jsonrpcjs/HEAD/tools/jsmake/js.jar
--------------------------------------------------------------------------------
/tools/jasmine/jasmine_favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gimmi/jsonrpcjs/HEAD/tools/jasmine/jasmine_favicon.png
--------------------------------------------------------------------------------
/tools/jsmake/jsmake.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 | java -cp "%~dp0js.jar" org.mozilla.javascript.tools.shell.Main -modules "." "%~dp0bootstrap.js" "%~dp0jsmake.js" %*
--------------------------------------------------------------------------------
/tools/jsmake/jsmaked.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 | java -cp "%~dp0js.jar" org.mozilla.javascript.tools.debugger.Main -modules "." "%~dp0bootstrap.js" "%~dp0jsmake.js" %*
3 |
--------------------------------------------------------------------------------
/tools/jsmake/bootstrap.js:
--------------------------------------------------------------------------------
1 | (function (global, args) {
2 | load(args.shift());
3 | var main = new jsmake.Main();
4 | main.init(global);
5 | load('build.js');
6 | main.runTask(args.shift() || 'default', args);
7 | }(this, arguments));
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2012 Gian Marco Gherardi
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
--------------------------------------------------------------------------------
/src/jsonrpc.Observable.js:
--------------------------------------------------------------------------------
1 | jsonrpc.Observable = function () {
2 | this._listeners = [];
3 | };
4 |
5 | jsonrpc.Observable.prototype = {
6 | bind: function (fn, scope) {
7 | var token = { fn: fn, scope: scope || this };
8 | this._listeners.push(token);
9 | return token;
10 | },
11 |
12 | unbind: function (token) {
13 | var idx = this._listeners.indexOf(token);
14 | if (idx !== -1) {
15 | this._listeners.splice(idx, 1);
16 | }
17 | },
18 |
19 | trigger: function () {
20 | var i;
21 | for (i = 0; i < this._listeners.length; i += 1) {
22 | this._listeners[i].fn.apply(this._listeners[i].scope, arguments);
23 | }
24 | }
25 | };
--------------------------------------------------------------------------------
/test/jsonrpc.ObservableTest.js:
--------------------------------------------------------------------------------
1 | describe('jsonrpc.Observable', function () {
2 | var target;
3 |
4 | beforeEach(function () {
5 | target = new jsonrpc.Observable();
6 | });
7 |
8 | it('should trigger events', function () {
9 | var scope = {},
10 | h1 = jasmine.createSpy(),
11 | s1 = {},
12 | h2 = jasmine.createSpy();
13 |
14 | target.bind(h1, s1);
15 | target.unbind(target.bind(h2));
16 |
17 | target.trigger(1, 2, 3);
18 |
19 | expect(h1).toHaveBeenCalledWith(1, 2, 3);
20 | expect(h1.callCount).toEqual(1);
21 | expect(h1.mostRecentCall.object).toEqual(s1);
22 | expect(h2).not.toHaveBeenCalled();
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/src/jsonrpc.CallStack.js:
--------------------------------------------------------------------------------
1 | jsonrpc.CallStack = function (enterFn, enterScope, exitFn, exitScope) {
2 | this._counter = 0;
3 | this._enterFn = enterFn;
4 | this._exitFn = exitFn;
5 | this._enterScope = enterScope;
6 | this._exitScope = exitScope;
7 | };
8 |
9 | jsonrpc.CallStack.prototype = {
10 | enter: function () {
11 | this._counter = (this._counter < 0 ? 1 : this._counter + 1);
12 | if (this._counter === 1) {
13 | this._enterFn.apply(this._enterScope, arguments);
14 | }
15 | },
16 |
17 | exit: function (fn) {
18 | this._counter -= 1;
19 | if (this._counter === 0) {
20 | this._exitFn.apply(this._exitScope, arguments);
21 | }
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/tools/uglifyjs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name" : "uglify-js",
3 |
4 | "description" : "JavaScript parser and compressor/beautifier toolkit",
5 |
6 | "author" : {
7 | "name" : "Mihai Bazon",
8 | "email" : "mihai.bazon@gmail.com",
9 | "url" : "http://mihai.bazon.net/blog"
10 | },
11 |
12 | "version" : "1.2.3",
13 |
14 | "main" : "./uglify-js.js",
15 |
16 | "bin" : {
17 | "uglifyjs" : "./bin/uglifyjs"
18 | },
19 |
20 | "repository": {
21 | "type": "git",
22 | "url": "git@github.com:mishoo/UglifyJS.git"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/tools/uglifyjs/uglify-js.js:
--------------------------------------------------------------------------------
1 | //convienence function(src, [options]);
2 | function uglify(orig_code, options){
3 | options || (options = {});
4 | var jsp = uglify.parser;
5 | var pro = uglify.uglify;
6 |
7 | var ast = jsp.parse(orig_code, options.strict_semicolons); // parse code and get the initial AST
8 | ast = pro.ast_mangle(ast, options.mangle_options); // get a new AST with mangled names
9 | ast = pro.ast_squeeze(ast, options.squeeze_options); // get an AST with compression optimizations
10 | var final_code = pro.gen_code(ast, options.gen_options); // compressed code here
11 | return final_code;
12 | };
13 |
14 | uglify.parser = require("./lib/parse-js");
15 | uglify.uglify = require("./lib/process");
16 |
17 | module.exports = uglify
--------------------------------------------------------------------------------
/src/jsonrpc.DelayedTask.js:
--------------------------------------------------------------------------------
1 | jsonrpc.DelayedTask = function (fn, scope, args) {
2 | this._fn = fn || function () {};
3 | this._scope = scope || undefined;
4 | this._args = args || [];
5 | this._id = null;
6 | };
7 |
8 | jsonrpc.DelayedTask.prototype = {
9 | delay: function (delay, fn, scope, args) {
10 | var me = this;
11 |
12 | this._fn = fn || this._fn;
13 | this._scope = scope || this._scope;
14 | this._args = args || this._args;
15 | this.cancel();
16 | this._id = window.setInterval(function () {
17 | window.clearInterval(me._id);
18 | me._id = null;
19 | me._fn.apply(me._scope, me._args);
20 | }, delay);
21 | },
22 |
23 | cancel: function () {
24 | if (this._id) {
25 | window.clearInterval(this._id);
26 | this._id = null;
27 | }
28 | }
29 | };
--------------------------------------------------------------------------------
/tools/jsmake.javascript.JavascriptUtils.js:
--------------------------------------------------------------------------------
1 | jsmake.javascript = {};
2 |
3 | jsmake.javascript.JavascriptUtils = function () {
4 | };
5 | jsmake.javascript.JavascriptUtils.prototype = {
6 | jslint: function (files, options, globals) {
7 | if (!JSLINT) {
8 | throw 'JSLint library must be loaded. Download JSLint and include it in build script';
9 | }
10 | globals = '/*global ' + jsmake.Utils.map(globals, function (global, key) {
11 | return key + ': ' + !!global;
12 | }, this).join(', ') + ' */\n';
13 | var errors = [];
14 | jsmake.Utils.each(files, function (file) {
15 | var content = globals + fs.readFile(file);
16 | JSLINT(content, options);
17 | jsmake.Utils.each(JSLINT.errors, function (error) {
18 | if (error) {
19 | errors.push(file + ':' + (error.line - 1) + ',' + error.character + ': ' + error.reason);
20 | }
21 | });
22 | });
23 |
24 | if (errors.length) {
25 | sys.log('JSLint found ' + errors.length + ' errors');
26 | sys.log(errors.join('\n'));
27 | throw 'Fatal error, see previous messages.';
28 | }
29 | }
30 | };
--------------------------------------------------------------------------------
/tools/jasmine/MIT.LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2008-2011 Pivotal Labs
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/SpecRunner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Jasmine Test Runner
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/test/jsonrpc.CallStackTest.js:
--------------------------------------------------------------------------------
1 | describe('jsonrpc.CallStack', function () {
2 | var target, enterFn, exitFn, scope;
3 |
4 | beforeEach(function () {
5 | enterFn = jasmine.createSpy();
6 | exitFn = jasmine.createSpy();
7 | scope = {};
8 | target = new jsonrpc.CallStack(enterFn, exitFn, scope);
9 | });
10 |
11 | it('should keep scope and parameters', function () {
12 | var enterScope = {},
13 | exitScope = {},
14 | enterFn = jasmine.createSpy(),
15 | exitFn = jasmine.createSpy(),
16 | target = new jsonrpc.CallStack(enterFn, enterScope, exitFn, exitScope);
17 |
18 | target.enter(1, 2, 3);
19 | target.exit(4, 5, 6);
20 |
21 | expect(enterFn).toHaveBeenCalledWith(1, 2, 3);
22 | expect(enterFn.mostRecentCall.object).toBe(enterScope);
23 |
24 | expect(exitFn).toHaveBeenCalledWith(4, 5, 6);
25 | expect(exitFn.mostRecentCall.object).toBe(exitScope);
26 | });
27 |
28 | it('should simmetrically invoke enter and exit fn', function () {
29 | var count = 0,
30 | enter = [],
31 | exit = [],
32 | target;
33 | target = new jsonrpc.CallStack(function () { count += 1; enter.push(count); }, null, function () { count += 1; exit.push(count); }, null);
34 |
35 | target.exit();
36 | target.enter();
37 | target.enter();
38 | target.exit();
39 | target.exit();
40 | target.exit();
41 | target.enter();
42 |
43 | expect(enter).toEqual([1, 3]);
44 | expect(exit).toEqual([2]);
45 | expect(count).toEqual(3);
46 | });
47 | });
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | JSON-RPC 2.0 client library for Javascript
2 | ------------------------------------------
3 |
4 | Blah blah blah
5 |
6 | Sample code
7 | -----------
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/test/jsonrpc.DelayedTaskTest.js:
--------------------------------------------------------------------------------
1 | describe('jsonrpc.DelayedTask', function () {
2 | it('should invoke after delay', function () {
3 | var fn = jasmine.createSpy(),
4 | scope = {},
5 | target = new jsonrpc.DelayedTask(fn, scope, [ 1, 2, 3 ]);
6 |
7 | runs(function () {
8 | target.delay(100);
9 | expect(fn).not.toHaveBeenCalled();
10 | });
11 |
12 | waits(110);
13 |
14 | runs(function () {
15 | expect(fn).toHaveBeenCalledWith(1, 2, 3);
16 | expect(fn.mostRecentCall.object).toBe(scope);
17 | });
18 | });
19 |
20 | it('should cancel call', function () {
21 | var fn = jasmine.createSpy(),
22 | target = new jsonrpc.DelayedTask(fn);
23 |
24 | runs(function () {
25 | target.delay(100);
26 | });
27 |
28 | waits(50);
29 |
30 | runs(function () {
31 | target.cancel();
32 | });
33 |
34 | waits(60);
35 |
36 | runs(function () {
37 | expect(fn).not.toHaveBeenCalled();
38 | });
39 | });
40 |
41 | it('should call the last specified function', function () {
42 | var fn = jasmine.createSpy(),
43 | target = new jsonrpc.DelayedTask(fn),
44 | newFn = jasmine.createSpy(),
45 | newScope = {};
46 |
47 | runs(function () {
48 | target.delay(30, newFn, newScope, [1, 2, 3]);
49 | });
50 |
51 | waits(50);
52 |
53 | runs(function () {
54 | expect(fn).not.toHaveBeenCalled();
55 | expect(newFn).toHaveBeenCalledWith(1, 2, 3);
56 | expect(newFn.mostRecentCall.object).toBe(newScope);
57 | });
58 | });
59 |
60 | it('should restart timer on each delay call', function () {
61 | var stop = null,
62 | fn = function () { stop = new Date().getTime(); },
63 | target = new jsonrpc.DelayedTask(fn),
64 | start;
65 |
66 | runs(function () {
67 | start = new Date().getTime();
68 | target.delay(50);
69 | });
70 |
71 | waits(30);
72 |
73 | runs(function () {
74 | target.delay(50);
75 | });
76 |
77 | waits(30);
78 |
79 | runs(function () {
80 | target.delay(50);
81 | });
82 |
83 | waitsFor(function () {
84 | return !!stop;
85 | });
86 |
87 | runs(function () {
88 | expect(stop - start).toBeGreaterThan(100);
89 | expect(stop - start).toBeLessThan(120);
90 | });
91 | });
92 | });
93 |
--------------------------------------------------------------------------------
/specrunner.js:
--------------------------------------------------------------------------------
1 | // From http://stackoverflow.com/questions/2261705/how-to-run-a-javascript-function-asynchronously-without-using-settimeout/5767884#5767884
2 | (function (global) {
3 | var timer = new java.util.Timer();
4 | var counter = 1;
5 | var ids = {};
6 |
7 | global.setTimeout = function (fn, delay) {
8 | var id = counter++;
9 | ids[id] = new JavaAdapter(java.util.TimerTask, { run: fn });
10 | timer.schedule(ids[id], delay);
11 | return id;
12 | };
13 |
14 | global.clearTimeout = function (id) {
15 | ids[id].cancel();
16 | timer.purge();
17 | delete ids[id];
18 | };
19 |
20 | global.setInterval = function (fn, delay) {
21 | var id = counter++;
22 | ids[id] = new JavaAdapter(java.util.TimerTask, { run: fn });
23 | timer.schedule(ids[id], delay, delay);
24 | return id;
25 | };
26 |
27 | global.clearInterval = global.clearTimeout;
28 | })(this);
29 |
30 | this.window = this; // jasmine uses 'window' global variable to detect commonjs environment. We want Jasmine to behave like when run in browser
31 | load('tools/jasmine/jasmine.js');
32 |
33 | //var jasmine = require('tools/jasmine/jasmine').jasmine;
34 |
35 | (function (args) {
36 | for (var i = 0; i < args.length; i += 1) {
37 | load(args[i]);
38 | }
39 | }(arguments));
40 |
41 | jasmine.RhinoReporter = function() {
42 | };
43 | jasmine.RhinoReporter.prototype = {
44 | reportRunnerStarting: function(runner) {
45 | this._results = '';
46 | },
47 | reportRunnerResults: function(runner) {
48 | var failedCount = runner.results().failedCount;
49 |
50 | this.log(this._results);
51 | this.log("Passed: " + runner.results().passedCount);
52 | this.log("Failed: " + failedCount);
53 | this.log("Total : " + runner.results().totalCount);
54 |
55 | java.lang.System.exit(failedCount);
56 | },
57 | reportSuiteResults: function(suite) {
58 | },
59 | reportSpecStarting: function(spec) {
60 | },
61 | reportSpecResults: function(spec) {
62 | var i, specResults = spec.results().getItems();
63 |
64 | if (spec.results().passed()) {
65 | java.lang.System.out.print(".");
66 | } else {
67 | java.lang.System.out.print("F");
68 | this._results += "FAILED\n";
69 | this._results += "Suite: " + spec.suite.description + "\n";
70 | this._results += "Spec : " + spec.description + "\n";
71 | for (i = 0; i < specResults.length; i += 1) {
72 | this._results += specResults[i].trace + "\n";
73 | }
74 | }
75 | },
76 | log: function(str) {
77 | print(str);
78 | }
79 | };
80 | jasmine.getEnv().addReporter(new jasmine.RhinoReporter());
81 | jasmine.getEnv().execute();
--------------------------------------------------------------------------------
/tools/jasmine/jasmine.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif;
3 | }
4 |
5 |
6 | .jasmine_reporter a:visited, .jasmine_reporter a {
7 | color: #303;
8 | }
9 |
10 | .jasmine_reporter a:hover, .jasmine_reporter a:active {
11 | color: blue;
12 | }
13 |
14 | .run_spec {
15 | float:right;
16 | padding-right: 5px;
17 | font-size: .8em;
18 | text-decoration: none;
19 | }
20 |
21 | .jasmine_reporter {
22 | margin: 0 5px;
23 | }
24 |
25 | .banner {
26 | color: #303;
27 | background-color: #fef;
28 | padding: 5px;
29 | }
30 |
31 | .logo {
32 | float: left;
33 | font-size: 1.1em;
34 | padding-left: 5px;
35 | }
36 |
37 | .logo .version {
38 | font-size: .6em;
39 | padding-left: 1em;
40 | }
41 |
42 | .runner.running {
43 | background-color: yellow;
44 | }
45 |
46 |
47 | .options {
48 | text-align: right;
49 | font-size: .8em;
50 | }
51 |
52 |
53 |
54 |
55 | .suite {
56 | border: 1px outset gray;
57 | margin: 5px 0;
58 | padding-left: 1em;
59 | }
60 |
61 | .suite .suite {
62 | margin: 5px;
63 | }
64 |
65 | .suite.passed {
66 | background-color: #dfd;
67 | }
68 |
69 | .suite.failed {
70 | background-color: #fdd;
71 | }
72 |
73 | .spec {
74 | margin: 5px;
75 | padding-left: 1em;
76 | clear: both;
77 | }
78 |
79 | .spec.failed, .spec.passed, .spec.skipped {
80 | padding-bottom: 5px;
81 | border: 1px solid gray;
82 | }
83 |
84 | .spec.failed {
85 | background-color: #fbb;
86 | border-color: red;
87 | }
88 |
89 | .spec.passed {
90 | background-color: #bfb;
91 | border-color: green;
92 | }
93 |
94 | .spec.skipped {
95 | background-color: #bbb;
96 | }
97 |
98 | .messages {
99 | border-left: 1px dashed gray;
100 | padding-left: 1em;
101 | padding-right: 1em;
102 | }
103 |
104 | .passed {
105 | background-color: #cfc;
106 | display: none;
107 | }
108 |
109 | .failed {
110 | background-color: #fbb;
111 | }
112 |
113 | .skipped {
114 | color: #777;
115 | background-color: #eee;
116 | display: none;
117 | }
118 |
119 |
120 | /*.resultMessage {*/
121 | /*white-space: pre;*/
122 | /*}*/
123 |
124 | .resultMessage span.result {
125 | display: block;
126 | line-height: 2em;
127 | color: black;
128 | }
129 |
130 | .resultMessage .mismatch {
131 | color: black;
132 | }
133 |
134 | .stackTrace {
135 | white-space: pre;
136 | font-size: .8em;
137 | margin-left: 10px;
138 | max-height: 5em;
139 | overflow: auto;
140 | border: 1px inset red;
141 | padding: 1em;
142 | background: #eef;
143 | }
144 |
145 | .finished-at {
146 | padding-left: 1em;
147 | font-size: .6em;
148 | }
149 |
150 | .show-passed .passed,
151 | .show-skipped .skipped {
152 | display: block;
153 | }
154 |
155 |
156 | #jasmine_content {
157 | position:fixed;
158 | right: 100%;
159 | }
160 |
161 | .runner {
162 | border: 1px solid gray;
163 | display: block;
164 | margin: 5px 0;
165 | padding: 2px 0 2px 10px;
166 | }
167 |
--------------------------------------------------------------------------------
/tools/uglifyjs/lib/squeeze-more.js:
--------------------------------------------------------------------------------
1 | var jsp = require("./parse-js"),
2 | pro = require("./process"),
3 | slice = jsp.slice,
4 | member = jsp.member,
5 | curry = jsp.curry,
6 | MAP = pro.MAP,
7 | PRECEDENCE = jsp.PRECEDENCE,
8 | OPERATORS = jsp.OPERATORS;
9 |
10 | function ast_squeeze_more(ast) {
11 | var w = pro.ast_walker(), walk = w.walk, scope;
12 | function with_scope(s, cont) {
13 | var save = scope, ret;
14 | scope = s;
15 | ret = cont();
16 | scope = save;
17 | return ret;
18 | };
19 | function _lambda(name, args, body) {
20 | return [ this[0], name, args, with_scope(body.scope, curry(MAP, body, walk)) ];
21 | };
22 | return w.with_walkers({
23 | "toplevel": function(body) {
24 | return [ this[0], with_scope(this.scope, curry(MAP, body, walk)) ];
25 | },
26 | "function": _lambda,
27 | "defun": _lambda,
28 | "new": function(ctor, args) {
29 | if (ctor[0] == "name") {
30 | if (ctor[1] == "Array" && !scope.has("Array")) {
31 | if (args.length != 1) {
32 | return [ "array", args ];
33 | } else {
34 | return walk([ "call", [ "name", "Array" ], args ]);
35 | }
36 | } else if (ctor[1] == "Object" && !scope.has("Object")) {
37 | if (!args.length) {
38 | return [ "object", [] ];
39 | } else {
40 | return walk([ "call", [ "name", "Object" ], args ]);
41 | }
42 | } else if ((ctor[1] == "RegExp" || ctor[1] == "Function" || ctor[1] == "Error") && !scope.has(ctor[1])) {
43 | return walk([ "call", [ "name", ctor[1] ], args]);
44 | }
45 | }
46 | },
47 | "call": function(expr, args) {
48 | if (expr[0] == "dot" && expr[2] == "toString" && args.length == 0) {
49 | // foo.toString() ==> foo+""
50 | return [ "binary", "+", expr[1], [ "string", "" ]];
51 | }
52 | if (expr[0] == "name") {
53 | if (expr[1] == "Array" && args.length != 1 && !scope.has("Array")) {
54 | return [ "array", args ];
55 | }
56 | if (expr[1] == "Object" && !args.length && !scope.has("Object")) {
57 | return [ "object", [] ];
58 | }
59 | if (expr[1] == "String" && !scope.has("String")) {
60 | return [ "binary", "+", args[0], [ "string", "" ]];
61 | }
62 | }
63 | }
64 | }, function() {
65 | return walk(pro.ast_add_scope(ast));
66 | });
67 | };
68 |
69 | exports.ast_squeeze_more = ast_squeeze_more;
70 |
--------------------------------------------------------------------------------
/src/jsonrpc.JsonRpc.js:
--------------------------------------------------------------------------------
1 | jsonrpc.JsonRpc = function (url) {
2 | this._url = url;
3 | this.loading = new jsonrpc.Observable();
4 | this.loaded = new jsonrpc.Observable();
5 | this.unhandledFailure = new jsonrpc.Observable();
6 | this._loadingState = new jsonrpc.CallStack(this.loading.trigger, this.loading, this.loaded.trigger, this.loaded);
7 | this._requests = [];
8 | this._batchingMilliseconds = 10;
9 | this._delayedTask = new jsonrpc.DelayedTask();
10 | };
11 |
12 | jsonrpc.JsonRpc.prototype = {
13 | setBatchingMilliseconds: function (value) {
14 | this._batchingMilliseconds = value;
15 | },
16 |
17 | call: function () {
18 | var args = this._getParams.apply(this, arguments);
19 |
20 | this._loadingState.enter();
21 | this._requests.push(args);
22 |
23 | if (this._batchingMilliseconds) {
24 | this._delayedTask.delay(this._batchingMilliseconds, this._sendRequests, this);
25 | } else {
26 | this._sendRequests();
27 | }
28 | },
29 |
30 | _sendRequests: function () {
31 | var me = this,
32 | requests = this._requests,
33 | data = [],
34 | i;
35 |
36 | this._requests = [];
37 |
38 | for (i = 0; i < requests.length; i += 1) {
39 | requests[i].request.id = i;
40 | data.push(requests[i].request);
41 | }
42 |
43 | if (data.length === 1) {
44 | data = data[0];
45 | }
46 |
47 | me._doJsonPost(me._url, data, function (htmlSuccess, htmlResponse) {
48 | var responses;
49 | if (htmlSuccess) {
50 | responses = (me._isArray(htmlResponse) ? htmlResponse : [htmlResponse]);
51 | } else {
52 | responses = [];
53 | for (i = 0; i < requests.length; i += 1) {
54 | responses[i] = { id: i, error: { message: htmlResponse } };
55 | }
56 | }
57 | me._handleResponses(requests, responses);
58 | });
59 | },
60 |
61 | _handleResponses: function (requests, responses) {
62 | var i, response, request;
63 | for (i = 0; i < responses.length; i += 1) {
64 | response = responses[i];
65 | request = requests[response.id];
66 | this._handleResponse(request, response);
67 | }
68 | },
69 |
70 | _handleResponse: function (request, response) {
71 | var success = !response.error,
72 | ret = (success ? response.result : response.error.message);
73 |
74 | this._loadingState.exit();
75 |
76 | if (success) {
77 | request.success.call(request.scope, ret);
78 | } else {
79 | request.failure.call(request.scope, ret);
80 | }
81 | request.callback.call(request.scope, success, ret);
82 | },
83 |
84 | _getParams: function () {
85 | var me = this,
86 | args = Array.prototype.slice.call(arguments),
87 | ret = {
88 | request: {
89 | jsonrpc: '2.0',
90 | method: args.shift()
91 | }
92 | };
93 |
94 | ret.request.params = [];
95 | while (args.length > 1 && !this._isFunction(args[0])) {
96 | ret.request.params.push(args.shift());
97 | }
98 |
99 | if (this._isFunction(args[0])) {
100 | ret.success = args[0];
101 | ret.scope = args[1];
102 | } else {
103 | ret.success = args[0].success;
104 | ret.failure = args[0].failure;
105 | ret.callback = args[0].callback;
106 | ret.scope = args[0].scope;
107 | }
108 | ret.success = ret.success || function () { return; };
109 | ret.failure = ret.failure || function () { me.unhandledFailure.trigger.apply(me.unhandledFailure, arguments); };
110 | ret.callback = ret.callback || function () { return; };
111 |
112 | return ret;
113 | },
114 |
115 | _isArray: function (v) {
116 | return Object.prototype.toString.apply(v) === '[object Array]';
117 | },
118 |
119 | _isFunction: function (v) {
120 | return Object.prototype.toString.apply(v) === '[object Function]';
121 | },
122 |
123 | _doJsonPost: function (url, data, callback) {
124 | var xhr = new XMLHttpRequest();
125 | xhr.open("POST", url, true);
126 | xhr.setRequestHeader('Content-Type', 'application/json');
127 | xhr.onreadystatechange = function () {
128 | if (xhr.readyState !== 4) {
129 | return;
130 | }
131 |
132 | var contentType = xhr.getResponseHeader('Content-Type');
133 |
134 | if (xhr.status !== 200) {
135 | callback(false, 'Expected HTTP response "200 OK", found "' + xhr.status + ' ' + xhr.statusText + '"');
136 | } else if (contentType.indexOf('application/json') !== 0) {
137 | callback(false, 'Expected JSON encoded response, found "' + contentType + '"');
138 | } else {
139 | callback(true, JSON.parse(this.responseText));
140 | }
141 | };
142 | xhr.send(JSON.stringify(data));
143 | }
144 | };
--------------------------------------------------------------------------------
/tools/jasmine/jasmine-html.js:
--------------------------------------------------------------------------------
1 | jasmine.TrivialReporter = function(doc) {
2 | this.document = doc || document;
3 | this.suiteDivs = {};
4 | this.logRunningSpecs = false;
5 | };
6 |
7 | jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
8 | var el = document.createElement(type);
9 |
10 | for (var i = 2; i < arguments.length; i++) {
11 | var child = arguments[i];
12 |
13 | if (typeof child === 'string') {
14 | el.appendChild(document.createTextNode(child));
15 | } else {
16 | if (child) { el.appendChild(child); }
17 | }
18 | }
19 |
20 | for (var attr in attrs) {
21 | if (attr == "className") {
22 | el[attr] = attrs[attr];
23 | } else {
24 | el.setAttribute(attr, attrs[attr]);
25 | }
26 | }
27 |
28 | return el;
29 | };
30 |
31 | jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
32 | var showPassed, showSkipped;
33 |
34 | this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' },
35 | this.createDom('div', { className: 'banner' },
36 | this.createDom('div', { className: 'logo' },
37 | this.createDom('span', { className: 'title' }, "Jasmine"),
38 | this.createDom('span', { className: 'version' }, runner.env.versionString())),
39 | this.createDom('div', { className: 'options' },
40 | "Show ",
41 | showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
42 | this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
43 | showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
44 | this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
45 | )
46 | ),
47 |
48 | this.runnerDiv = this.createDom('div', { className: 'runner running' },
49 | this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
50 | this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
51 | this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
52 | );
53 |
54 | this.document.body.appendChild(this.outerDiv);
55 |
56 | var suites = runner.suites();
57 | for (var i = 0; i < suites.length; i++) {
58 | var suite = suites[i];
59 | var suiteDiv = this.createDom('div', { className: 'suite' },
60 | this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
61 | this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
62 | this.suiteDivs[suite.id] = suiteDiv;
63 | var parentDiv = this.outerDiv;
64 | if (suite.parentSuite) {
65 | parentDiv = this.suiteDivs[suite.parentSuite.id];
66 | }
67 | parentDiv.appendChild(suiteDiv);
68 | }
69 |
70 | this.startedAt = new Date();
71 |
72 | var self = this;
73 | showPassed.onclick = function(evt) {
74 | if (showPassed.checked) {
75 | self.outerDiv.className += ' show-passed';
76 | } else {
77 | self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
78 | }
79 | };
80 |
81 | showSkipped.onclick = function(evt) {
82 | if (showSkipped.checked) {
83 | self.outerDiv.className += ' show-skipped';
84 | } else {
85 | self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
86 | }
87 | };
88 | };
89 |
90 | jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
91 | var results = runner.results();
92 | var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
93 | this.runnerDiv.setAttribute("class", className);
94 | //do it twice for IE
95 | this.runnerDiv.setAttribute("className", className);
96 | var specs = runner.specs();
97 | var specCount = 0;
98 | for (var i = 0; i < specs.length; i++) {
99 | if (this.specFilter(specs[i])) {
100 | specCount++;
101 | }
102 | }
103 | var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
104 | message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
105 | this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
106 |
107 | this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
108 | };
109 |
110 | jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
111 | var results = suite.results();
112 | var status = results.passed() ? 'passed' : 'failed';
113 | if (results.totalCount === 0) { // todo: change this to check results.skipped
114 | status = 'skipped';
115 | }
116 | this.suiteDivs[suite.id].className += " " + status;
117 | };
118 |
119 | jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
120 | if (this.logRunningSpecs) {
121 | this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
122 | }
123 | };
124 |
125 | jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
126 | var results = spec.results();
127 | var status = results.passed() ? 'passed' : 'failed';
128 | if (results.skipped) {
129 | status = 'skipped';
130 | }
131 | var specDiv = this.createDom('div', { className: 'spec ' + status },
132 | this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
133 | this.createDom('a', {
134 | className: 'description',
135 | href: '?spec=' + encodeURIComponent(spec.getFullName()),
136 | title: spec.getFullName()
137 | }, spec.description));
138 |
139 |
140 | var resultItems = results.getItems();
141 | var messagesDiv = this.createDom('div', { className: 'messages' });
142 | for (var i = 0; i < resultItems.length; i++) {
143 | var result = resultItems[i];
144 |
145 | if (result.type == 'log') {
146 | messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
147 | } else if (result.type == 'expect' && result.passed && !result.passed()) {
148 | messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
149 |
150 | if (result.trace.stack) {
151 | messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
152 | }
153 | }
154 | }
155 |
156 | if (messagesDiv.childNodes.length > 0) {
157 | specDiv.appendChild(messagesDiv);
158 | }
159 |
160 | this.suiteDivs[spec.suite.id].appendChild(specDiv);
161 | };
162 |
163 | jasmine.TrivialReporter.prototype.log = function() {
164 | var console = jasmine.getGlobal().console;
165 | if (console && console.log) {
166 | if (console.log.apply) {
167 | console.log.apply(console, arguments);
168 | } else {
169 | console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
170 | }
171 | }
172 | };
173 |
174 | jasmine.TrivialReporter.prototype.getLocation = function() {
175 | return this.document.location;
176 | };
177 |
178 | jasmine.TrivialReporter.prototype.specFilter = function(spec) {
179 | var paramMap = {};
180 | var params = this.getLocation().search.substring(1).split('&');
181 | for (var i = 0; i < params.length; i++) {
182 | var p = params[i].split('=');
183 | paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
184 | }
185 |
186 | if (!paramMap.spec) {
187 | return true;
188 | }
189 | return spec.getFullName().indexOf(paramMap.spec) === 0;
190 | };
191 |
--------------------------------------------------------------------------------
/test/jsonrpc.JsonRpcTest.js:
--------------------------------------------------------------------------------
1 | describe("jsonrpc.JsonRpc", function () {
2 | var target, fakeSuccess, fakeResponse;
3 |
4 | beforeEach(function () {
5 | target = new jsonrpc.JsonRpc('rpc');
6 | target.setBatchingMilliseconds(0);
7 | spyOn(target, '_doJsonPost').andCallFake(function (url, data, callback) {
8 | callback(fakeSuccess, fakeResponse);
9 | });
10 | });
11 |
12 | it("isFunction", function () {
13 | expect(target._isFunction(1)).toBe(false);
14 | expect(target._isFunction('a')).toBe(false);
15 | expect(target._isFunction(undefined)).toBe(false);
16 | expect(target._isFunction(null)).toBe(false);
17 | expect(target._isFunction(function () {
18 | })).toBe(true);
19 | });
20 |
21 | it("isArray", function () {
22 | expect(target._isArray()).toBe(false);
23 | expect(target._isArray([])).toBe(true);
24 | expect(target._isArray({})).toBe(false);
25 | expect(target._isArray(123)).toBe(false);
26 | expect(target._isArray('')).toBe(false);
27 | expect(target._isArray('a string')).toBe(false);
28 | expect(target._isArray(null)).toBe(false);
29 | expect(target._isArray(arguments)).toBe(false);
30 | });
31 |
32 | it('should interpret call with succes function and scope', function () {
33 | var scope = {},
34 | successFn = jasmine.createSpy(),
35 | actual;
36 |
37 | actual = target._getParams('method', 1, 2, 3, successFn, scope);
38 |
39 | expect(actual.request.method).toEqual('method');
40 | expect(actual.request.params).toEqual([1, 2, 3]);
41 | expect(actual.success).toBe(successFn);
42 | expect(actual.scope).toBe(scope);
43 | });
44 |
45 | it('should batch calls within timeout', function () {
46 | target.setBatchingMilliseconds(10);
47 |
48 | runs(function () {
49 | target.call('method1', 'par1', jasmine.createSpy(), {});
50 | target.call('method2', 'par2', jasmine.createSpy(), {});
51 | expect(target._doJsonPost).not.toHaveBeenCalled();
52 | });
53 |
54 | waits(15);
55 |
56 | runs(function () {
57 | target.call('method3', 'par3', jasmine.createSpy(), {});
58 | });
59 |
60 | waits(15);
61 |
62 | runs(function () {
63 | expect(target._doJsonPost).toHaveBeenCalledWith('rpc', [{
64 | jsonrpc: '2.0',
65 | id: 0,
66 | method: 'method1',
67 | params: ['par1']
68 | }, {
69 | jsonrpc: '2.0',
70 | id: 1,
71 | method: 'method2',
72 | params: ['par2']
73 | }], jasmine.any(Function));
74 | expect(target._doJsonPost).toHaveBeenCalledWith('rpc', {
75 | jsonrpc: '2.0',
76 | id: 0,
77 | method: 'method3',
78 | params: ['par3']
79 | }, jasmine.any(Function));
80 | });
81 | });
82 |
83 | it('should wrap batched call in single loading/loaded events', function () {
84 | var loadingFn = jasmine.createSpy(),
85 | loadedFn = jasmine.createSpy();
86 | target.setBatchingMilliseconds(10);
87 | target.loading.bind(loadingFn);
88 | target.loaded.bind(loadedFn);
89 |
90 | runs(function () {
91 | expect(loadingFn.callCount).toEqual(0);
92 | expect(loadedFn.callCount).toEqual(0);
93 | target.call('method1', 'par1', jasmine.createSpy(), {});
94 | target.call('method2', 'par2', jasmine.createSpy(), {});
95 | expect(loadingFn.callCount).toEqual(1);
96 | expect(loadedFn.callCount).toEqual(0);
97 | });
98 |
99 | waits(15);
100 |
101 | runs(function () {
102 | expect(loadingFn.callCount).toEqual(1);
103 | expect(loadedFn.callCount).toEqual(1);
104 | });
105 | });
106 |
107 | it('should assign progressive ids', function () {
108 | target.setBatchingMilliseconds(10);
109 |
110 | runs(function () {
111 | target.call('method1', {});
112 | target.call('method2', {});
113 | target.call('method3', {});
114 | target.call('method4', {});
115 | });
116 |
117 | waits(15);
118 |
119 | runs(function () {
120 | expect(target._doJsonPost).toHaveBeenCalledWith('rpc', [{
121 | jsonrpc: '2.0',
122 | id: 0,
123 | method: 'method1',
124 | params: []
125 | }, {
126 | jsonrpc: '2.0',
127 | id: 1,
128 | method: 'method2',
129 | params: []
130 | }, {
131 | jsonrpc: '2.0',
132 | id: 2,
133 | method: 'method3',
134 | params: []
135 | }, {
136 | jsonrpc: '2.0',
137 | id: 3,
138 | method: 'method4',
139 | params: []
140 | }], jasmine.any(Function));
141 | });
142 | });
143 |
144 | it('should interpret call with options', function () {
145 | var scope = {},
146 | successFn = jasmine.createSpy(),
147 | failureFn = jasmine.createSpy(),
148 | callbackFn = jasmine.createSpy(),
149 | actual;
150 |
151 | actual = target._getParams('method', 1, 2, 3, {
152 | success: successFn,
153 | failure: failureFn,
154 | callback: callbackFn,
155 | scope: scope
156 | });
157 |
158 | expect(actual.request.method).toEqual('method');
159 | expect(actual.request.params).toEqual([1, 2, 3]);
160 | expect(actual.success).toBe(successFn);
161 | expect(actual.failure).toBe(failureFn);
162 | expect(actual.callback).toBe(callbackFn);
163 | expect(actual.scope).toBe(scope);
164 | });
165 |
166 | it('should do json post with expected parameters', function () {
167 | var scope = {},
168 | successFn = jasmine.createSpy();
169 |
170 | target.call('method', 1, 2, 3, successFn, scope);
171 |
172 | expect(target._doJsonPost).toHaveBeenCalledWith('rpc', {
173 | jsonrpc: '2.0',
174 | id: 0,
175 | method: 'method',
176 | params: [1, 2, 3]
177 | }, jasmine.any(Function));
178 | });
179 |
180 | it('should invoke expected callbacks on succesful call', function () {
181 | var scope = {},
182 | successFn = jasmine.createSpy(),
183 | failureFn = jasmine.createSpy(),
184 | callbackFn = jasmine.createSpy();
185 |
186 | fakeSuccess = true;
187 | fakeResponse = { id: 0, result: 'return val' };
188 |
189 | target.call('method', 1, 2, 3, {
190 | success: successFn,
191 | failure: failureFn,
192 | callback: callbackFn,
193 | scope: scope
194 | });
195 |
196 | expect(failureFn).not.toHaveBeenCalled();
197 | expect(successFn).toHaveBeenCalledWith('return val');
198 | expect(successFn.callCount).toBe(1);
199 | expect(successFn.mostRecentCall.object).toBe(scope);
200 | expect(callbackFn).toHaveBeenCalledWith(true, 'return val');
201 | expect(callbackFn.callCount).toBe(1);
202 | expect(callbackFn.mostRecentCall.object).toBe(scope);
203 | });
204 |
205 | it('should invoke expected callbacks on transport error', function () {
206 | var scope = {},
207 | successFn = jasmine.createSpy(),
208 | failureFn = jasmine.createSpy(),
209 | callbackFn = jasmine.createSpy();
210 |
211 | fakeSuccess = false;
212 | fakeResponse = 'error msg';
213 |
214 | target.call('method', 1, 2, 3, {
215 | success: successFn,
216 | failure: failureFn,
217 | callback: callbackFn,
218 | scope: scope
219 | });
220 |
221 | expect(failureFn).toHaveBeenCalledWith('error msg');
222 | expect(failureFn.callCount).toBe(1);
223 | expect(successFn).not.toHaveBeenCalled();
224 | expect(callbackFn).toHaveBeenCalledWith(false, 'error msg');
225 | expect(callbackFn.callCount).toBe(1);
226 | });
227 |
228 | it('should invoke expected callbacks on rpc error', function () {
229 | var scope = {},
230 | successFn = jasmine.createSpy(),
231 | failureFn = jasmine.createSpy(),
232 | callbackFn = jasmine.createSpy();
233 |
234 | fakeSuccess = true;
235 | fakeResponse = { id: 0, error: { message: 'rpc error' } };
236 |
237 | target.call('method', 1, 2, 3, {
238 | success: successFn,
239 | failure: failureFn,
240 | callback: callbackFn,
241 | scope: scope
242 | });
243 |
244 | expect(failureFn).toHaveBeenCalledWith('rpc error');
245 | expect(failureFn.callCount).toBe(1);
246 | expect(successFn).not.toHaveBeenCalled();
247 | expect(callbackFn).toHaveBeenCalledWith(false, 'rpc error');
248 | expect(callbackFn.callCount).toBe(1);
249 | });
250 |
251 | it('should trigger unhandledFailure event when call fail and failure callback not defined', function () {
252 | var unhandledFailureFn = jasmine.createSpy();
253 |
254 | target.unhandledFailure.bind(unhandledFailureFn);
255 |
256 | fakeSuccess = false;
257 | fakeResponse = 'error msg';
258 |
259 | target.call('method', jasmine.createSpy());
260 | target.call('method', {
261 | failure: jasmine.createSpy()
262 | });
263 |
264 | expect(unhandledFailureFn.callCount).toBe(1);
265 | expect(unhandledFailureFn).toHaveBeenCalledWith('error msg');
266 | });
267 |
268 | it('should trigger loading and loaded events', function () {
269 | var loadingFn = jasmine.createSpy(),
270 | loadedFn = jasmine.createSpy();
271 |
272 | target.loading.bind(loadingFn);
273 | target.loaded.bind(loadedFn);
274 | fakeSuccess = true;
275 | fakeResponse = { id: 0, result: 'return val' };
276 |
277 | target.call('method', 1, 2, 3, function () {});
278 |
279 | expect(loadingFn.callCount).toBe(1);
280 | expect(loadedFn.callCount).toBe(1);
281 | });
282 |
283 | it('should support responses in different order than requests', function () {
284 | // TODO
285 | });
286 | });
287 |
--------------------------------------------------------------------------------
/tools/jsmake/jsmake.js:
--------------------------------------------------------------------------------
1 | /*
2 | JSMake version 0.8.37
3 |
4 | http://gimmi.github.com/jsmake/
5 |
6 | Copyright 2011 Gian Marco Gherardi
7 |
8 | Licensed under the Apache License, Version 2.0 (the "License");
9 | you may not use this file except in compliance with the License.
10 | You may obtain a copy of the License at
11 |
12 | http://www.apache.org/licenses/LICENSE-2.0
13 |
14 | Unless required by applicable law or agreed to in writing, software
15 | distributed under the License is distributed on an "AS IS" BASIS,
16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | See the License for the specific language governing permissions and
18 | limitations under the License.
19 | */
20 | /** @namespace Top level namespace for JSMake */
21 | jsmake = this.jsmake || {};
22 |
23 | jsmake.Rhino = {
24 | translateJavaString: function (javaString) {
25 | if (javaString === null) {
26 | return null;
27 | }
28 | if (javaString === undefined) {
29 | return undefined;
30 | }
31 | return String(javaString);
32 | }
33 | };
34 | /** @class Various helper methods to make working with Javascript easier */
35 | jsmake.Utils = {
36 | /**
37 | * Return the same string with escaped regex chars, in order to be safely included as part of regex
38 | * @param {String} str string to escape
39 | * @returns {String} escaped string
40 | */
41 | escapeForRegex: function (str) {
42 | return str.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
43 | },
44 | /**
45 | * @param v
46 | * @returns {Boolean} true if passed value is an array
47 | */
48 | isArray: function (v) {
49 | // Check ignored test 'isArray should show strange behavior on Firefox'
50 | return Object.prototype.toString.apply(v) === '[object Array]';
51 | },
52 | /**
53 | * @param v
54 | * @returns {Boolean} true if passed value is an argument object
55 | */
56 | isArguments: function (v) {
57 | return !!(v && Object.prototype.hasOwnProperty.call(v, 'callee'));
58 | },
59 | /**
60 | * @param v
61 | * @returns {Array} passed value, converted to array
62 | */
63 | toArray: function (v) {
64 | if (this.isEmpty(v)) {
65 | return [];
66 | } else if (this.isArray(v)) {
67 | return v;
68 | } else if (this.isArguments(v)) {
69 | return Array.prototype.slice.call(v);
70 | } else {
71 | return [ v ];
72 | }
73 | },
74 | /**
75 | * @param v
76 | * @returns {Boolean} true if passed value is an object
77 | */
78 | isObject : function (v) {
79 | return !!v && Object.prototype.toString.call(v) === '[object Object]';
80 | },
81 | /**
82 | * @param v
83 | * @returns {Boolean} true if passed value is a number
84 | */
85 | isNumber: function (v) {
86 | return typeof v === 'number' && isFinite(v);
87 | },
88 | /**
89 | * @param v
90 | * @returns {Boolean} true if passed value is null, undefined or empty array
91 | */
92 | isEmpty : function (v) {
93 | return v === null || v === undefined || ((this.isArray(v) && !v.length));
94 | },
95 | /**
96 | * @param v
97 | * @returns {Boolean} true if passed value is a function
98 | */
99 | isFunction : function (v) {
100 | return Object.prototype.toString.apply(v) === '[object Function]';
101 | },
102 | /**
103 | * @param v
104 | * @returns {Boolean} true if passed value is a string
105 | */
106 | isString: function (value) {
107 | return typeof value === 'string';
108 | },
109 | /**
110 | * @param {String} str string to trim
111 | * @returns {String} passed value with head and tail spaces removed
112 | */
113 | trim: function (str) {
114 | return str.replace(/(?:^\s+)|(?:\s+$)/g, '');
115 | },
116 | /**
117 | * A function that does nothing, useful to pass around as null value
118 | */
119 | EMPTY_FN: function () {
120 | },
121 | /**
122 | * Iterate over each element of items.
123 | * @param items the collection on which iterate, can be anything
124 | * @param {Function} fn the funcion to call for each element in items.
125 | * Will be called with the following parameters: currentItem, itemIndex, items.
126 | * If function returns truthy value then iteration will stop
127 | * @param {Object} [scope] 'this' binding for function
128 | * @example
129 | * // Array iteration: the following code logs
130 | * // item=a, index=0, items=[a,b]
131 | * // item=b, index=1, items=[a,b]
132 | * jsmake.Utils.each([ 'a', 'b'], function (item, index, items) {
133 | * jsmake.Sys.log('item=' + item + ', index=' + index + ', items=' + items);
134 | * }, this);
135 | * // Object iteration: the following code logs
136 | * // item=1, index=a, items=[object]
137 | * // item=2, index=b, items=[object]
138 | * jsmake.Utils.each({ a: 1, b: 2 }, function (item, index, items) {
139 | * jsmake.Sys.log('item=' + item + ', index=' + index + ', items=' + items);
140 | * }, this);
141 | */
142 | each: function (items, fn, scope) {
143 | var key;
144 | if (this.isObject(items)) {
145 | for (key in items) {
146 | if (items.hasOwnProperty(key)) {
147 | if (fn.call(scope, items[key], key, items)) {
148 | return;
149 | }
150 | }
151 | }
152 | } else {
153 | items = this.toArray(items);
154 | for (key = 0; key < items.length; key += 1) {
155 | if (fn.call(scope, items[key], key, items)) {
156 | return;
157 | }
158 | }
159 | }
160 | },
161 | /**
162 | * Filter collection, returning elements that satisfy passed criteria
163 | * @param items can be anything, see {@link jsmake.Utils.each}
164 | * @param {Function} fn filter criteria, will be called for each element in items, passing current element as parameter.
165 | * Must return falsy value to indicate that the element should be filtered out
166 | * @param {Object} [scope] 'this' binding for function
167 | * @returns {Array} filtered values
168 | * @example
169 | * // returns [ 1, 2 ]
170 | * jsmake.Utils.filter([ 1, 2, 3 ], function (item) {
171 | * return item < 3;
172 | * });
173 | */
174 | filter: function (items, fn, scope) {
175 | var ret = [];
176 | this.each(items, function (item) {
177 | if (fn.call(scope, item)) {
178 | ret.push(item);
179 | }
180 | }, this);
181 | return ret;
182 | },
183 | /**
184 | * Transform each item in passed collection, returning a new array with transformed items
185 | * @param items can be anything, see {@link jsmake.Utils.each}
186 | * @param {Function} fn transformation function, will be called for each element in items.
187 | * Will be called with the following parameters: currentItem, itemIndex, items.
188 | * If function returns truthy value then iteration will stop
189 | * Must return the transformed item
190 | * @param {Object} [scope] 'this' binding for function
191 | * @returns {Array} new array with transformed items
192 | * @example
193 | * // returns [ 4, 9 ]
194 | * jsmake.Utils.map([ 2, 3 ], function (item, index, items) {
195 | * return item * item;
196 | * });
197 | */
198 | map: function (items, fn, scope) {
199 | var ret = [];
200 | this.each(items, function (item, index, items) {
201 | ret.push(fn.call(scope, item, index, items));
202 | }, this);
203 | return ret;
204 | },
205 | /**
206 | * @example
207 | * // returns 'items are: 2 3 '
208 | * jsmake.Utils.reduce([ 2, 3 ], function (memo, item, index, items) {
209 | * return memo + item + ' ';
210 | * }, 'items are: ');
211 | */
212 | reduce: function (items, fn, memo, scope) {
213 | this.each(items, function (item, index, items) {
214 | memo = fn.call(scope, memo, item, index, items);
215 | }, this);
216 | return memo;
217 | },
218 | /**
219 | * @example
220 | * jsmake.Utils.contains([ 2, 3 ], 3); // returns true
221 | * jsmake.Utils.contains([ 2, 3 ], 4); // returns false
222 | */
223 | contains: function (items, item) {
224 | var ret = false;
225 | this.each(items, function (it) {
226 | ret = (it === item);
227 | return ret;
228 | }, this);
229 | return ret;
230 | },
231 | /**
232 | * @example
233 | * jsmake.Utils.distinct([ 2, 3, 2, 3 ]); // returns [ 2, 3 ]
234 | */
235 | distinct: function (items) {
236 | var ret = [];
237 | this.each(items, function (item) {
238 | if (!this.contains(ret, item)) {
239 | ret.push(item);
240 | }
241 | }, this);
242 | return ret;
243 | },
244 | /**
245 | * @example
246 | * jsmake.Utils.flatten([ 1, [ 2, 3 ], [ 4, [ 5, 6 ] ] ]); // returns [ 1, 2, 3, 4, 5, 6 ]
247 | */
248 | flatten: function (items) {
249 | return this.reduce(items, function (memo, item) {
250 | if (this.isArray(item)) {
251 | memo = memo.concat(this.flatten(item));
252 | } else {
253 | memo.push(item);
254 | }
255 | return memo;
256 | }, [], this);
257 | }
258 | };
259 |
260 | jsmake.Project = function (logger) {
261 | this._tasks = {};
262 | this._logger = logger;
263 | };
264 | jsmake.Project.prototype = {
265 | addTask: function (task) {
266 | this._tasks[task.getName()] = task;
267 | },
268 | getTask: function (name) {
269 | var task = this._tasks[name];
270 | if (!task) {
271 | throw "Task '" + name + "' not defined";
272 | }
273 | return task;
274 | },
275 | getTasks: function (name) {
276 | var tasks = [];
277 | this._fillDependencies(this.getTask(name), tasks, new jsmake.RecursionChecker('Task recursion found'));
278 | return jsmake.Utils.distinct(tasks);
279 | },
280 | runTask: function (name, args) {
281 | var tasks, taskNames;
282 | tasks = this.getTasks(name);
283 | taskNames = jsmake.Utils.map(tasks, function (task) {
284 | return task.getName();
285 | }, this);
286 | this._logger.log('Task execution order: ' + taskNames.join(', '));
287 | jsmake.Utils.each(tasks, function (task) {
288 | task.run(task.getName() === name ? args : []);
289 | }, this);
290 | },
291 | _fillDependencies: function (task, tasks, recursionChecker) {
292 | recursionChecker.wrap(task.getName(), function () {
293 | jsmake.Utils.each(task.getTaskNames(), function (taskName) {
294 | var task = this.getTask(taskName);
295 | this._fillDependencies(task, tasks, recursionChecker);
296 | }, this);
297 | tasks.push(task);
298 | }, this);
299 | }
300 | };
301 |
302 | jsmake.Task = function (name, taskNames, body, logger) {
303 | this._name = name;
304 | this._taskNames = taskNames;
305 | this._body = body;
306 | this._logger = logger;
307 | };
308 | jsmake.Task.prototype = {
309 | getName: function () {
310 | return this._name;
311 | },
312 | getTaskNames: function () {
313 | return this._taskNames;
314 | },
315 | run: function (args) {
316 | this._logger.log('Executing task ' + this._name);
317 | this._body.apply({}, args);
318 | }
319 | };
320 |
321 | jsmake.RecursionChecker = function (message) {
322 | this._message = message;
323 | this._stack = [];
324 | };
325 | jsmake.RecursionChecker.prototype = {
326 | enter: function (id) {
327 | this._check(id);
328 | this._stack.push(id);
329 | },
330 | exit: function () {
331 | this._stack.pop();
332 | },
333 | wrap: function (id, fn, scope) {
334 | this.enter(id);
335 | try {
336 | fn.call(scope);
337 | } finally {
338 | this.exit();
339 | }
340 | },
341 | _check: function (id) {
342 | if (jsmake.Utils.contains(this._stack, id)) {
343 | this._stack.push(id);
344 | throw this._message + ': ' + this._stack.join(' => ');
345 | }
346 | }
347 | };
348 |
349 | jsmake.AntPathMatcher = function (pattern, caseSensitive) {
350 | this._pattern = pattern;
351 | this._caseSensitive = caseSensitive;
352 | };
353 | jsmake.AntPathMatcher.prototype = {
354 | match: function (path) {
355 | var patternTokens, pathTokens;
356 | patternTokens = this._tokenize(this._pattern);
357 | pathTokens = this._tokenize(path);
358 | return this._matchTokens(patternTokens, pathTokens);
359 | },
360 | _matchTokens: function (patternTokens, pathTokens) {
361 | var patternToken, pathToken;
362 | while (true) {
363 | patternToken = patternTokens.shift();
364 | if (patternToken === '**') {
365 | pathTokens = pathTokens.slice(-patternTokens.length).reverse();
366 | patternTokens = patternTokens.reverse();
367 | return this._matchTokens(patternTokens, pathTokens);
368 | }
369 | pathToken = pathTokens.shift();
370 | if (patternToken && pathToken) {
371 | if (!this._matchToken(patternToken, pathToken)) {
372 | return false;
373 | }
374 | } else if (patternToken && !pathToken) {
375 | return false;
376 | } else if (!patternToken && pathToken) {
377 | return false;
378 | } else {
379 | return true;
380 | }
381 | }
382 | },
383 | _matchToken: function (patternToken, pathToken) {
384 | var regex = '', i, ch;
385 | for (i = 0; i < patternToken.length; i += 1) {
386 | ch = patternToken.charAt(i);
387 | if (ch === '*') {
388 | regex += '.*';
389 | } else if (ch === '?') {
390 | regex += '.{1}';
391 | } else {
392 | regex += jsmake.Utils.escapeForRegex(ch);
393 | }
394 | }
395 | return new RegExp('^' + regex + '$', (this._caseSensitive ? '' : 'i')).test(pathToken);
396 | },
397 | _tokenize: function (pattern) {
398 | var tokens = pattern.split(/\\+|\/+/);
399 | tokens = jsmake.Utils.map(tokens, function (token) {
400 | return jsmake.Utils.trim(token);
401 | }, this);
402 | tokens = jsmake.Utils.filter(tokens, function (token) {
403 | return !/^[\s\.]*$/.test(token);
404 | }, this);
405 | if (tokens[tokens.length - 1] === '**') {
406 | throw 'Invalid ** wildcard at end pattern, use **/* instead'; // TODO maybe useless
407 | }
408 | // TODO invalid more then one **
409 | return tokens;
410 | }
411 | };
412 |
413 | /**
414 | * @class Contains methods for system interaciont and informations
415 | */
416 | jsmake.Sys = {
417 | /**
418 | * Returns if OS is Windows
419 | * @returns true if running on Windows
420 | */
421 | isWindowsOs: function () {
422 | return jsmake.Fs.getPathSeparator() === '\\';
423 | },
424 | runCommand: function (command, opts) {
425 | return runCommand(command, opts);
426 | },
427 | /**
428 | * Create a runner object, used to define and invoke an external program
429 | * @param {String} command the path of the command executable
430 | * @return {jsmake.CommandRunner} CommandRunner instance to fluently configure and run command
431 | * @see jsmake.CommandRunner
432 | * @example
433 | * // runs '/path/to/cmd.exe par1 par2 par3 par4'
434 | * jsmake.Sys.createRunner('/path/to/cmd.exe')
435 | * .args('par1', 'par2')
436 | * .args([ 'par3', 'par4' ])
437 | * .run();
438 | */
439 | createRunner: function (command) {
440 | return new jsmake.CommandRunner(command);
441 | },
442 | /**
443 | * Returns environment variable value
444 | * @param {String} name name of the environment variable
445 | * @param {String} [def] default value to return if environment variable not defined.
446 | * @returns {String} environment variable value if found, or default value.
447 | * @throws {Error} if environment variable is not found and no default value passed.
448 | */
449 | getEnvVar: function (name, def) {
450 | var val = jsmake.Rhino.translateJavaString(java.lang.System.getenv(name));
451 | return this._getEnvVar(name, val, def);
452 | },
453 | /**
454 | * Log message to the console
455 | * @param {String} msg the message to log
456 | */
457 | log: function (msg) {
458 | print(msg);
459 | },
460 | _getEnvVar: function (name, val, def) {
461 | if (val !== null) {
462 | return val;
463 | }
464 | if (def !== undefined) {
465 | return def;
466 | }
467 | throw 'Environment variable "' + name + '" not defined.';
468 | }
469 | };
470 |
471 | /** @class Contains methods for working with filesystem */
472 | jsmake.Fs = {
473 | /**
474 | * Create a zip file containing specified file/directory
475 | * @param {String} srcPath file/directory to zip
476 | * @param {String} destFile zip file name
477 | */
478 | zipPath: function (srcPath, destFile) {
479 | jsmake.PathZipper.zip(srcPath, destFile);
480 | },
481 | /**
482 | * Create a filesystem scanner
483 | * @param {String} basePath the path to scan for children tha match criteria
484 | * @returns {jsmake.FsScanner} FsScanner instance to fluently configure and run scanner
485 | * @see jsmake.FsScanner
486 | * @example
487 | * // returns all js and java files in \home folder, including subfolders, excluding .git folders
488 | * jsmake.Fs.createScanner('\home')
489 | * .include('**\*.js')
490 | * .include('**\*.java')
491 | * .exclude('**\.git')
492 | * .scan();
493 | */
494 | createScanner: function (basePath) {
495 | return new jsmake.FsScanner(basePath, this.isCaseSensitive());
496 | },
497 | /**
498 | * Return default OS character encoding
499 | * @returns {String} Character encoding, e.g. 'UTF-8' or 'Cp1252'
500 | */
501 | getCharacterEncoding: function () {
502 | return java.lang.System.getProperty("file.encoding", "UTF-8"); // Windows default is "Cp1252"
503 | },
504 | /**
505 | * Return OS path separator
506 | * @returns {String} path separator, e.g. '/' or '\'
507 | */
508 | getPathSeparator: function () {
509 | return jsmake.Rhino.translateJavaString(java.io.File.separator);
510 | },
511 | /**
512 | * Returns true if OS has case sensitive filesystem
513 | * @returns {Boolean} true if OS has case sensitive filesystem
514 | */
515 | isCaseSensitive: function () {
516 | return !jsmake.Sys.isWindowsOs();
517 | },
518 | /**
519 | * Read text file content
520 | * @param {String} path path of the file to read
521 | * @param {String} [characterEncoding=OS default]
522 | * @returns {String} text content
523 | */
524 | readFile: function (path, characterEncoding) {
525 | characterEncoding = characterEncoding || this.getCharacterEncoding();
526 | if (!this.fileExists(path)) {
527 | throw "File '" + path + "' not found";
528 | }
529 | return readFile(path, characterEncoding);
530 | },
531 | /**
532 | * Write String to file, creating all necessary parent directories and overwriting if file already exists
533 | * @param {String} path path of the file to write
534 | * @param {String} content file content
535 | * @param {String} [characterEncoding=OS default]
536 | */
537 | writeFile: function (path, content, characterEncoding) {
538 | characterEncoding = characterEncoding || this.getCharacterEncoding();
539 | this.createDirectory(this.getParentDirectory(path));
540 | var out = new java.io.FileOutputStream(new java.io.File(path));
541 | content = new java.lang.String(content || '');
542 | try {
543 | out.write(content.getBytes(characterEncoding));
544 | } finally {
545 | out.close();
546 | }
547 | },
548 | /**
549 | * Extract last element from a path
550 | * @param {String} path the source path
551 | * @returns {String} the name of the last element in the path
552 | * @example
553 | * jsmake.Fs.getName('/users/gimmi/file.txt'); // returns 'file.txt'
554 | */
555 | getName: function (path) {
556 | return jsmake.Rhino.translateJavaString(new java.io.File(path).getName());
557 | },
558 | /**
559 | * Copy file or directory to another directory
560 | * @param {String} srcPath source file/directory. Must exists
561 | * @param {String} destDirectory destination directory
562 | */
563 | copyPath: function (srcPath, destDirectory) {
564 | if (this.fileExists(srcPath)) {
565 | this._copyFile(srcPath, destDirectory);
566 | } else if (this.directoryExists(srcPath)) {
567 | this._copyDirectory(srcPath, destDirectory);
568 | } else {
569 | throw "Cannot copy source path '" + srcPath + "', it does not exists";
570 | }
571 | },
572 | /**
573 | * @param {String} path file or directory path
574 | * @returns {Boolean} true if file or directory exists
575 | */
576 | pathExists: function (path) {
577 | return new java.io.File(path).exists();
578 | },
579 | /**
580 | * @param {String} path directory path
581 | * @returns {Boolean} true if path exists and is a directory
582 | */
583 | directoryExists: function (path) {
584 | var file = new java.io.File(path);
585 | return file.exists() && file.isDirectory();
586 | },
587 | /**
588 | * @param {String} path file path
589 | * @returns {Boolean} true if path exists and is a file
590 | */
591 | fileExists: function (path) {
592 | var file = new java.io.File(path);
593 | return file.exists() && file.isFile();
594 | },
595 | /**
596 | * Create directory and all necessary parents
597 | * @param {String} path directory to create
598 | */
599 | createDirectory: function (path) {
600 | var file = new java.io.File(path);
601 | if (file.exists() && file.isDirectory()) {
602 | return;
603 | }
604 | if (!file.mkdirs()) {
605 | throw "Failed to create directories for path '" + path + "'";
606 | }
607 | },
608 | /**
609 | * Delete file or directory, with all cild elements
610 | * @param {String} path to delete
611 | */
612 | deletePath: function (path) {
613 | if (!this.pathExists(path)) {
614 | return;
615 | }
616 | jsmake.Utils.each(jsmake.Fs.getChildPathNames(path), function (name) {
617 | this.deletePath(this.combinePaths(path, name));
618 | }, this);
619 | if (!new java.io.File(path)['delete']()) {
620 | throw "'Unable to delete path '" + path + "'";
621 | }
622 | },
623 | /**
624 | * Transform a path to absolute, removing '.' and '..' references
625 | * @param {String} path path to translate
626 | * @returns {String} path in canonical form
627 | * @example
628 | * jsmake.Fs.getCanonicalPath('../file.txt'); // returns '/users/file.txt'
629 | */
630 | getCanonicalPath: function (path) {
631 | return jsmake.Rhino.translateJavaString(new java.io.File(path).getCanonicalPath());
632 | },
633 | /**
634 | * Returns parent path
635 | * @param {String} path
636 | * @returns {String} parent path
637 | */
638 | getParentDirectory: function (path) {
639 | return jsmake.Rhino.translateJavaString(new java.io.File(path).getCanonicalFile().getParent());
640 | },
641 | /**
642 | * Combine all passed path fragments into one, using OS path separator. Supports any number of parameters.
643 | * @example
644 | * jsmake.Fs.combinePaths('home', 'gimmi', [ 'dir/subdir', 'file.txt' ]);
645 | * // returns 'home/gimmi/dir/subdir/file.txt'
646 | */
647 | combinePaths: function () {
648 | var paths = jsmake.Utils.flatten(arguments);
649 | return jsmake.Utils.reduce(paths, function (memo, path) {
650 | return (memo ? this._javaCombine(memo, path) : path);
651 | }, null, this);
652 | },
653 | getChildPathNames: function (basePath) {
654 | return this._listFilesWithFilter(basePath, function () {
655 | return true;
656 | });
657 | },
658 | getChildFileNames: function (basePath) {
659 | return this._listFilesWithFilter(basePath, function (fileName) {
660 | return new java.io.File(fileName).isFile();
661 | });
662 | },
663 | getChildDirectoryNames: function (basePath) {
664 | return this._listFilesWithFilter(basePath, function (fileName) {
665 | return new java.io.File(fileName).isDirectory();
666 | });
667 | },
668 | _javaCombine: function (path1, path2) {
669 | return jsmake.Rhino.translateJavaString(new java.io.File(path1, path2).getPath());
670 | },
671 | _copyDirectory: function (srcDirectory, destDirectory) {
672 | this.deletePath(destDirectory);
673 | this.createDirectory(destDirectory);
674 | jsmake.Utils.each(this.getChildFileNames(srcDirectory), function (path) {
675 | this.copyPath(this.combinePaths(srcDirectory, path), destDirectory);
676 | }, this);
677 | jsmake.Utils.each(this.getChildDirectoryNames(srcDirectory), function (path) {
678 | this.copyPath(this.combinePaths(srcDirectory, path), this.combinePaths(destDirectory, path));
679 | }, this);
680 | },
681 | _copyFile: function (srcFile, destDirectory) {
682 | var destFile = this.combinePaths(destDirectory, this.getName(srcFile));
683 | this.deletePath(destFile);
684 | this.createDirectory(destDirectory);
685 | this._copyFileToFile(srcFile, destFile);
686 | },
687 | _copyFileToFile: function (srcFile, destFile) {
688 | var input, output, buffer, n;
689 | input = new java.io.FileInputStream(srcFile);
690 | try {
691 | output = new java.io.FileOutputStream(destFile);
692 | try {
693 | buffer = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, 1024 * 4);
694 | while (-1 !== (n = input.read(buffer))) {
695 | output.write(buffer, 0, n);
696 | }
697 | } finally {
698 | output.close();
699 | }
700 | } finally {
701 | input.close();
702 | }
703 | },
704 | _listFilesWithFilter: function (basePath, filter) {
705 | var fileFilter, files;
706 | fileFilter = new java.io.FileFilter({ accept: filter });
707 | files = this._translateJavaArray(new java.io.File(basePath).listFiles(fileFilter));
708 | return jsmake.Utils.map(files, function (file) {
709 | return jsmake.Rhino.translateJavaString(file.getName());
710 | }, this);
711 | },
712 | _translateJavaArray: function (javaArray) {
713 | var ary = [], i;
714 | if (javaArray === null) {
715 | return null;
716 | }
717 | for (i = 0; i < javaArray.length; i += 1) {
718 | ary.push(javaArray[i]);
719 | }
720 | return ary;
721 | }
722 | };
723 | /**
724 | * Don't instantiate it directly, use {@link jsmake.Fs.createScanner}
725 | * @constructor
726 | */
727 | jsmake.FsScanner = function (basePath, caseSensitive) {
728 | this._basePath = basePath;
729 | this._includeMatchers = [];
730 | this._excludeMatchers = [];
731 | this._caseSensitive = caseSensitive;
732 | };
733 | jsmake.FsScanner.prototype = {
734 | /**
735 | * Add a criteria for path inclusion. If no inclusion path are specified, '**\*' is assumed
736 | * @param {String} pattern
737 | * @returns {jsmake.FsScanner} this instance, for chaining calls
738 | * @example
739 | * jsmake.Fs.createScanner('\home').include('**\*.js').scan();
740 | */
741 | include: function (pattern) {
742 | this._includeMatchers.push(new jsmake.AntPathMatcher(pattern, this._caseSensitive));
743 | return this;
744 | },
745 | /**
746 | * Add a criteria for path exclusion
747 | * @param {String} pattern
748 | * @returns {jsmake.FsScanner} this instance, for chaining calls
749 | * @example
750 | * jsmake.Fs.createScanner('\home').exclude('**\.git').scan();
751 | */
752 | exclude: function (pattern) {
753 | this._excludeMatchers.push(new jsmake.AntPathMatcher(pattern, this._caseSensitive));
754 | return this;
755 | },
756 | /**
757 | * Execute filesystem scanning with defined criterias
758 | * @returns {String[]} all mathing paths
759 | * @example
760 | * // returns the path of all files in /home directory
761 | * jsmake.Fs.createScanner('/home').scan();
762 | */
763 | scan: function () {
764 | var fileNames = [];
765 | if (this._includeMatchers.length === 0) {
766 | this.include('**/*');
767 | }
768 | this._scan('.', fileNames);
769 | return fileNames;
770 | },
771 | _scan: function (relativePath, fileNames) {
772 | var fullPath = jsmake.Fs.combinePaths(this._basePath, relativePath);
773 | jsmake.Utils.each(jsmake.Fs.getChildFileNames(fullPath), function (fileName) {
774 | fileName = jsmake.Fs.combinePaths(relativePath, fileName);
775 | if (this._evaluatePath(fileName, false)) {
776 | fileNames.push(jsmake.Fs.combinePaths(this._basePath, fileName));
777 | }
778 | }, this);
779 | jsmake.Utils.each(jsmake.Fs.getChildDirectoryNames(fullPath), function (dir) {
780 | dir = jsmake.Fs.combinePaths(relativePath, dir);
781 | if (this._evaluatePath(dir, true)) {
782 | this._scan(dir, fileNames);
783 | }
784 | }, this);
785 | },
786 | _evaluatePath: function (path, def) {
787 | if (this._runMatchers(this._excludeMatchers, path)) {
788 | return false;
789 | }
790 | if (this._runMatchers(this._includeMatchers, path)) {
791 | return true;
792 | }
793 | return def;
794 | },
795 | _runMatchers: function (matchers, value) {
796 | var match = false;
797 | jsmake.Utils.each(matchers, function (matcher) {
798 | match = match || matcher.match(value);
799 | }, this);
800 | return match;
801 | }
802 | };
803 |
804 | /**
805 | * Don't instantiate it directly, use {@link jsmake.Sys.createRunner}
806 | * @constructor
807 | */
808 | jsmake.CommandRunner = function (command) {
809 | this._command = command;
810 | this._arguments = [];
811 | this._logger = jsmake.Sys;
812 | };
813 | jsmake.CommandRunner.prototype = {
814 | /**
815 | * Add all passed arguments. Supports any number of parameters.
816 | * @returns {jsmake.CommandRunner} this instance, for chaining calls
817 | * @example
818 | * jsmake.Sys.createRunner('cmd.exe').args('par1', 'par2', [ 'par3', 'par4' ]).run();
819 | */
820 | args: function () {
821 | this._arguments = this._arguments.concat(jsmake.Utils.flatten(arguments));
822 | return this;
823 | },
824 | /**
825 | * Run configured command. if exitstatus of the command is 0 then execution is considered succesful, otherwise an exception is thrown
826 | */
827 | run: function () {
828 | this._logger.log(this._command + ' ' + this._arguments.join(' '));
829 | var exitStatus = jsmake.Sys.runCommand(this._command, { args: this._arguments });
830 | if (exitStatus !== 0) {
831 | throw 'Command failed with exit status ' + exitStatus;
832 | }
833 | }
834 | };
835 | jsmake.PathZipper = {
836 | zip: function (srcPath, destFile) {
837 | var zipOutputStream = new java.util.zip.ZipOutputStream(new java.io.FileOutputStream(destFile));
838 | try {
839 | this._zip(jsmake.Fs.getParentDirectory(srcPath), jsmake.Fs.getName(srcPath), zipOutputStream);
840 | } finally {
841 | zipOutputStream.close(); // This raise exception "java.util.zip.ZipException: ZIP file must have at least one entry"
842 | }
843 | },
844 | _zip: function (basePath, relativePath, zipOutputStream) {
845 | var names, path;
846 | path = jsmake.Fs.combinePaths(basePath, relativePath);
847 | if (jsmake.Fs.fileExists(path)) {
848 | this._addFile(basePath, relativePath, zipOutputStream);
849 | } else if (jsmake.Fs.directoryExists(path)) {
850 | jsmake.Utils.each(jsmake.Fs.getChildPathNames(path), function (name) {
851 | this._zip(basePath, jsmake.Fs.combinePaths(relativePath, name), zipOutputStream);
852 | }, this);
853 | } else {
854 | throw "Cannot zip source path '" + path + "', it does not exists";
855 | }
856 | },
857 | _addFile: function (basePath, relativePath, zipOutputStream) {
858 | var fileInputStream, buffer, n;
859 | zipOutputStream.putNextEntry(new java.util.zip.ZipEntry(relativePath));
860 | buffer = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, 1024 * 4);
861 | fileInputStream = new java.io.FileInputStream(jsmake.Fs.combinePaths(basePath, relativePath));
862 | try {
863 | while (-1 !== (n = fileInputStream.read(buffer))) {
864 | zipOutputStream.write(buffer, 0, n);
865 | }
866 | } finally {
867 | fileInputStream.close();
868 | }
869 | zipOutputStream.closeEntry();
870 | }
871 | };
872 | /** @class Various helper methods for manipulating XML files */
873 | jsmake.Xml = {
874 | /**
875 | * Search nodes that match XPath in XML file.
876 | * @param {String} file XML file path
877 | * @param {String} xpath XPath query to search for
878 | * @returns {String[]} an array of values of matching nodes
879 | * @example
880 | * var values = jsmake.Xml.getValues('temp/file.xml', '//series/season/episode/text()');
881 | */
882 | getValues: function (file, xpath) {
883 | var i, ret = [], nodeList;
884 | nodeList = this._getNodeList(this._loadDocument(file), xpath);
885 | for (i = 0; i < nodeList.getLength(); i += 1) {
886 | ret.push(jsmake.Rhino.translateJavaString(nodeList.item(i).getNodeValue()));
887 | }
888 | return ret;
889 | },
890 | /**
891 | * Like {@link jsmake.Xml.getValues}, but expect a single match, throwing exception otherwise.
892 | * @param {String} file XML file path
893 | * @param {String} xpath XPath query to search for
894 | * @returns {String} value of matching node
895 | * @example
896 | * var episode = jsmake.Xml.getValue('temp/file.xml', '//series/season[@id="1"]/episode/text()');
897 | */
898 | getValue: function (file, xpath) {
899 | var values = this.getValues(file, xpath);
900 | if (values.length !== 1) {
901 | throw "Unable to find a single element for xpath '" + xpath + "' in file '" + file + "'";
902 | }
903 | return values[0];
904 | },
905 | /**
906 | * Set value of matching node in XML file. throw exception if multiple nodes match XPath.
907 | * @param {String} file XML file path
908 | * @param {String} xpath XPath query to search for
909 | * @param {String} value value to set
910 | * @example
911 | * jsmake.Xml.setValue('temp/file.xml', '//series/season[@id="1"]/episode', 'new episode value');
912 | */
913 | setValue: function (file, xpath, value) {
914 | var nodeList, document;
915 | document = this._loadDocument(file);
916 | nodeList = this._getNodeList(document, xpath);
917 | if (nodeList.getLength() !== 1) {
918 | throw "Unable to find a single element for xpath '" + xpath + "' in file '" + file + "'";
919 | }
920 | nodeList.item(0).setTextContent(value);
921 | this._saveDocument(document, file);
922 | },
923 | _getNodeList: function (document, xpath) {
924 | return javax.xml.xpath.XPathFactory.newInstance().newXPath().evaluate(xpath, document, javax.xml.xpath.XPathConstants.NODESET);
925 | },
926 | _loadDocument: function (file) {
927 | var documentBuilderFactory, document;
928 | documentBuilderFactory = javax.xml.parsers.DocumentBuilderFactory.newInstance();
929 | documentBuilderFactory.setNamespaceAware(true);
930 | document = documentBuilderFactory.newDocumentBuilder().parse(file);
931 | return document;
932 | },
933 | _saveDocument: function (document, file) {
934 | var transformer;
935 | transformer = javax.xml.transform.TransformerFactory.newInstance().newTransformer();
936 | transformer.transform(new javax.xml.transform.dom.DOMSource(document), new javax.xml.transform.stream.StreamResult(new java.io.File(file)));
937 | }
938 | };
939 |
940 | jsmake.Main = function () {
941 | this._project = null;
942 | this._logger = jsmake.Sys;
943 | };
944 | jsmake.Main.prototype = {
945 | init: function (global) {
946 | this._project = new jsmake.Project(this._logger);
947 | global.task = this._bind(this._task, this);
948 | },
949 | runTask: function (name, args) {
950 | this._project.runTask(name, args);
951 | },
952 | // TODO document it with JSDoc
953 | _task: function () {
954 | var args = this._getTaskParameters(jsmake.Utils.toArray(arguments));
955 | this._project.addTask(new jsmake.Task(args[0], args[1], args[2], this._logger));
956 | },
957 | _getTaskParameters: function (args) {
958 | return [
959 | args.shift(),
960 | jsmake.Utils.isFunction(args[0]) ? [] : jsmake.Utils.toArray(args.shift()),
961 | args.shift() || jsmake.Utils.EMPTY_FN
962 | ];
963 | },
964 | _bind: function (fn, scope) {
965 | return function () {
966 | fn.apply(scope, arguments);
967 | };
968 | }
969 | };
970 |
--------------------------------------------------------------------------------
/tools/uglifyjs/lib/parse-js.js:
--------------------------------------------------------------------------------
1 | /***********************************************************************
2 |
3 | A JavaScript tokenizer / parser / beautifier / compressor.
4 |
5 | This version is suitable for Node.js. With minimal changes (the
6 | exports stuff) it should work on any JS platform.
7 |
8 | This file contains the tokenizer/parser. It is a port to JavaScript
9 | of parse-js [1], a JavaScript parser library written in Common Lisp
10 | by Marijn Haverbeke. Thank you Marijn!
11 |
12 | [1] http://marijn.haverbeke.nl/parse-js/
13 |
14 | Exported functions:
15 |
16 | - tokenizer(code) -- returns a function. Call the returned
17 | function to fetch the next token.
18 |
19 | - parse(code) -- returns an AST of the given JavaScript code.
20 |
21 | -------------------------------- (C) ---------------------------------
22 |
23 | Author: Mihai Bazon
24 |
25 | http://mihai.bazon.net/blog
26 |
27 | Distributed under the BSD license:
28 |
29 | Copyright 2010 (c) Mihai Bazon
30 | Based on parse-js (http://marijn.haverbeke.nl/parse-js/).
31 |
32 | Redistribution and use in source and binary forms, with or without
33 | modification, are permitted provided that the following conditions
34 | are met:
35 |
36 | * Redistributions of source code must retain the above
37 | copyright notice, this list of conditions and the following
38 | disclaimer.
39 |
40 | * Redistributions in binary form must reproduce the above
41 | copyright notice, this list of conditions and the following
42 | disclaimer in the documentation and/or other materials
43 | provided with the distribution.
44 |
45 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
46 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
48 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
49 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
50 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
51 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
52 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
53 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
54 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
55 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 | SUCH DAMAGE.
57 |
58 | ***********************************************************************/
59 |
60 | /* -----[ Tokenizer (constants) ]----- */
61 |
62 | var KEYWORDS = array_to_hash([
63 | "break",
64 | "case",
65 | "catch",
66 | "const",
67 | "continue",
68 | "debugger",
69 | "default",
70 | "delete",
71 | "do",
72 | "else",
73 | "finally",
74 | "for",
75 | "function",
76 | "if",
77 | "in",
78 | "instanceof",
79 | "new",
80 | "return",
81 | "switch",
82 | "throw",
83 | "try",
84 | "typeof",
85 | "var",
86 | "void",
87 | "while",
88 | "with"
89 | ]);
90 |
91 | var RESERVED_WORDS = array_to_hash([
92 | "abstract",
93 | "boolean",
94 | "byte",
95 | "char",
96 | "class",
97 | "double",
98 | "enum",
99 | "export",
100 | "extends",
101 | "final",
102 | "float",
103 | "goto",
104 | "implements",
105 | "import",
106 | "int",
107 | "interface",
108 | "long",
109 | "native",
110 | "package",
111 | "private",
112 | "protected",
113 | "public",
114 | "short",
115 | "static",
116 | "super",
117 | "synchronized",
118 | "throws",
119 | "transient",
120 | "volatile"
121 | ]);
122 |
123 | var KEYWORDS_BEFORE_EXPRESSION = array_to_hash([
124 | "return",
125 | "new",
126 | "delete",
127 | "throw",
128 | "else",
129 | "case"
130 | ]);
131 |
132 | var KEYWORDS_ATOM = array_to_hash([
133 | "false",
134 | "null",
135 | "true",
136 | "undefined"
137 | ]);
138 |
139 | var OPERATOR_CHARS = array_to_hash(characters("+-*&%=<>!?|~^"));
140 |
141 | var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
142 | var RE_OCT_NUMBER = /^0[0-7]+$/;
143 | var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i;
144 |
145 | var OPERATORS = array_to_hash([
146 | "in",
147 | "instanceof",
148 | "typeof",
149 | "new",
150 | "void",
151 | "delete",
152 | "++",
153 | "--",
154 | "+",
155 | "-",
156 | "!",
157 | "~",
158 | "&",
159 | "|",
160 | "^",
161 | "*",
162 | "/",
163 | "%",
164 | ">>",
165 | "<<",
166 | ">>>",
167 | "<",
168 | ">",
169 | "<=",
170 | ">=",
171 | "==",
172 | "===",
173 | "!=",
174 | "!==",
175 | "?",
176 | "=",
177 | "+=",
178 | "-=",
179 | "/=",
180 | "*=",
181 | "%=",
182 | ">>=",
183 | "<<=",
184 | ">>>=",
185 | "|=",
186 | "^=",
187 | "&=",
188 | "&&",
189 | "||"
190 | ]);
191 |
192 | var WHITESPACE_CHARS = array_to_hash(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000"));
193 |
194 | var PUNC_BEFORE_EXPRESSION = array_to_hash(characters("[{}(,.;:"));
195 |
196 | var PUNC_CHARS = array_to_hash(characters("[]{}(),;:"));
197 |
198 | var REGEXP_MODIFIERS = array_to_hash(characters("gmsiy"));
199 |
200 | /* -----[ Tokenizer ]----- */
201 |
202 | // regexps adapted from http://xregexp.com/plugins/#unicode
203 | var UNICODE = {
204 | letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u0523\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0621-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971\\u0972\\u097B-\\u097F\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D28\\u0D2A-\\u0D39\\u0D3D\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC\\u0EDD\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8B\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10D0-\\u10FA\\u10FC\\u1100-\\u1159\\u115F-\\u11A2\\u11A8-\\u11F9\\u1200-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u1676\\u1681-\\u169A\\u16A0-\\u16EA\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u1900-\\u191C\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19A9\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u2094\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183\\u2184\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2C6F\\u2C71-\\u2C7D\\u2C80-\\u2CE4\\u2D00-\\u2D25\\u2D30-\\u2D65\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005\\u3006\\u3031-\\u3035\\u303B\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31B7\\u31F0-\\u31FF\\u3400\\u4DB5\\u4E00\\u9FC3\\uA000-\\uA48C\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA65F\\uA662-\\uA66E\\uA67F-\\uA697\\uA717-\\uA71F\\uA722-\\uA788\\uA78B\\uA78C\\uA7FB-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA90A-\\uA925\\uA930-\\uA946\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAC00\\uD7A3\\uF900-\\uFA2D\\uFA30-\\uFA6A\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"),
205 | non_spacing_mark: new RegExp("[\\u0300-\\u036F\\u0483-\\u0487\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u0610-\\u061A\\u064B-\\u065E\\u0670\\u06D6-\\u06DC\\u06DF-\\u06E4\\u06E7\\u06E8\\u06EA-\\u06ED\\u0711\\u0730-\\u074A\\u07A6-\\u07B0\\u07EB-\\u07F3\\u0816-\\u0819\\u081B-\\u0823\\u0825-\\u0827\\u0829-\\u082D\\u0900-\\u0902\\u093C\\u0941-\\u0948\\u094D\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09BC\\u09C1-\\u09C4\\u09CD\\u09E2\\u09E3\\u0A01\\u0A02\\u0A3C\\u0A41\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A70\\u0A71\\u0A75\\u0A81\\u0A82\\u0ABC\\u0AC1-\\u0AC5\\u0AC7\\u0AC8\\u0ACD\\u0AE2\\u0AE3\\u0B01\\u0B3C\\u0B3F\\u0B41-\\u0B44\\u0B4D\\u0B56\\u0B62\\u0B63\\u0B82\\u0BC0\\u0BCD\\u0C3E-\\u0C40\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C62\\u0C63\\u0CBC\\u0CBF\\u0CC6\\u0CCC\\u0CCD\\u0CE2\\u0CE3\\u0D41-\\u0D44\\u0D4D\\u0D62\\u0D63\\u0DCA\\u0DD2-\\u0DD4\\u0DD6\\u0E31\\u0E34-\\u0E3A\\u0E47-\\u0E4E\\u0EB1\\u0EB4-\\u0EB9\\u0EBB\\u0EBC\\u0EC8-\\u0ECD\\u0F18\\u0F19\\u0F35\\u0F37\\u0F39\\u0F71-\\u0F7E\\u0F80-\\u0F84\\u0F86\\u0F87\\u0F90-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u102D-\\u1030\\u1032-\\u1037\\u1039\\u103A\\u103D\\u103E\\u1058\\u1059\\u105E-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108D\\u109D\\u135F\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17B7-\\u17BD\\u17C6\\u17C9-\\u17D3\\u17DD\\u180B-\\u180D\\u18A9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193B\\u1A17\\u1A18\\u1A56\\u1A58-\\u1A5E\\u1A60\\u1A62\\u1A65-\\u1A6C\\u1A73-\\u1A7C\\u1A7F\\u1B00-\\u1B03\\u1B34\\u1B36-\\u1B3A\\u1B3C\\u1B42\\u1B6B-\\u1B73\\u1B80\\u1B81\\u1BA2-\\u1BA5\\u1BA8\\u1BA9\\u1C2C-\\u1C33\\u1C36\\u1C37\\u1CD0-\\u1CD2\\u1CD4-\\u1CE0\\u1CE2-\\u1CE8\\u1CED\\u1DC0-\\u1DE6\\u1DFD-\\u1DFF\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2CEF-\\u2CF1\\u2DE0-\\u2DFF\\u302A-\\u302F\\u3099\\u309A\\uA66F\\uA67C\\uA67D\\uA6F0\\uA6F1\\uA802\\uA806\\uA80B\\uA825\\uA826\\uA8C4\\uA8E0-\\uA8F1\\uA926-\\uA92D\\uA947-\\uA951\\uA980-\\uA982\\uA9B3\\uA9B6-\\uA9B9\\uA9BC\\uAA29-\\uAA2E\\uAA31\\uAA32\\uAA35\\uAA36\\uAA43\\uAA4C\\uAAB0\\uAAB2-\\uAAB4\\uAAB7\\uAAB8\\uAABE\\uAABF\\uAAC1\\uABE5\\uABE8\\uABED\\uFB1E\\uFE00-\\uFE0F\\uFE20-\\uFE26]"),
206 | space_combining_mark: new RegExp("[\\u0903\\u093E-\\u0940\\u0949-\\u094C\\u094E\\u0982\\u0983\\u09BE-\\u09C0\\u09C7\\u09C8\\u09CB\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB\\u0ACC\\u0B02\\u0B03\\u0B3E\\u0B40\\u0B47\\u0B48\\u0B4B\\u0B4C\\u0B57\\u0BBE\\u0BBF\\u0BC1\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7\\u0CC8\\u0CCA\\u0CCB\\u0CD5\\u0CD6\\u0D02\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48\\u0D4A-\\u0D4C\\u0D57\\u0D82\\u0D83\\u0DCF-\\u0DD1\\u0DD8-\\u0DDF\\u0DF2\\u0DF3\\u0F3E\\u0F3F\\u0F7F\\u102B\\u102C\\u1031\\u1038\\u103B\\u103C\\u1056\\u1057\\u1062-\\u1064\\u1067-\\u106D\\u1083\\u1084\\u1087-\\u108C\\u108F\\u109A-\\u109C\\u17B6\\u17BE-\\u17C5\\u17C7\\u17C8\\u1923-\\u1926\\u1929-\\u192B\\u1930\\u1931\\u1933-\\u1938\\u19B0-\\u19C0\\u19C8\\u19C9\\u1A19-\\u1A1B\\u1A55\\u1A57\\u1A61\\u1A63\\u1A64\\u1A6D-\\u1A72\\u1B04\\u1B35\\u1B3B\\u1B3D-\\u1B41\\u1B43\\u1B44\\u1B82\\u1BA1\\u1BA6\\u1BA7\\u1BAA\\u1C24-\\u1C2B\\u1C34\\u1C35\\u1CE1\\u1CF2\\uA823\\uA824\\uA827\\uA880\\uA881\\uA8B4-\\uA8C3\\uA952\\uA953\\uA983\\uA9B4\\uA9B5\\uA9BA\\uA9BB\\uA9BD-\\uA9C0\\uAA2F\\uAA30\\uAA33\\uAA34\\uAA4D\\uAA7B\\uABE3\\uABE4\\uABE6\\uABE7\\uABE9\\uABEA\\uABEC]"),
207 | connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]")
208 | };
209 |
210 | function is_letter(ch) {
211 | return UNICODE.letter.test(ch);
212 | };
213 |
214 | function is_digit(ch) {
215 | ch = ch.charCodeAt(0);
216 | return ch >= 48 && ch <= 57; //XXX: find out if "UnicodeDigit" means something else than 0..9
217 | };
218 |
219 | function is_alphanumeric_char(ch) {
220 | return is_digit(ch) || is_letter(ch);
221 | };
222 |
223 | function is_unicode_combining_mark(ch) {
224 | return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch);
225 | };
226 |
227 | function is_unicode_connector_punctuation(ch) {
228 | return UNICODE.connector_punctuation.test(ch);
229 | };
230 |
231 | function is_identifier_start(ch) {
232 | return ch == "$" || ch == "_" || is_letter(ch);
233 | };
234 |
235 | function is_identifier_char(ch) {
236 | return is_identifier_start(ch)
237 | || is_unicode_combining_mark(ch)
238 | || is_digit(ch)
239 | || is_unicode_connector_punctuation(ch)
240 | || ch == "\u200c" // zero-width non-joiner
241 | || ch == "\u200d" // zero-width joiner (in my ECMA-262 PDF, this is also 200c)
242 | ;
243 | };
244 |
245 | function parse_js_number(num) {
246 | if (RE_HEX_NUMBER.test(num)) {
247 | return parseInt(num.substr(2), 16);
248 | } else if (RE_OCT_NUMBER.test(num)) {
249 | return parseInt(num.substr(1), 8);
250 | } else if (RE_DEC_NUMBER.test(num)) {
251 | return parseFloat(num);
252 | }
253 | };
254 |
255 | function JS_Parse_Error(message, line, col, pos) {
256 | this.message = message;
257 | this.line = line + 1;
258 | this.col = col + 1;
259 | this.pos = pos + 1;
260 | this.stack = new Error().stack;
261 | };
262 |
263 | JS_Parse_Error.prototype.toString = function() {
264 | return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
265 | };
266 |
267 | function js_error(message, line, col, pos) {
268 | throw new JS_Parse_Error(message, line, col, pos);
269 | };
270 |
271 | function is_token(token, type, val) {
272 | return token.type == type && (val == null || token.value == val);
273 | };
274 |
275 | var EX_EOF = {};
276 |
277 | function tokenizer($TEXT) {
278 |
279 | var S = {
280 | text : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, ''),
281 | pos : 0,
282 | tokpos : 0,
283 | line : 0,
284 | tokline : 0,
285 | col : 0,
286 | tokcol : 0,
287 | newline_before : false,
288 | regex_allowed : false,
289 | comments_before : []
290 | };
291 |
292 | function peek() { return S.text.charAt(S.pos); };
293 |
294 | function next(signal_eof, in_string) {
295 | var ch = S.text.charAt(S.pos++);
296 | if (signal_eof && !ch)
297 | throw EX_EOF;
298 | if (ch == "\n") {
299 | S.newline_before = S.newline_before || !in_string;
300 | ++S.line;
301 | S.col = 0;
302 | } else {
303 | ++S.col;
304 | }
305 | return ch;
306 | };
307 |
308 | function eof() {
309 | return !S.peek();
310 | };
311 |
312 | function find(what, signal_eof) {
313 | var pos = S.text.indexOf(what, S.pos);
314 | if (signal_eof && pos == -1) throw EX_EOF;
315 | return pos;
316 | };
317 |
318 | function start_token() {
319 | S.tokline = S.line;
320 | S.tokcol = S.col;
321 | S.tokpos = S.pos;
322 | };
323 |
324 | function token(type, value, is_comment) {
325 | S.regex_allowed = ((type == "operator" && !HOP(UNARY_POSTFIX, value)) ||
326 | (type == "keyword" && HOP(KEYWORDS_BEFORE_EXPRESSION, value)) ||
327 | (type == "punc" && HOP(PUNC_BEFORE_EXPRESSION, value)));
328 | var ret = {
329 | type : type,
330 | value : value,
331 | line : S.tokline,
332 | col : S.tokcol,
333 | pos : S.tokpos,
334 | endpos : S.pos,
335 | nlb : S.newline_before
336 | };
337 | if (!is_comment) {
338 | ret.comments_before = S.comments_before;
339 | S.comments_before = [];
340 | }
341 | S.newline_before = false;
342 | return ret;
343 | };
344 |
345 | function skip_whitespace() {
346 | while (HOP(WHITESPACE_CHARS, peek()))
347 | next();
348 | };
349 |
350 | function read_while(pred) {
351 | var ret = "", ch = peek(), i = 0;
352 | while (ch && pred(ch, i++)) {
353 | ret += next();
354 | ch = peek();
355 | }
356 | return ret;
357 | };
358 |
359 | function parse_error(err) {
360 | js_error(err, S.tokline, S.tokcol, S.tokpos);
361 | };
362 |
363 | function read_num(prefix) {
364 | var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
365 | var num = read_while(function(ch, i){
366 | if (ch == "x" || ch == "X") {
367 | if (has_x) return false;
368 | return has_x = true;
369 | }
370 | if (!has_x && (ch == "E" || ch == "e")) {
371 | if (has_e) return false;
372 | return has_e = after_e = true;
373 | }
374 | if (ch == "-") {
375 | if (after_e || (i == 0 && !prefix)) return true;
376 | return false;
377 | }
378 | if (ch == "+") return after_e;
379 | after_e = false;
380 | if (ch == ".") {
381 | if (!has_dot && !has_x)
382 | return has_dot = true;
383 | return false;
384 | }
385 | return is_alphanumeric_char(ch);
386 | });
387 | if (prefix)
388 | num = prefix + num;
389 | var valid = parse_js_number(num);
390 | if (!isNaN(valid)) {
391 | return token("num", valid);
392 | } else {
393 | parse_error("Invalid syntax: " + num);
394 | }
395 | };
396 |
397 | function read_escaped_char(in_string) {
398 | var ch = next(true, in_string);
399 | switch (ch) {
400 | case "n" : return "\n";
401 | case "r" : return "\r";
402 | case "t" : return "\t";
403 | case "b" : return "\b";
404 | case "v" : return "\u000b";
405 | case "f" : return "\f";
406 | case "0" : return "\0";
407 | case "x" : return String.fromCharCode(hex_bytes(2));
408 | case "u" : return String.fromCharCode(hex_bytes(4));
409 | case "\n": return "";
410 | default : return ch;
411 | }
412 | };
413 |
414 | function hex_bytes(n) {
415 | var num = 0;
416 | for (; n > 0; --n) {
417 | var digit = parseInt(next(true), 16);
418 | if (isNaN(digit))
419 | parse_error("Invalid hex-character pattern in string");
420 | num = (num << 4) | digit;
421 | }
422 | return num;
423 | };
424 |
425 | function read_string() {
426 | return with_eof_error("Unterminated string constant", function(){
427 | var quote = next(), ret = "";
428 | for (;;) {
429 | var ch = next(true);
430 | if (ch == "\\") {
431 | // read OctalEscapeSequence (XXX: deprecated if "strict mode")
432 | // https://github.com/mishoo/UglifyJS/issues/178
433 | var octal_len = 0, first = null;
434 | ch = read_while(function(ch){
435 | if (ch >= "0" && ch <= "7") {
436 | if (!first) {
437 | first = ch;
438 | return ++octal_len;
439 | }
440 | else if (first <= "3" && octal_len <= 2) return ++octal_len;
441 | else if (first >= "4" && octal_len <= 1) return ++octal_len;
442 | }
443 | return false;
444 | });
445 | if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
446 | else ch = read_escaped_char(true);
447 | }
448 | else if (ch == quote) break;
449 | ret += ch;
450 | }
451 | return token("string", ret);
452 | });
453 | };
454 |
455 | function read_line_comment() {
456 | next();
457 | var i = find("\n"), ret;
458 | if (i == -1) {
459 | ret = S.text.substr(S.pos);
460 | S.pos = S.text.length;
461 | } else {
462 | ret = S.text.substring(S.pos, i);
463 | S.pos = i;
464 | }
465 | return token("comment1", ret, true);
466 | };
467 |
468 | function read_multiline_comment() {
469 | next();
470 | return with_eof_error("Unterminated multiline comment", function(){
471 | var i = find("*/", true),
472 | text = S.text.substring(S.pos, i);
473 | S.pos = i + 2;
474 | S.line += text.split("\n").length - 1;
475 | S.newline_before = text.indexOf("\n") >= 0;
476 |
477 | // https://github.com/mishoo/UglifyJS/issues/#issue/100
478 | if (/^@cc_on/i.test(text)) {
479 | warn("WARNING: at line " + S.line);
480 | warn("*** Found \"conditional comment\": " + text);
481 | warn("*** UglifyJS DISCARDS ALL COMMENTS. This means your code might no longer work properly in Internet Explorer.");
482 | }
483 |
484 | return token("comment2", text, true);
485 | });
486 | };
487 |
488 | function read_name() {
489 | var backslash = false, name = "", ch;
490 | while ((ch = peek()) != null) {
491 | if (!backslash) {
492 | if (ch == "\\") backslash = true, next();
493 | else if (is_identifier_char(ch)) name += next();
494 | else break;
495 | }
496 | else {
497 | if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
498 | ch = read_escaped_char();
499 | if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
500 | name += ch;
501 | backslash = false;
502 | }
503 | }
504 | return name;
505 | };
506 |
507 | function read_regexp(regexp) {
508 | return with_eof_error("Unterminated regular expression", function(){
509 | var prev_backslash = false, ch, in_class = false;
510 | while ((ch = next(true))) if (prev_backslash) {
511 | regexp += "\\" + ch;
512 | prev_backslash = false;
513 | } else if (ch == "[") {
514 | in_class = true;
515 | regexp += ch;
516 | } else if (ch == "]" && in_class) {
517 | in_class = false;
518 | regexp += ch;
519 | } else if (ch == "/" && !in_class) {
520 | break;
521 | } else if (ch == "\\") {
522 | prev_backslash = true;
523 | } else {
524 | regexp += ch;
525 | }
526 | var mods = read_name();
527 | return token("regexp", [ regexp, mods ]);
528 | });
529 | };
530 |
531 | function read_operator(prefix) {
532 | function grow(op) {
533 | if (!peek()) return op;
534 | var bigger = op + peek();
535 | if (HOP(OPERATORS, bigger)) {
536 | next();
537 | return grow(bigger);
538 | } else {
539 | return op;
540 | }
541 | };
542 | return token("operator", grow(prefix || next()));
543 | };
544 |
545 | function handle_slash() {
546 | next();
547 | var regex_allowed = S.regex_allowed;
548 | switch (peek()) {
549 | case "/":
550 | S.comments_before.push(read_line_comment());
551 | S.regex_allowed = regex_allowed;
552 | return next_token();
553 | case "*":
554 | S.comments_before.push(read_multiline_comment());
555 | S.regex_allowed = regex_allowed;
556 | return next_token();
557 | }
558 | return S.regex_allowed ? read_regexp("") : read_operator("/");
559 | };
560 |
561 | function handle_dot() {
562 | next();
563 | return is_digit(peek())
564 | ? read_num(".")
565 | : token("punc", ".");
566 | };
567 |
568 | function read_word() {
569 | var word = read_name();
570 | return !HOP(KEYWORDS, word)
571 | ? token("name", word)
572 | : HOP(OPERATORS, word)
573 | ? token("operator", word)
574 | : HOP(KEYWORDS_ATOM, word)
575 | ? token("atom", word)
576 | : token("keyword", word);
577 | };
578 |
579 | function with_eof_error(eof_error, cont) {
580 | try {
581 | return cont();
582 | } catch(ex) {
583 | if (ex === EX_EOF) parse_error(eof_error);
584 | else throw ex;
585 | }
586 | };
587 |
588 | function next_token(force_regexp) {
589 | if (force_regexp != null)
590 | return read_regexp(force_regexp);
591 | skip_whitespace();
592 | start_token();
593 | var ch = peek();
594 | if (!ch) return token("eof");
595 | if (is_digit(ch)) return read_num();
596 | if (ch == '"' || ch == "'") return read_string();
597 | if (HOP(PUNC_CHARS, ch)) return token("punc", next());
598 | if (ch == ".") return handle_dot();
599 | if (ch == "/") return handle_slash();
600 | if (HOP(OPERATOR_CHARS, ch)) return read_operator();
601 | if (ch == "\\" || is_identifier_start(ch)) return read_word();
602 | parse_error("Unexpected character '" + ch + "'");
603 | };
604 |
605 | next_token.context = function(nc) {
606 | if (nc) S = nc;
607 | return S;
608 | };
609 |
610 | return next_token;
611 |
612 | };
613 |
614 | /* -----[ Parser (constants) ]----- */
615 |
616 | var UNARY_PREFIX = array_to_hash([
617 | "typeof",
618 | "void",
619 | "delete",
620 | "--",
621 | "++",
622 | "!",
623 | "~",
624 | "-",
625 | "+"
626 | ]);
627 |
628 | var UNARY_POSTFIX = array_to_hash([ "--", "++" ]);
629 |
630 | var ASSIGNMENT = (function(a, ret, i){
631 | while (i < a.length) {
632 | ret[a[i]] = a[i].substr(0, a[i].length - 1);
633 | i++;
634 | }
635 | return ret;
636 | })(
637 | ["+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&="],
638 | { "=": true },
639 | 0
640 | );
641 |
642 | var PRECEDENCE = (function(a, ret){
643 | for (var i = 0, n = 1; i < a.length; ++i, ++n) {
644 | var b = a[i];
645 | for (var j = 0; j < b.length; ++j) {
646 | ret[b[j]] = n;
647 | }
648 | }
649 | return ret;
650 | })(
651 | [
652 | ["||"],
653 | ["&&"],
654 | ["|"],
655 | ["^"],
656 | ["&"],
657 | ["==", "===", "!=", "!=="],
658 | ["<", ">", "<=", ">=", "in", "instanceof"],
659 | [">>", "<<", ">>>"],
660 | ["+", "-"],
661 | ["*", "/", "%"]
662 | ],
663 | {}
664 | );
665 |
666 | var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
667 |
668 | var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
669 |
670 | /* -----[ Parser ]----- */
671 |
672 | function NodeWithToken(str, start, end) {
673 | this.name = str;
674 | this.start = start;
675 | this.end = end;
676 | };
677 |
678 | NodeWithToken.prototype.toString = function() { return this.name; };
679 |
680 | function parse($TEXT, exigent_mode, embed_tokens) {
681 |
682 | var S = {
683 | input : typeof $TEXT == "string" ? tokenizer($TEXT, true) : $TEXT,
684 | token : null,
685 | prev : null,
686 | peeked : null,
687 | in_function : 0,
688 | in_loop : 0,
689 | labels : []
690 | };
691 |
692 | S.token = next();
693 |
694 | function is(type, value) {
695 | return is_token(S.token, type, value);
696 | };
697 |
698 | function peek() { return S.peeked || (S.peeked = S.input()); };
699 |
700 | function next() {
701 | S.prev = S.token;
702 | if (S.peeked) {
703 | S.token = S.peeked;
704 | S.peeked = null;
705 | } else {
706 | S.token = S.input();
707 | }
708 | return S.token;
709 | };
710 |
711 | function prev() {
712 | return S.prev;
713 | };
714 |
715 | function croak(msg, line, col, pos) {
716 | var ctx = S.input.context();
717 | js_error(msg,
718 | line != null ? line : ctx.tokline,
719 | col != null ? col : ctx.tokcol,
720 | pos != null ? pos : ctx.tokpos);
721 | };
722 |
723 | function token_error(token, msg) {
724 | croak(msg, token.line, token.col);
725 | };
726 |
727 | function unexpected(token) {
728 | if (token == null)
729 | token = S.token;
730 | token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
731 | };
732 |
733 | function expect_token(type, val) {
734 | if (is(type, val)) {
735 | return next();
736 | }
737 | token_error(S.token, "Unexpected token " + S.token.type + ", expected " + type);
738 | };
739 |
740 | function expect(punc) { return expect_token("punc", punc); };
741 |
742 | function can_insert_semicolon() {
743 | return !exigent_mode && (
744 | S.token.nlb || is("eof") || is("punc", "}")
745 | );
746 | };
747 |
748 | function semicolon() {
749 | if (is("punc", ";")) next();
750 | else if (!can_insert_semicolon()) unexpected();
751 | };
752 |
753 | function as() {
754 | return slice(arguments);
755 | };
756 |
757 | function parenthesised() {
758 | expect("(");
759 | var ex = expression();
760 | expect(")");
761 | return ex;
762 | };
763 |
764 | function add_tokens(str, start, end) {
765 | return str instanceof NodeWithToken ? str : new NodeWithToken(str, start, end);
766 | };
767 |
768 | function maybe_embed_tokens(parser) {
769 | if (embed_tokens) return function() {
770 | var start = S.token;
771 | var ast = parser.apply(this, arguments);
772 | ast[0] = add_tokens(ast[0], start, prev());
773 | return ast;
774 | };
775 | else return parser;
776 | };
777 |
778 | var statement = maybe_embed_tokens(function() {
779 | if (is("operator", "/") || is("operator", "/=")) {
780 | S.peeked = null;
781 | S.token = S.input(S.token.value.substr(1)); // force regexp
782 | }
783 | switch (S.token.type) {
784 | case "num":
785 | case "string":
786 | case "regexp":
787 | case "operator":
788 | case "atom":
789 | return simple_statement();
790 |
791 | case "name":
792 | return is_token(peek(), "punc", ":")
793 | ? labeled_statement(prog1(S.token.value, next, next))
794 | : simple_statement();
795 |
796 | case "punc":
797 | switch (S.token.value) {
798 | case "{":
799 | return as("block", block_());
800 | case "[":
801 | case "(":
802 | return simple_statement();
803 | case ";":
804 | next();
805 | return as("block");
806 | default:
807 | unexpected();
808 | }
809 |
810 | case "keyword":
811 | switch (prog1(S.token.value, next)) {
812 | case "break":
813 | return break_cont("break");
814 |
815 | case "continue":
816 | return break_cont("continue");
817 |
818 | case "debugger":
819 | semicolon();
820 | return as("debugger");
821 |
822 | case "do":
823 | return (function(body){
824 | expect_token("keyword", "while");
825 | return as("do", prog1(parenthesised, semicolon), body);
826 | })(in_loop(statement));
827 |
828 | case "for":
829 | return for_();
830 |
831 | case "function":
832 | return function_(true);
833 |
834 | case "if":
835 | return if_();
836 |
837 | case "return":
838 | if (S.in_function == 0)
839 | croak("'return' outside of function");
840 | return as("return",
841 | is("punc", ";")
842 | ? (next(), null)
843 | : can_insert_semicolon()
844 | ? null
845 | : prog1(expression, semicolon));
846 |
847 | case "switch":
848 | return as("switch", parenthesised(), switch_block_());
849 |
850 | case "throw":
851 | if (S.token.nlb)
852 | croak("Illegal newline after 'throw'");
853 | return as("throw", prog1(expression, semicolon));
854 |
855 | case "try":
856 | return try_();
857 |
858 | case "var":
859 | return prog1(var_, semicolon);
860 |
861 | case "const":
862 | return prog1(const_, semicolon);
863 |
864 | case "while":
865 | return as("while", parenthesised(), in_loop(statement));
866 |
867 | case "with":
868 | return as("with", parenthesised(), statement());
869 |
870 | default:
871 | unexpected();
872 | }
873 | }
874 | });
875 |
876 | function labeled_statement(label) {
877 | S.labels.push(label);
878 | var start = S.token, stat = statement();
879 | if (exigent_mode && !HOP(STATEMENTS_WITH_LABELS, stat[0]))
880 | unexpected(start);
881 | S.labels.pop();
882 | return as("label", label, stat);
883 | };
884 |
885 | function simple_statement() {
886 | return as("stat", prog1(expression, semicolon));
887 | };
888 |
889 | function break_cont(type) {
890 | var name;
891 | if (!can_insert_semicolon()) {
892 | name = is("name") ? S.token.value : null;
893 | }
894 | if (name != null) {
895 | next();
896 | if (!member(name, S.labels))
897 | croak("Label " + name + " without matching loop or statement");
898 | }
899 | else if (S.in_loop == 0)
900 | croak(type + " not inside a loop or switch");
901 | semicolon();
902 | return as(type, name);
903 | };
904 |
905 | function for_() {
906 | expect("(");
907 | var init = null;
908 | if (!is("punc", ";")) {
909 | init = is("keyword", "var")
910 | ? (next(), var_(true))
911 | : expression(true, true);
912 | if (is("operator", "in"))
913 | return for_in(init);
914 | }
915 | return regular_for(init);
916 | };
917 |
918 | function regular_for(init) {
919 | expect(";");
920 | var test = is("punc", ";") ? null : expression();
921 | expect(";");
922 | var step = is("punc", ")") ? null : expression();
923 | expect(")");
924 | return as("for", init, test, step, in_loop(statement));
925 | };
926 |
927 | function for_in(init) {
928 | var lhs = init[0] == "var" ? as("name", init[1][0]) : init;
929 | next();
930 | var obj = expression();
931 | expect(")");
932 | return as("for-in", init, lhs, obj, in_loop(statement));
933 | };
934 |
935 | var function_ = function(in_statement) {
936 | var name = is("name") ? prog1(S.token.value, next) : null;
937 | if (in_statement && !name)
938 | unexpected();
939 | expect("(");
940 | return as(in_statement ? "defun" : "function",
941 | name,
942 | // arguments
943 | (function(first, a){
944 | while (!is("punc", ")")) {
945 | if (first) first = false; else expect(",");
946 | if (!is("name")) unexpected();
947 | a.push(S.token.value);
948 | next();
949 | }
950 | next();
951 | return a;
952 | })(true, []),
953 | // body
954 | (function(){
955 | ++S.in_function;
956 | var loop = S.in_loop;
957 | S.in_loop = 0;
958 | var a = block_();
959 | --S.in_function;
960 | S.in_loop = loop;
961 | return a;
962 | })());
963 | };
964 |
965 | function if_() {
966 | var cond = parenthesised(), body = statement(), belse;
967 | if (is("keyword", "else")) {
968 | next();
969 | belse = statement();
970 | }
971 | return as("if", cond, body, belse);
972 | };
973 |
974 | function block_() {
975 | expect("{");
976 | var a = [];
977 | while (!is("punc", "}")) {
978 | if (is("eof")) unexpected();
979 | a.push(statement());
980 | }
981 | next();
982 | return a;
983 | };
984 |
985 | var switch_block_ = curry(in_loop, function(){
986 | expect("{");
987 | var a = [], cur = null;
988 | while (!is("punc", "}")) {
989 | if (is("eof")) unexpected();
990 | if (is("keyword", "case")) {
991 | next();
992 | cur = [];
993 | a.push([ expression(), cur ]);
994 | expect(":");
995 | }
996 | else if (is("keyword", "default")) {
997 | next();
998 | expect(":");
999 | cur = [];
1000 | a.push([ null, cur ]);
1001 | }
1002 | else {
1003 | if (!cur) unexpected();
1004 | cur.push(statement());
1005 | }
1006 | }
1007 | next();
1008 | return a;
1009 | });
1010 |
1011 | function try_() {
1012 | var body = block_(), bcatch, bfinally;
1013 | if (is("keyword", "catch")) {
1014 | next();
1015 | expect("(");
1016 | if (!is("name"))
1017 | croak("Name expected");
1018 | var name = S.token.value;
1019 | next();
1020 | expect(")");
1021 | bcatch = [ name, block_() ];
1022 | }
1023 | if (is("keyword", "finally")) {
1024 | next();
1025 | bfinally = block_();
1026 | }
1027 | if (!bcatch && !bfinally)
1028 | croak("Missing catch/finally blocks");
1029 | return as("try", body, bcatch, bfinally);
1030 | };
1031 |
1032 | function vardefs(no_in) {
1033 | var a = [];
1034 | for (;;) {
1035 | if (!is("name"))
1036 | unexpected();
1037 | var name = S.token.value;
1038 | next();
1039 | if (is("operator", "=")) {
1040 | next();
1041 | a.push([ name, expression(false, no_in) ]);
1042 | } else {
1043 | a.push([ name ]);
1044 | }
1045 | if (!is("punc", ","))
1046 | break;
1047 | next();
1048 | }
1049 | return a;
1050 | };
1051 |
1052 | function var_(no_in) {
1053 | return as("var", vardefs(no_in));
1054 | };
1055 |
1056 | function const_() {
1057 | return as("const", vardefs());
1058 | };
1059 |
1060 | function new_() {
1061 | var newexp = expr_atom(false), args;
1062 | if (is("punc", "(")) {
1063 | next();
1064 | args = expr_list(")");
1065 | } else {
1066 | args = [];
1067 | }
1068 | return subscripts(as("new", newexp, args), true);
1069 | };
1070 |
1071 | var expr_atom = maybe_embed_tokens(function(allow_calls) {
1072 | if (is("operator", "new")) {
1073 | next();
1074 | return new_();
1075 | }
1076 | if (is("punc")) {
1077 | switch (S.token.value) {
1078 | case "(":
1079 | next();
1080 | return subscripts(prog1(expression, curry(expect, ")")), allow_calls);
1081 | case "[":
1082 | next();
1083 | return subscripts(array_(), allow_calls);
1084 | case "{":
1085 | next();
1086 | return subscripts(object_(), allow_calls);
1087 | }
1088 | unexpected();
1089 | }
1090 | if (is("keyword", "function")) {
1091 | next();
1092 | return subscripts(function_(false), allow_calls);
1093 | }
1094 | if (HOP(ATOMIC_START_TOKEN, S.token.type)) {
1095 | var atom = S.token.type == "regexp"
1096 | ? as("regexp", S.token.value[0], S.token.value[1])
1097 | : as(S.token.type, S.token.value);
1098 | return subscripts(prog1(atom, next), allow_calls);
1099 | }
1100 | unexpected();
1101 | });
1102 |
1103 | function expr_list(closing, allow_trailing_comma, allow_empty) {
1104 | var first = true, a = [];
1105 | while (!is("punc", closing)) {
1106 | if (first) first = false; else expect(",");
1107 | if (allow_trailing_comma && is("punc", closing)) break;
1108 | if (is("punc", ",") && allow_empty) {
1109 | a.push([ "atom", "undefined" ]);
1110 | } else {
1111 | a.push(expression(false));
1112 | }
1113 | }
1114 | next();
1115 | return a;
1116 | };
1117 |
1118 | function array_() {
1119 | return as("array", expr_list("]", !exigent_mode, true));
1120 | };
1121 |
1122 | function object_() {
1123 | var first = true, a = [];
1124 | while (!is("punc", "}")) {
1125 | if (first) first = false; else expect(",");
1126 | if (!exigent_mode && is("punc", "}"))
1127 | // allow trailing comma
1128 | break;
1129 | var type = S.token.type;
1130 | var name = as_property_name();
1131 | if (type == "name" && (name == "get" || name == "set") && !is("punc", ":")) {
1132 | a.push([ as_name(), function_(false), name ]);
1133 | } else {
1134 | expect(":");
1135 | a.push([ name, expression(false) ]);
1136 | }
1137 | }
1138 | next();
1139 | return as("object", a);
1140 | };
1141 |
1142 | function as_property_name() {
1143 | switch (S.token.type) {
1144 | case "num":
1145 | case "string":
1146 | return prog1(S.token.value, next);
1147 | }
1148 | return as_name();
1149 | };
1150 |
1151 | function as_name() {
1152 | switch (S.token.type) {
1153 | case "name":
1154 | case "operator":
1155 | case "keyword":
1156 | case "atom":
1157 | return prog1(S.token.value, next);
1158 | default:
1159 | unexpected();
1160 | }
1161 | };
1162 |
1163 | function subscripts(expr, allow_calls) {
1164 | if (is("punc", ".")) {
1165 | next();
1166 | return subscripts(as("dot", expr, as_name()), allow_calls);
1167 | }
1168 | if (is("punc", "[")) {
1169 | next();
1170 | return subscripts(as("sub", expr, prog1(expression, curry(expect, "]"))), allow_calls);
1171 | }
1172 | if (allow_calls && is("punc", "(")) {
1173 | next();
1174 | return subscripts(as("call", expr, expr_list(")")), true);
1175 | }
1176 | return expr;
1177 | };
1178 |
1179 | function maybe_unary(allow_calls) {
1180 | if (is("operator") && HOP(UNARY_PREFIX, S.token.value)) {
1181 | return make_unary("unary-prefix",
1182 | prog1(S.token.value, next),
1183 | maybe_unary(allow_calls));
1184 | }
1185 | var val = expr_atom(allow_calls);
1186 | while (is("operator") && HOP(UNARY_POSTFIX, S.token.value) && !S.token.nlb) {
1187 | val = make_unary("unary-postfix", S.token.value, val);
1188 | next();
1189 | }
1190 | return val;
1191 | };
1192 |
1193 | function make_unary(tag, op, expr) {
1194 | if ((op == "++" || op == "--") && !is_assignable(expr))
1195 | croak("Invalid use of " + op + " operator");
1196 | return as(tag, op, expr);
1197 | };
1198 |
1199 | function expr_op(left, min_prec, no_in) {
1200 | var op = is("operator") ? S.token.value : null;
1201 | if (op && op == "in" && no_in) op = null;
1202 | var prec = op != null ? PRECEDENCE[op] : null;
1203 | if (prec != null && prec > min_prec) {
1204 | next();
1205 | var right = expr_op(maybe_unary(true), prec, no_in);
1206 | return expr_op(as("binary", op, left, right), min_prec, no_in);
1207 | }
1208 | return left;
1209 | };
1210 |
1211 | function expr_ops(no_in) {
1212 | return expr_op(maybe_unary(true), 0, no_in);
1213 | };
1214 |
1215 | function maybe_conditional(no_in) {
1216 | var expr = expr_ops(no_in);
1217 | if (is("operator", "?")) {
1218 | next();
1219 | var yes = expression(false);
1220 | expect(":");
1221 | return as("conditional", expr, yes, expression(false, no_in));
1222 | }
1223 | return expr;
1224 | };
1225 |
1226 | function is_assignable(expr) {
1227 | if (!exigent_mode) return true;
1228 | switch (expr[0]+"") {
1229 | case "dot":
1230 | case "sub":
1231 | case "new":
1232 | case "call":
1233 | return true;
1234 | case "name":
1235 | return expr[1] != "this";
1236 | }
1237 | };
1238 |
1239 | function maybe_assign(no_in) {
1240 | var left = maybe_conditional(no_in), val = S.token.value;
1241 | if (is("operator") && HOP(ASSIGNMENT, val)) {
1242 | if (is_assignable(left)) {
1243 | next();
1244 | return as("assign", ASSIGNMENT[val], left, maybe_assign(no_in));
1245 | }
1246 | croak("Invalid assignment");
1247 | }
1248 | return left;
1249 | };
1250 |
1251 | var expression = maybe_embed_tokens(function(commas, no_in) {
1252 | if (arguments.length == 0)
1253 | commas = true;
1254 | var expr = maybe_assign(no_in);
1255 | if (commas && is("punc", ",")) {
1256 | next();
1257 | return as("seq", expr, expression(true, no_in));
1258 | }
1259 | return expr;
1260 | });
1261 |
1262 | function in_loop(cont) {
1263 | try {
1264 | ++S.in_loop;
1265 | return cont();
1266 | } finally {
1267 | --S.in_loop;
1268 | }
1269 | };
1270 |
1271 | return as("toplevel", (function(a){
1272 | while (!is("eof"))
1273 | a.push(statement());
1274 | return a;
1275 | })([]));
1276 |
1277 | };
1278 |
1279 | /* -----[ Utilities ]----- */
1280 |
1281 | function curry(f) {
1282 | var args = slice(arguments, 1);
1283 | return function() { return f.apply(this, args.concat(slice(arguments))); };
1284 | };
1285 |
1286 | function prog1(ret) {
1287 | if (ret instanceof Function)
1288 | ret = ret();
1289 | for (var i = 1, n = arguments.length; --n > 0; ++i)
1290 | arguments[i]();
1291 | return ret;
1292 | };
1293 |
1294 | function array_to_hash(a) {
1295 | var ret = {};
1296 | for (var i = 0; i < a.length; ++i)
1297 | ret[a[i]] = true;
1298 | return ret;
1299 | };
1300 |
1301 | function slice(a, start) {
1302 | return Array.prototype.slice.call(a, start || 0);
1303 | };
1304 |
1305 | function characters(str) {
1306 | return str.split("");
1307 | };
1308 |
1309 | function member(name, array) {
1310 | for (var i = array.length; --i >= 0;)
1311 | if (array[i] == name)
1312 | return true;
1313 | return false;
1314 | };
1315 |
1316 | function HOP(obj, prop) {
1317 | return Object.prototype.hasOwnProperty.call(obj, prop);
1318 | };
1319 |
1320 | var warn = function() {};
1321 |
1322 | /* -----[ Exports ]----- */
1323 |
1324 | exports.tokenizer = tokenizer;
1325 | exports.parse = parse;
1326 | exports.slice = slice;
1327 | exports.curry = curry;
1328 | exports.member = member;
1329 | exports.array_to_hash = array_to_hash;
1330 | exports.PRECEDENCE = PRECEDENCE;
1331 | exports.KEYWORDS_ATOM = KEYWORDS_ATOM;
1332 | exports.RESERVED_WORDS = RESERVED_WORDS;
1333 | exports.KEYWORDS = KEYWORDS;
1334 | exports.ATOMIC_START_TOKEN = ATOMIC_START_TOKEN;
1335 | exports.OPERATORS = OPERATORS;
1336 | exports.is_alphanumeric_char = is_alphanumeric_char;
1337 | exports.set_logger = function(logger) {
1338 | warn = logger;
1339 | };
1340 |
--------------------------------------------------------------------------------
/tools/jasmine/jasmine.js:
--------------------------------------------------------------------------------
1 | var isCommonJS = typeof window == "undefined";
2 |
3 | /**
4 | * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
5 | *
6 | * @namespace
7 | */
8 | var jasmine = {};
9 | if (isCommonJS) exports.jasmine = jasmine;
10 | /**
11 | * @private
12 | */
13 | jasmine.unimplementedMethod_ = function() {
14 | throw new Error("unimplemented method");
15 | };
16 |
17 | /**
18 | * Use jasmine.undefined instead of undefined, since undefined is just
19 | * a plain old variable and may be redefined by somebody else.
20 | *
21 | * @private
22 | */
23 | jasmine.undefined = jasmine.___undefined___;
24 |
25 | /**
26 | * Show diagnostic messages in the console if set to true
27 | *
28 | */
29 | jasmine.VERBOSE = false;
30 |
31 | /**
32 | * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.
33 | *
34 | */
35 | jasmine.DEFAULT_UPDATE_INTERVAL = 250;
36 |
37 | /**
38 | * Default timeout interval in milliseconds for waitsFor() blocks.
39 | */
40 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
41 |
42 | jasmine.getGlobal = function() {
43 | function getGlobal() {
44 | return this;
45 | }
46 |
47 | return getGlobal();
48 | };
49 |
50 | /**
51 | * Allows for bound functions to be compared. Internal use only.
52 | *
53 | * @ignore
54 | * @private
55 | * @param base {Object} bound 'this' for the function
56 | * @param name {Function} function to find
57 | */
58 | jasmine.bindOriginal_ = function(base, name) {
59 | var original = base[name];
60 | if (original.apply) {
61 | return function() {
62 | return original.apply(base, arguments);
63 | };
64 | } else {
65 | // IE support
66 | return jasmine.getGlobal()[name];
67 | }
68 | };
69 |
70 | jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
71 | jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
72 | jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
73 | jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
74 |
75 | jasmine.MessageResult = function(values) {
76 | this.type = 'log';
77 | this.values = values;
78 | this.trace = new Error(); // todo: test better
79 | };
80 |
81 | jasmine.MessageResult.prototype.toString = function() {
82 | var text = "";
83 | for (var i = 0; i < this.values.length; i++) {
84 | if (i > 0) text += " ";
85 | if (jasmine.isString_(this.values[i])) {
86 | text += this.values[i];
87 | } else {
88 | text += jasmine.pp(this.values[i]);
89 | }
90 | }
91 | return text;
92 | };
93 |
94 | jasmine.ExpectationResult = function(params) {
95 | this.type = 'expect';
96 | this.matcherName = params.matcherName;
97 | this.passed_ = params.passed;
98 | this.expected = params.expected;
99 | this.actual = params.actual;
100 | this.message = this.passed_ ? 'Passed.' : params.message;
101 |
102 | var trace = (params.trace || new Error(this.message));
103 | this.trace = this.passed_ ? '' : trace;
104 | };
105 |
106 | jasmine.ExpectationResult.prototype.toString = function () {
107 | return this.message;
108 | };
109 |
110 | jasmine.ExpectationResult.prototype.passed = function () {
111 | return this.passed_;
112 | };
113 |
114 | /**
115 | * Getter for the Jasmine environment. Ensures one gets created
116 | */
117 | jasmine.getEnv = function() {
118 | var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
119 | return env;
120 | };
121 |
122 | /**
123 | * @ignore
124 | * @private
125 | * @param value
126 | * @returns {Boolean}
127 | */
128 | jasmine.isArray_ = function(value) {
129 | return jasmine.isA_("Array", value);
130 | };
131 |
132 | /**
133 | * @ignore
134 | * @private
135 | * @param value
136 | * @returns {Boolean}
137 | */
138 | jasmine.isString_ = function(value) {
139 | return jasmine.isA_("String", value);
140 | };
141 |
142 | /**
143 | * @ignore
144 | * @private
145 | * @param value
146 | * @returns {Boolean}
147 | */
148 | jasmine.isNumber_ = function(value) {
149 | return jasmine.isA_("Number", value);
150 | };
151 |
152 | /**
153 | * @ignore
154 | * @private
155 | * @param {String} typeName
156 | * @param value
157 | * @returns {Boolean}
158 | */
159 | jasmine.isA_ = function(typeName, value) {
160 | return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
161 | };
162 |
163 | /**
164 | * Pretty printer for expecations. Takes any object and turns it into a human-readable string.
165 | *
166 | * @param value {Object} an object to be outputted
167 | * @returns {String}
168 | */
169 | jasmine.pp = function(value) {
170 | var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
171 | stringPrettyPrinter.format(value);
172 | return stringPrettyPrinter.string;
173 | };
174 |
175 | /**
176 | * Returns true if the object is a DOM Node.
177 | *
178 | * @param {Object} obj object to check
179 | * @returns {Boolean}
180 | */
181 | jasmine.isDomNode = function(obj) {
182 | return obj.nodeType > 0;
183 | };
184 |
185 | /**
186 | * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter.
187 | *
188 | * @example
189 | * // don't care about which function is passed in, as long as it's a function
190 | * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
191 | *
192 | * @param {Class} clazz
193 | * @returns matchable object of the type clazz
194 | */
195 | jasmine.any = function(clazz) {
196 | return new jasmine.Matchers.Any(clazz);
197 | };
198 |
199 | /**
200 | * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
201 | *
202 | * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine
203 | * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
204 | *
205 | * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
206 | *
207 | * Spies are torn down at the end of every spec.
208 | *
209 | * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
210 | *
211 | * @example
212 | * // a stub
213 | * var myStub = jasmine.createSpy('myStub'); // can be used anywhere
214 | *
215 | * // spy example
216 | * var foo = {
217 | * not: function(bool) { return !bool; }
218 | * }
219 | *
220 | * // actual foo.not will not be called, execution stops
221 | * spyOn(foo, 'not');
222 |
223 | // foo.not spied upon, execution will continue to implementation
224 | * spyOn(foo, 'not').andCallThrough();
225 | *
226 | * // fake example
227 | * var foo = {
228 | * not: function(bool) { return !bool; }
229 | * }
230 | *
231 | * // foo.not(val) will return val
232 | * spyOn(foo, 'not').andCallFake(function(value) {return value;});
233 | *
234 | * // mock example
235 | * foo.not(7 == 7);
236 | * expect(foo.not).toHaveBeenCalled();
237 | * expect(foo.not).toHaveBeenCalledWith(true);
238 | *
239 | * @constructor
240 | * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
241 | * @param {String} name
242 | */
243 | jasmine.Spy = function(name) {
244 | /**
245 | * The name of the spy, if provided.
246 | */
247 | this.identity = name || 'unknown';
248 | /**
249 | * Is this Object a spy?
250 | */
251 | this.isSpy = true;
252 | /**
253 | * The actual function this spy stubs.
254 | */
255 | this.plan = function() {
256 | };
257 | /**
258 | * Tracking of the most recent call to the spy.
259 | * @example
260 | * var mySpy = jasmine.createSpy('foo');
261 | * mySpy(1, 2);
262 | * mySpy.mostRecentCall.args = [1, 2];
263 | */
264 | this.mostRecentCall = {};
265 |
266 | /**
267 | * Holds arguments for each call to the spy, indexed by call count
268 | * @example
269 | * var mySpy = jasmine.createSpy('foo');
270 | * mySpy(1, 2);
271 | * mySpy(7, 8);
272 | * mySpy.mostRecentCall.args = [7, 8];
273 | * mySpy.argsForCall[0] = [1, 2];
274 | * mySpy.argsForCall[1] = [7, 8];
275 | */
276 | this.argsForCall = [];
277 | this.calls = [];
278 | };
279 |
280 | /**
281 | * Tells a spy to call through to the actual implemenatation.
282 | *
283 | * @example
284 | * var foo = {
285 | * bar: function() { // do some stuff }
286 | * }
287 | *
288 | * // defining a spy on an existing property: foo.bar
289 | * spyOn(foo, 'bar').andCallThrough();
290 | */
291 | jasmine.Spy.prototype.andCallThrough = function() {
292 | this.plan = this.originalValue;
293 | return this;
294 | };
295 |
296 | /**
297 | * For setting the return value of a spy.
298 | *
299 | * @example
300 | * // defining a spy from scratch: foo() returns 'baz'
301 | * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
302 | *
303 | * // defining a spy on an existing property: foo.bar() returns 'baz'
304 | * spyOn(foo, 'bar').andReturn('baz');
305 | *
306 | * @param {Object} value
307 | */
308 | jasmine.Spy.prototype.andReturn = function(value) {
309 | this.plan = function() {
310 | return value;
311 | };
312 | return this;
313 | };
314 |
315 | /**
316 | * For throwing an exception when a spy is called.
317 | *
318 | * @example
319 | * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
320 | * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
321 | *
322 | * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
323 | * spyOn(foo, 'bar').andThrow('baz');
324 | *
325 | * @param {String} exceptionMsg
326 | */
327 | jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
328 | this.plan = function() {
329 | throw exceptionMsg;
330 | };
331 | return this;
332 | };
333 |
334 | /**
335 | * Calls an alternate implementation when a spy is called.
336 | *
337 | * @example
338 | * var baz = function() {
339 | * // do some stuff, return something
340 | * }
341 | * // defining a spy from scratch: foo() calls the function baz
342 | * var foo = jasmine.createSpy('spy on foo').andCall(baz);
343 | *
344 | * // defining a spy on an existing property: foo.bar() calls an anonymnous function
345 | * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
346 | *
347 | * @param {Function} fakeFunc
348 | */
349 | jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
350 | this.plan = fakeFunc;
351 | return this;
352 | };
353 |
354 | /**
355 | * Resets all of a spy's the tracking variables so that it can be used again.
356 | *
357 | * @example
358 | * spyOn(foo, 'bar');
359 | *
360 | * foo.bar();
361 | *
362 | * expect(foo.bar.callCount).toEqual(1);
363 | *
364 | * foo.bar.reset();
365 | *
366 | * expect(foo.bar.callCount).toEqual(0);
367 | */
368 | jasmine.Spy.prototype.reset = function() {
369 | this.wasCalled = false;
370 | this.callCount = 0;
371 | this.argsForCall = [];
372 | this.calls = [];
373 | this.mostRecentCall = {};
374 | };
375 |
376 | jasmine.createSpy = function(name) {
377 |
378 | var spyObj = function() {
379 | spyObj.wasCalled = true;
380 | spyObj.callCount++;
381 | var args = jasmine.util.argsToArray(arguments);
382 | spyObj.mostRecentCall.object = this;
383 | spyObj.mostRecentCall.args = args;
384 | spyObj.argsForCall.push(args);
385 | spyObj.calls.push({object: this, args: args});
386 | return spyObj.plan.apply(this, arguments);
387 | };
388 |
389 | var spy = new jasmine.Spy(name);
390 |
391 | for (var prop in spy) {
392 | spyObj[prop] = spy[prop];
393 | }
394 |
395 | spyObj.reset();
396 |
397 | return spyObj;
398 | };
399 |
400 | /**
401 | * Determines whether an object is a spy.
402 | *
403 | * @param {jasmine.Spy|Object} putativeSpy
404 | * @returns {Boolean}
405 | */
406 | jasmine.isSpy = function(putativeSpy) {
407 | return putativeSpy && putativeSpy.isSpy;
408 | };
409 |
410 | /**
411 | * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something
412 | * large in one call.
413 | *
414 | * @param {String} baseName name of spy class
415 | * @param {Array} methodNames array of names of methods to make spies
416 | */
417 | jasmine.createSpyObj = function(baseName, methodNames) {
418 | if (!jasmine.isArray_(methodNames) || methodNames.length === 0) {
419 | throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
420 | }
421 | var obj = {};
422 | for (var i = 0; i < methodNames.length; i++) {
423 | obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
424 | }
425 | return obj;
426 | };
427 |
428 | /**
429 | * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
430 | *
431 | * Be careful not to leave calls to jasmine.log in production code.
432 | */
433 | jasmine.log = function() {
434 | var spec = jasmine.getEnv().currentSpec;
435 | spec.log.apply(spec, arguments);
436 | };
437 |
438 | /**
439 | * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy.
440 | *
441 | * @example
442 | * // spy example
443 | * var foo = {
444 | * not: function(bool) { return !bool; }
445 | * }
446 | * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
447 | *
448 | * @see jasmine.createSpy
449 | * @param obj
450 | * @param methodName
451 | * @returns a Jasmine spy that can be chained with all spy methods
452 | */
453 | var spyOn = function(obj, methodName) {
454 | return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
455 | };
456 | if (isCommonJS) exports.spyOn = spyOn;
457 |
458 | /**
459 | * Creates a Jasmine spec that will be added to the current suite.
460 | *
461 | * // TODO: pending tests
462 | *
463 | * @example
464 | * it('should be true', function() {
465 | * expect(true).toEqual(true);
466 | * });
467 | *
468 | * @param {String} desc description of this specification
469 | * @param {Function} func defines the preconditions and expectations of the spec
470 | */
471 | var it = function(desc, func) {
472 | return jasmine.getEnv().it(desc, func);
473 | };
474 | if (isCommonJS) exports.it = it;
475 |
476 | /**
477 | * Creates a disabled Jasmine spec.
478 | *
479 | * A convenience method that allows existing specs to be disabled temporarily during development.
480 | *
481 | * @param {String} desc description of this specification
482 | * @param {Function} func defines the preconditions and expectations of the spec
483 | */
484 | var xit = function(desc, func) {
485 | return jasmine.getEnv().xit(desc, func);
486 | };
487 | if (isCommonJS) exports.xit = xit;
488 |
489 | /**
490 | * Starts a chain for a Jasmine expectation.
491 | *
492 | * It is passed an Object that is the actual value and should chain to one of the many
493 | * jasmine.Matchers functions.
494 | *
495 | * @param {Object} actual Actual value to test against and expected value
496 | */
497 | var expect = function(actual) {
498 | return jasmine.getEnv().currentSpec.expect(actual);
499 | };
500 | if (isCommonJS) exports.expect = expect;
501 |
502 | /**
503 | * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs.
504 | *
505 | * @param {Function} func Function that defines part of a jasmine spec.
506 | */
507 | var runs = function(func) {
508 | jasmine.getEnv().currentSpec.runs(func);
509 | };
510 | if (isCommonJS) exports.runs = runs;
511 |
512 | /**
513 | * Waits a fixed time period before moving to the next block.
514 | *
515 | * @deprecated Use waitsFor() instead
516 | * @param {Number} timeout milliseconds to wait
517 | */
518 | var waits = function(timeout) {
519 | jasmine.getEnv().currentSpec.waits(timeout);
520 | };
521 | if (isCommonJS) exports.waits = waits;
522 |
523 | /**
524 | * Waits for the latchFunction to return true before proceeding to the next block.
525 | *
526 | * @param {Function} latchFunction
527 | * @param {String} optional_timeoutMessage
528 | * @param {Number} optional_timeout
529 | */
530 | var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
531 | jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
532 | };
533 | if (isCommonJS) exports.waitsFor = waitsFor;
534 |
535 | /**
536 | * A function that is called before each spec in a suite.
537 | *
538 | * Used for spec setup, including validating assumptions.
539 | *
540 | * @param {Function} beforeEachFunction
541 | */
542 | var beforeEach = function(beforeEachFunction) {
543 | jasmine.getEnv().beforeEach(beforeEachFunction);
544 | };
545 | if (isCommonJS) exports.beforeEach = beforeEach;
546 |
547 | /**
548 | * A function that is called after each spec in a suite.
549 | *
550 | * Used for restoring any state that is hijacked during spec execution.
551 | *
552 | * @param {Function} afterEachFunction
553 | */
554 | var afterEach = function(afterEachFunction) {
555 | jasmine.getEnv().afterEach(afterEachFunction);
556 | };
557 | if (isCommonJS) exports.afterEach = afterEach;
558 |
559 | /**
560 | * Defines a suite of specifications.
561 | *
562 | * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
563 | * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
564 | * of setup in some tests.
565 | *
566 | * @example
567 | * // TODO: a simple suite
568 | *
569 | * // TODO: a simple suite with a nested describe block
570 | *
571 | * @param {String} description A string, usually the class under test.
572 | * @param {Function} specDefinitions function that defines several specs.
573 | */
574 | var describe = function(description, specDefinitions) {
575 | return jasmine.getEnv().describe(description, specDefinitions);
576 | };
577 | if (isCommonJS) exports.describe = describe;
578 |
579 | /**
580 | * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development.
581 | *
582 | * @param {String} description A string, usually the class under test.
583 | * @param {Function} specDefinitions function that defines several specs.
584 | */
585 | var xdescribe = function(description, specDefinitions) {
586 | return jasmine.getEnv().xdescribe(description, specDefinitions);
587 | };
588 | if (isCommonJS) exports.xdescribe = xdescribe;
589 |
590 |
591 | // Provide the XMLHttpRequest class for IE 5.x-6.x:
592 | jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
593 | function tryIt(f) {
594 | try {
595 | return f();
596 | } catch(e) {
597 | }
598 | return null;
599 | }
600 |
601 | var xhr = tryIt(function() {
602 | return new ActiveXObject("Msxml2.XMLHTTP.6.0");
603 | }) ||
604 | tryIt(function() {
605 | return new ActiveXObject("Msxml2.XMLHTTP.3.0");
606 | }) ||
607 | tryIt(function() {
608 | return new ActiveXObject("Msxml2.XMLHTTP");
609 | }) ||
610 | tryIt(function() {
611 | return new ActiveXObject("Microsoft.XMLHTTP");
612 | });
613 |
614 | if (!xhr) throw new Error("This browser does not support XMLHttpRequest.");
615 |
616 | return xhr;
617 | } : XMLHttpRequest;
618 | /**
619 | * @namespace
620 | */
621 | jasmine.util = {};
622 |
623 | /**
624 | * Declare that a child class inherit it's prototype from the parent class.
625 | *
626 | * @private
627 | * @param {Function} childClass
628 | * @param {Function} parentClass
629 | */
630 | jasmine.util.inherit = function(childClass, parentClass) {
631 | /**
632 | * @private
633 | */
634 | var subclass = function() {
635 | };
636 | subclass.prototype = parentClass.prototype;
637 | childClass.prototype = new subclass();
638 | };
639 |
640 | jasmine.util.formatException = function(e) {
641 | var lineNumber;
642 | if (e.line) {
643 | lineNumber = e.line;
644 | }
645 | else if (e.lineNumber) {
646 | lineNumber = e.lineNumber;
647 | }
648 |
649 | var file;
650 |
651 | if (e.sourceURL) {
652 | file = e.sourceURL;
653 | }
654 | else if (e.fileName) {
655 | file = e.fileName;
656 | }
657 |
658 | var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
659 |
660 | if (file && lineNumber) {
661 | message += ' in ' + file + ' (line ' + lineNumber + ')';
662 | }
663 |
664 | return message;
665 | };
666 |
667 | jasmine.util.htmlEscape = function(str) {
668 | if (!str) return str;
669 | return str.replace(/&/g, '&')
670 | .replace(//g, '>');
672 | };
673 |
674 | jasmine.util.argsToArray = function(args) {
675 | var arrayOfArgs = [];
676 | for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
677 | return arrayOfArgs;
678 | };
679 |
680 | jasmine.util.extend = function(destination, source) {
681 | for (var property in source) destination[property] = source[property];
682 | return destination;
683 | };
684 |
685 | /**
686 | * Environment for Jasmine
687 | *
688 | * @constructor
689 | */
690 | jasmine.Env = function() {
691 | this.currentSpec = null;
692 | this.currentSuite = null;
693 | this.currentRunner_ = new jasmine.Runner(this);
694 |
695 | this.reporter = new jasmine.MultiReporter();
696 |
697 | this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
698 | this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
699 | this.lastUpdate = 0;
700 | this.specFilter = function() {
701 | return true;
702 | };
703 |
704 | this.nextSpecId_ = 0;
705 | this.nextSuiteId_ = 0;
706 | this.equalityTesters_ = [];
707 |
708 | // wrap matchers
709 | this.matchersClass = function() {
710 | jasmine.Matchers.apply(this, arguments);
711 | };
712 | jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
713 |
714 | jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
715 | };
716 |
717 |
718 | jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
719 | jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
720 | jasmine.Env.prototype.setInterval = jasmine.setInterval;
721 | jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
722 |
723 | /**
724 | * @returns an object containing jasmine version build info, if set.
725 | */
726 | jasmine.Env.prototype.version = function () {
727 | if (jasmine.version_) {
728 | return jasmine.version_;
729 | } else {
730 | throw new Error('Version not set');
731 | }
732 | };
733 |
734 | /**
735 | * @returns string containing jasmine version build info, if set.
736 | */
737 | jasmine.Env.prototype.versionString = function() {
738 | if (!jasmine.version_) {
739 | return "version unknown";
740 | }
741 |
742 | var version = this.version();
743 | var versionString = version.major + "." + version.minor + "." + version.build;
744 | if (version.release_candidate) {
745 | versionString += ".rc" + version.release_candidate;
746 | }
747 | versionString += " revision " + version.revision;
748 | return versionString;
749 | };
750 |
751 | /**
752 | * @returns a sequential integer starting at 0
753 | */
754 | jasmine.Env.prototype.nextSpecId = function () {
755 | return this.nextSpecId_++;
756 | };
757 |
758 | /**
759 | * @returns a sequential integer starting at 0
760 | */
761 | jasmine.Env.prototype.nextSuiteId = function () {
762 | return this.nextSuiteId_++;
763 | };
764 |
765 | /**
766 | * Register a reporter to receive status updates from Jasmine.
767 | * @param {jasmine.Reporter} reporter An object which will receive status updates.
768 | */
769 | jasmine.Env.prototype.addReporter = function(reporter) {
770 | this.reporter.addReporter(reporter);
771 | };
772 |
773 | jasmine.Env.prototype.execute = function() {
774 | this.currentRunner_.execute();
775 | };
776 |
777 | jasmine.Env.prototype.describe = function(description, specDefinitions) {
778 | var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
779 |
780 | var parentSuite = this.currentSuite;
781 | if (parentSuite) {
782 | parentSuite.add(suite);
783 | } else {
784 | this.currentRunner_.add(suite);
785 | }
786 |
787 | this.currentSuite = suite;
788 |
789 | var declarationError = null;
790 | try {
791 | specDefinitions.call(suite);
792 | } catch(e) {
793 | declarationError = e;
794 | }
795 |
796 | if (declarationError) {
797 | this.it("encountered a declaration exception", function() {
798 | throw declarationError;
799 | });
800 | }
801 |
802 | this.currentSuite = parentSuite;
803 |
804 | return suite;
805 | };
806 |
807 | jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
808 | if (this.currentSuite) {
809 | this.currentSuite.beforeEach(beforeEachFunction);
810 | } else {
811 | this.currentRunner_.beforeEach(beforeEachFunction);
812 | }
813 | };
814 |
815 | jasmine.Env.prototype.currentRunner = function () {
816 | return this.currentRunner_;
817 | };
818 |
819 | jasmine.Env.prototype.afterEach = function(afterEachFunction) {
820 | if (this.currentSuite) {
821 | this.currentSuite.afterEach(afterEachFunction);
822 | } else {
823 | this.currentRunner_.afterEach(afterEachFunction);
824 | }
825 |
826 | };
827 |
828 | jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
829 | return {
830 | execute: function() {
831 | }
832 | };
833 | };
834 |
835 | jasmine.Env.prototype.it = function(description, func) {
836 | var spec = new jasmine.Spec(this, this.currentSuite, description);
837 | this.currentSuite.add(spec);
838 | this.currentSpec = spec;
839 |
840 | if (func) {
841 | spec.runs(func);
842 | }
843 |
844 | return spec;
845 | };
846 |
847 | jasmine.Env.prototype.xit = function(desc, func) {
848 | return {
849 | id: this.nextSpecId(),
850 | runs: function() {
851 | }
852 | };
853 | };
854 |
855 | jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
856 | if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
857 | return true;
858 | }
859 |
860 | a.__Jasmine_been_here_before__ = b;
861 | b.__Jasmine_been_here_before__ = a;
862 |
863 | var hasKey = function(obj, keyName) {
864 | return obj !== null && obj[keyName] !== jasmine.undefined;
865 | };
866 |
867 | for (var property in b) {
868 | if (!hasKey(a, property) && hasKey(b, property)) {
869 | mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
870 | }
871 | }
872 | for (property in a) {
873 | if (!hasKey(b, property) && hasKey(a, property)) {
874 | mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
875 | }
876 | }
877 | for (property in b) {
878 | if (property == '__Jasmine_been_here_before__') continue;
879 | if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
880 | mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual.");
881 | }
882 | }
883 |
884 | if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
885 | mismatchValues.push("arrays were not the same length");
886 | }
887 |
888 | delete a.__Jasmine_been_here_before__;
889 | delete b.__Jasmine_been_here_before__;
890 | return (mismatchKeys.length === 0 && mismatchValues.length === 0);
891 | };
892 |
893 | jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
894 | mismatchKeys = mismatchKeys || [];
895 | mismatchValues = mismatchValues || [];
896 |
897 | for (var i = 0; i < this.equalityTesters_.length; i++) {
898 | var equalityTester = this.equalityTesters_[i];
899 | var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
900 | if (result !== jasmine.undefined) return result;
901 | }
902 |
903 | if (a === b) return true;
904 |
905 | if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
906 | return (a == jasmine.undefined && b == jasmine.undefined);
907 | }
908 |
909 | if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
910 | return a === b;
911 | }
912 |
913 | if (a instanceof Date && b instanceof Date) {
914 | return a.getTime() == b.getTime();
915 | }
916 |
917 | if (a instanceof jasmine.Matchers.Any) {
918 | return a.matches(b);
919 | }
920 |
921 | if (b instanceof jasmine.Matchers.Any) {
922 | return b.matches(a);
923 | }
924 |
925 | if (jasmine.isString_(a) && jasmine.isString_(b)) {
926 | return (a == b);
927 | }
928 |
929 | if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {
930 | return (a == b);
931 | }
932 |
933 | if (typeof a === "object" && typeof b === "object") {
934 | return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
935 | }
936 |
937 | //Straight check
938 | return (a === b);
939 | };
940 |
941 | jasmine.Env.prototype.contains_ = function(haystack, needle) {
942 | if (jasmine.isArray_(haystack)) {
943 | for (var i = 0; i < haystack.length; i++) {
944 | if (this.equals_(haystack[i], needle)) return true;
945 | }
946 | return false;
947 | }
948 | return haystack.indexOf(needle) >= 0;
949 | };
950 |
951 | jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
952 | this.equalityTesters_.push(equalityTester);
953 | };
954 | /** No-op base class for Jasmine reporters.
955 | *
956 | * @constructor
957 | */
958 | jasmine.Reporter = function() {
959 | };
960 |
961 | //noinspection JSUnusedLocalSymbols
962 | jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
963 | };
964 |
965 | //noinspection JSUnusedLocalSymbols
966 | jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
967 | };
968 |
969 | //noinspection JSUnusedLocalSymbols
970 | jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
971 | };
972 |
973 | //noinspection JSUnusedLocalSymbols
974 | jasmine.Reporter.prototype.reportSpecStarting = function(spec) {
975 | };
976 |
977 | //noinspection JSUnusedLocalSymbols
978 | jasmine.Reporter.prototype.reportSpecResults = function(spec) {
979 | };
980 |
981 | //noinspection JSUnusedLocalSymbols
982 | jasmine.Reporter.prototype.log = function(str) {
983 | };
984 |
985 | /**
986 | * Blocks are functions with executable code that make up a spec.
987 | *
988 | * @constructor
989 | * @param {jasmine.Env} env
990 | * @param {Function} func
991 | * @param {jasmine.Spec} spec
992 | */
993 | jasmine.Block = function(env, func, spec) {
994 | this.env = env;
995 | this.func = func;
996 | this.spec = spec;
997 | };
998 |
999 | jasmine.Block.prototype.execute = function(onComplete) {
1000 | try {
1001 | this.func.apply(this.spec);
1002 | } catch (e) {
1003 | this.spec.fail(e);
1004 | }
1005 | onComplete();
1006 | };
1007 | /** JavaScript API reporter.
1008 | *
1009 | * @constructor
1010 | */
1011 | jasmine.JsApiReporter = function() {
1012 | this.started = false;
1013 | this.finished = false;
1014 | this.suites_ = [];
1015 | this.results_ = {};
1016 | };
1017 |
1018 | jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
1019 | this.started = true;
1020 | var suites = runner.topLevelSuites();
1021 | for (var i = 0; i < suites.length; i++) {
1022 | var suite = suites[i];
1023 | this.suites_.push(this.summarize_(suite));
1024 | }
1025 | };
1026 |
1027 | jasmine.JsApiReporter.prototype.suites = function() {
1028 | return this.suites_;
1029 | };
1030 |
1031 | jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
1032 | var isSuite = suiteOrSpec instanceof jasmine.Suite;
1033 | var summary = {
1034 | id: suiteOrSpec.id,
1035 | name: suiteOrSpec.description,
1036 | type: isSuite ? 'suite' : 'spec',
1037 | children: []
1038 | };
1039 |
1040 | if (isSuite) {
1041 | var children = suiteOrSpec.children();
1042 | for (var i = 0; i < children.length; i++) {
1043 | summary.children.push(this.summarize_(children[i]));
1044 | }
1045 | }
1046 | return summary;
1047 | };
1048 |
1049 | jasmine.JsApiReporter.prototype.results = function() {
1050 | return this.results_;
1051 | };
1052 |
1053 | jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {
1054 | return this.results_[specId];
1055 | };
1056 |
1057 | //noinspection JSUnusedLocalSymbols
1058 | jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
1059 | this.finished = true;
1060 | };
1061 |
1062 | //noinspection JSUnusedLocalSymbols
1063 | jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
1064 | };
1065 |
1066 | //noinspection JSUnusedLocalSymbols
1067 | jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
1068 | this.results_[spec.id] = {
1069 | messages: spec.results().getItems(),
1070 | result: spec.results().failedCount > 0 ? "failed" : "passed"
1071 | };
1072 | };
1073 |
1074 | //noinspection JSUnusedLocalSymbols
1075 | jasmine.JsApiReporter.prototype.log = function(str) {
1076 | };
1077 |
1078 | jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){
1079 | var results = {};
1080 | for (var i = 0; i < specIds.length; i++) {
1081 | var specId = specIds[i];
1082 | results[specId] = this.summarizeResult_(this.results_[specId]);
1083 | }
1084 | return results;
1085 | };
1086 |
1087 | jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
1088 | var summaryMessages = [];
1089 | var messagesLength = result.messages.length;
1090 | for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
1091 | var resultMessage = result.messages[messageIndex];
1092 | summaryMessages.push({
1093 | text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
1094 | passed: resultMessage.passed ? resultMessage.passed() : true,
1095 | type: resultMessage.type,
1096 | message: resultMessage.message,
1097 | trace: {
1098 | stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
1099 | }
1100 | });
1101 | }
1102 |
1103 | return {
1104 | result : result.result,
1105 | messages : summaryMessages
1106 | };
1107 | };
1108 |
1109 | /**
1110 | * @constructor
1111 | * @param {jasmine.Env} env
1112 | * @param actual
1113 | * @param {jasmine.Spec} spec
1114 | */
1115 | jasmine.Matchers = function(env, actual, spec, opt_isNot) {
1116 | this.env = env;
1117 | this.actual = actual;
1118 | this.spec = spec;
1119 | this.isNot = opt_isNot || false;
1120 | this.reportWasCalled_ = false;
1121 | };
1122 |
1123 | // todo: @deprecated as of Jasmine 0.11, remove soon [xw]
1124 | jasmine.Matchers.pp = function(str) {
1125 | throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
1126 | };
1127 |
1128 | // todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
1129 | jasmine.Matchers.prototype.report = function(result, failing_message, details) {
1130 | throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
1131 | };
1132 |
1133 | jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {
1134 | for (var methodName in prototype) {
1135 | if (methodName == 'report') continue;
1136 | var orig = prototype[methodName];
1137 | matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
1138 | }
1139 | };
1140 |
1141 | jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
1142 | return function() {
1143 | var matcherArgs = jasmine.util.argsToArray(arguments);
1144 | var result = matcherFunction.apply(this, arguments);
1145 |
1146 | if (this.isNot) {
1147 | result = !result;
1148 | }
1149 |
1150 | if (this.reportWasCalled_) return result;
1151 |
1152 | var message;
1153 | if (!result) {
1154 | if (this.message) {
1155 | message = this.message.apply(this, arguments);
1156 | if (jasmine.isArray_(message)) {
1157 | message = message[this.isNot ? 1 : 0];
1158 | }
1159 | } else {
1160 | var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
1161 | message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;
1162 | if (matcherArgs.length > 0) {
1163 | for (var i = 0; i < matcherArgs.length; i++) {
1164 | if (i > 0) message += ",";
1165 | message += " " + jasmine.pp(matcherArgs[i]);
1166 | }
1167 | }
1168 | message += ".";
1169 | }
1170 | }
1171 | var expectationResult = new jasmine.ExpectationResult({
1172 | matcherName: matcherName,
1173 | passed: result,
1174 | expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],
1175 | actual: this.actual,
1176 | message: message
1177 | });
1178 | this.spec.addMatcherResult(expectationResult);
1179 | return jasmine.undefined;
1180 | };
1181 | };
1182 |
1183 |
1184 |
1185 |
1186 | /**
1187 | * toBe: compares the actual to the expected using ===
1188 | * @param expected
1189 | */
1190 | jasmine.Matchers.prototype.toBe = function(expected) {
1191 | return this.actual === expected;
1192 | };
1193 |
1194 | /**
1195 | * toNotBe: compares the actual to the expected using !==
1196 | * @param expected
1197 | * @deprecated as of 1.0. Use not.toBe() instead.
1198 | */
1199 | jasmine.Matchers.prototype.toNotBe = function(expected) {
1200 | return this.actual !== expected;
1201 | };
1202 |
1203 | /**
1204 | * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
1205 | *
1206 | * @param expected
1207 | */
1208 | jasmine.Matchers.prototype.toEqual = function(expected) {
1209 | return this.env.equals_(this.actual, expected);
1210 | };
1211 |
1212 | /**
1213 | * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
1214 | * @param expected
1215 | * @deprecated as of 1.0. Use not.toNotEqual() instead.
1216 | */
1217 | jasmine.Matchers.prototype.toNotEqual = function(expected) {
1218 | return !this.env.equals_(this.actual, expected);
1219 | };
1220 |
1221 | /**
1222 | * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes
1223 | * a pattern or a String.
1224 | *
1225 | * @param expected
1226 | */
1227 | jasmine.Matchers.prototype.toMatch = function(expected) {
1228 | return new RegExp(expected).test(this.actual);
1229 | };
1230 |
1231 | /**
1232 | * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
1233 | * @param expected
1234 | * @deprecated as of 1.0. Use not.toMatch() instead.
1235 | */
1236 | jasmine.Matchers.prototype.toNotMatch = function(expected) {
1237 | return !(new RegExp(expected).test(this.actual));
1238 | };
1239 |
1240 | /**
1241 | * Matcher that compares the actual to jasmine.undefined.
1242 | */
1243 | jasmine.Matchers.prototype.toBeDefined = function() {
1244 | return (this.actual !== jasmine.undefined);
1245 | };
1246 |
1247 | /**
1248 | * Matcher that compares the actual to jasmine.undefined.
1249 | */
1250 | jasmine.Matchers.prototype.toBeUndefined = function() {
1251 | return (this.actual === jasmine.undefined);
1252 | };
1253 |
1254 | /**
1255 | * Matcher that compares the actual to null.
1256 | */
1257 | jasmine.Matchers.prototype.toBeNull = function() {
1258 | return (this.actual === null);
1259 | };
1260 |
1261 | /**
1262 | * Matcher that boolean not-nots the actual.
1263 | */
1264 | jasmine.Matchers.prototype.toBeTruthy = function() {
1265 | return !!this.actual;
1266 | };
1267 |
1268 |
1269 | /**
1270 | * Matcher that boolean nots the actual.
1271 | */
1272 | jasmine.Matchers.prototype.toBeFalsy = function() {
1273 | return !this.actual;
1274 | };
1275 |
1276 |
1277 | /**
1278 | * Matcher that checks to see if the actual, a Jasmine spy, was called.
1279 | */
1280 | jasmine.Matchers.prototype.toHaveBeenCalled = function() {
1281 | if (arguments.length > 0) {
1282 | throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
1283 | }
1284 |
1285 | if (!jasmine.isSpy(this.actual)) {
1286 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1287 | }
1288 |
1289 | this.message = function() {
1290 | return [
1291 | "Expected spy " + this.actual.identity + " to have been called.",
1292 | "Expected spy " + this.actual.identity + " not to have been called."
1293 | ];
1294 | };
1295 |
1296 | return this.actual.wasCalled;
1297 | };
1298 |
1299 | /** @deprecated Use expect(xxx).toHaveBeenCalled() instead */
1300 | jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;
1301 |
1302 | /**
1303 | * Matcher that checks to see if the actual, a Jasmine spy, was not called.
1304 | *
1305 | * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead
1306 | */
1307 | jasmine.Matchers.prototype.wasNotCalled = function() {
1308 | if (arguments.length > 0) {
1309 | throw new Error('wasNotCalled does not take arguments');
1310 | }
1311 |
1312 | if (!jasmine.isSpy(this.actual)) {
1313 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1314 | }
1315 |
1316 | this.message = function() {
1317 | return [
1318 | "Expected spy " + this.actual.identity + " to not have been called.",
1319 | "Expected spy " + this.actual.identity + " to have been called."
1320 | ];
1321 | };
1322 |
1323 | return !this.actual.wasCalled;
1324 | };
1325 |
1326 | /**
1327 | * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.
1328 | *
1329 | * @example
1330 | *
1331 | */
1332 | jasmine.Matchers.prototype.toHaveBeenCalledWith = function() {
1333 | var expectedArgs = jasmine.util.argsToArray(arguments);
1334 | if (!jasmine.isSpy(this.actual)) {
1335 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1336 | }
1337 | this.message = function() {
1338 | if (this.actual.callCount === 0) {
1339 | // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw]
1340 | return [
1341 | "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.",
1342 | "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was."
1343 | ];
1344 | } else {
1345 | return [
1346 | "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall),
1347 | "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall)
1348 | ];
1349 | }
1350 | };
1351 |
1352 | return this.env.contains_(this.actual.argsForCall, expectedArgs);
1353 | };
1354 |
1355 | /** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */
1356 | jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;
1357 |
1358 | /** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */
1359 | jasmine.Matchers.prototype.wasNotCalledWith = function() {
1360 | var expectedArgs = jasmine.util.argsToArray(arguments);
1361 | if (!jasmine.isSpy(this.actual)) {
1362 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1363 | }
1364 |
1365 | this.message = function() {
1366 | return [
1367 | "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",
1368 | "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"
1369 | ];
1370 | };
1371 |
1372 | return !this.env.contains_(this.actual.argsForCall, expectedArgs);
1373 | };
1374 |
1375 | /**
1376 | * Matcher that checks that the expected item is an element in the actual Array.
1377 | *
1378 | * @param {Object} expected
1379 | */
1380 | jasmine.Matchers.prototype.toContain = function(expected) {
1381 | return this.env.contains_(this.actual, expected);
1382 | };
1383 |
1384 | /**
1385 | * Matcher that checks that the expected item is NOT an element in the actual Array.
1386 | *
1387 | * @param {Object} expected
1388 | * @deprecated as of 1.0. Use not.toNotContain() instead.
1389 | */
1390 | jasmine.Matchers.prototype.toNotContain = function(expected) {
1391 | return !this.env.contains_(this.actual, expected);
1392 | };
1393 |
1394 | jasmine.Matchers.prototype.toBeLessThan = function(expected) {
1395 | return this.actual < expected;
1396 | };
1397 |
1398 | jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {
1399 | return this.actual > expected;
1400 | };
1401 |
1402 | /**
1403 | * Matcher that checks that the expected item is equal to the actual item
1404 | * up to a given level of decimal precision (default 2).
1405 | *
1406 | * @param {Number} expected
1407 | * @param {Number} precision
1408 | */
1409 | jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) {
1410 | if (!(precision === 0)) {
1411 | precision = precision || 2;
1412 | }
1413 | var multiplier = Math.pow(10, precision);
1414 | var actual = Math.round(this.actual * multiplier);
1415 | expected = Math.round(expected * multiplier);
1416 | return expected == actual;
1417 | };
1418 |
1419 | /**
1420 | * Matcher that checks that the expected exception was thrown by the actual.
1421 | *
1422 | * @param {String} expected
1423 | */
1424 | jasmine.Matchers.prototype.toThrow = function(expected) {
1425 | var result = false;
1426 | var exception;
1427 | if (typeof this.actual != 'function') {
1428 | throw new Error('Actual is not a function');
1429 | }
1430 | try {
1431 | this.actual();
1432 | } catch (e) {
1433 | exception = e;
1434 | }
1435 | if (exception) {
1436 | result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
1437 | }
1438 |
1439 | var not = this.isNot ? "not " : "";
1440 |
1441 | this.message = function() {
1442 | if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
1443 | return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' ');
1444 | } else {
1445 | return "Expected function to throw an exception.";
1446 | }
1447 | };
1448 |
1449 | return result;
1450 | };
1451 |
1452 | jasmine.Matchers.Any = function(expectedClass) {
1453 | this.expectedClass = expectedClass;
1454 | };
1455 |
1456 | jasmine.Matchers.Any.prototype.matches = function(other) {
1457 | if (this.expectedClass == String) {
1458 | return typeof other == 'string' || other instanceof String;
1459 | }
1460 |
1461 | if (this.expectedClass == Number) {
1462 | return typeof other == 'number' || other instanceof Number;
1463 | }
1464 |
1465 | if (this.expectedClass == Function) {
1466 | return typeof other == 'function' || other instanceof Function;
1467 | }
1468 |
1469 | if (this.expectedClass == Object) {
1470 | return typeof other == 'object';
1471 | }
1472 |
1473 | return other instanceof this.expectedClass;
1474 | };
1475 |
1476 | jasmine.Matchers.Any.prototype.toString = function() {
1477 | return '';
1478 | };
1479 |
1480 | /**
1481 | * @constructor
1482 | */
1483 | jasmine.MultiReporter = function() {
1484 | this.subReporters_ = [];
1485 | };
1486 | jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);
1487 |
1488 | jasmine.MultiReporter.prototype.addReporter = function(reporter) {
1489 | this.subReporters_.push(reporter);
1490 | };
1491 |
1492 | (function() {
1493 | var functionNames = [
1494 | "reportRunnerStarting",
1495 | "reportRunnerResults",
1496 | "reportSuiteResults",
1497 | "reportSpecStarting",
1498 | "reportSpecResults",
1499 | "log"
1500 | ];
1501 | for (var i = 0; i < functionNames.length; i++) {
1502 | var functionName = functionNames[i];
1503 | jasmine.MultiReporter.prototype[functionName] = (function(functionName) {
1504 | return function() {
1505 | for (var j = 0; j < this.subReporters_.length; j++) {
1506 | var subReporter = this.subReporters_[j];
1507 | if (subReporter[functionName]) {
1508 | subReporter[functionName].apply(subReporter, arguments);
1509 | }
1510 | }
1511 | };
1512 | })(functionName);
1513 | }
1514 | })();
1515 | /**
1516 | * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults
1517 | *
1518 | * @constructor
1519 | */
1520 | jasmine.NestedResults = function() {
1521 | /**
1522 | * The total count of results
1523 | */
1524 | this.totalCount = 0;
1525 | /**
1526 | * Number of passed results
1527 | */
1528 | this.passedCount = 0;
1529 | /**
1530 | * Number of failed results
1531 | */
1532 | this.failedCount = 0;
1533 | /**
1534 | * Was this suite/spec skipped?
1535 | */
1536 | this.skipped = false;
1537 | /**
1538 | * @ignore
1539 | */
1540 | this.items_ = [];
1541 | };
1542 |
1543 | /**
1544 | * Roll up the result counts.
1545 | *
1546 | * @param result
1547 | */
1548 | jasmine.NestedResults.prototype.rollupCounts = function(result) {
1549 | this.totalCount += result.totalCount;
1550 | this.passedCount += result.passedCount;
1551 | this.failedCount += result.failedCount;
1552 | };
1553 |
1554 | /**
1555 | * Adds a log message.
1556 | * @param values Array of message parts which will be concatenated later.
1557 | */
1558 | jasmine.NestedResults.prototype.log = function(values) {
1559 | this.items_.push(new jasmine.MessageResult(values));
1560 | };
1561 |
1562 | /**
1563 | * Getter for the results: message & results.
1564 | */
1565 | jasmine.NestedResults.prototype.getItems = function() {
1566 | return this.items_;
1567 | };
1568 |
1569 | /**
1570 | * Adds a result, tracking counts (total, passed, & failed)
1571 | * @param {jasmine.ExpectationResult|jasmine.NestedResults} result
1572 | */
1573 | jasmine.NestedResults.prototype.addResult = function(result) {
1574 | if (result.type != 'log') {
1575 | if (result.items_) {
1576 | this.rollupCounts(result);
1577 | } else {
1578 | this.totalCount++;
1579 | if (result.passed()) {
1580 | this.passedCount++;
1581 | } else {
1582 | this.failedCount++;
1583 | }
1584 | }
1585 | }
1586 | this.items_.push(result);
1587 | };
1588 |
1589 | /**
1590 | * @returns {Boolean} True if everything below passed
1591 | */
1592 | jasmine.NestedResults.prototype.passed = function() {
1593 | return this.passedCount === this.totalCount;
1594 | };
1595 | /**
1596 | * Base class for pretty printing for expectation results.
1597 | */
1598 | jasmine.PrettyPrinter = function() {
1599 | this.ppNestLevel_ = 0;
1600 | };
1601 |
1602 | /**
1603 | * Formats a value in a nice, human-readable string.
1604 | *
1605 | * @param value
1606 | */
1607 | jasmine.PrettyPrinter.prototype.format = function(value) {
1608 | if (this.ppNestLevel_ > 40) {
1609 | throw new Error('jasmine.PrettyPrinter: format() nested too deeply!');
1610 | }
1611 |
1612 | this.ppNestLevel_++;
1613 | try {
1614 | if (value === jasmine.undefined) {
1615 | this.emitScalar('undefined');
1616 | } else if (value === null) {
1617 | this.emitScalar('null');
1618 | } else if (value === jasmine.getGlobal()) {
1619 | this.emitScalar('');
1620 | } else if (value instanceof jasmine.Matchers.Any) {
1621 | this.emitScalar(value.toString());
1622 | } else if (typeof value === 'string') {
1623 | this.emitString(value);
1624 | } else if (jasmine.isSpy(value)) {
1625 | this.emitScalar("spy on " + value.identity);
1626 | } else if (value instanceof RegExp) {
1627 | this.emitScalar(value.toString());
1628 | } else if (typeof value === 'function') {
1629 | this.emitScalar('Function');
1630 | } else if (typeof value.nodeType === 'number') {
1631 | this.emitScalar('HTMLNode');
1632 | } else if (value instanceof Date) {
1633 | this.emitScalar('Date(' + value + ')');
1634 | } else if (value.__Jasmine_been_here_before__) {
1635 | this.emitScalar('');
1636 | } else if (jasmine.isArray_(value) || typeof value == 'object') {
1637 | value.__Jasmine_been_here_before__ = true;
1638 | if (jasmine.isArray_(value)) {
1639 | this.emitArray(value);
1640 | } else {
1641 | this.emitObject(value);
1642 | }
1643 | delete value.__Jasmine_been_here_before__;
1644 | } else {
1645 | this.emitScalar(value.toString());
1646 | }
1647 | } finally {
1648 | this.ppNestLevel_--;
1649 | }
1650 | };
1651 |
1652 | jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {
1653 | for (var property in obj) {
1654 | if (property == '__Jasmine_been_here_before__') continue;
1655 | fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined &&
1656 | obj.__lookupGetter__(property) !== null) : false);
1657 | }
1658 | };
1659 |
1660 | jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;
1661 | jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;
1662 | jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;
1663 | jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;
1664 |
1665 | jasmine.StringPrettyPrinter = function() {
1666 | jasmine.PrettyPrinter.call(this);
1667 |
1668 | this.string = '';
1669 | };
1670 | jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);
1671 |
1672 | jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {
1673 | this.append(value);
1674 | };
1675 |
1676 | jasmine.StringPrettyPrinter.prototype.emitString = function(value) {
1677 | this.append("'" + value + "'");
1678 | };
1679 |
1680 | jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {
1681 | this.append('[ ');
1682 | for (var i = 0; i < array.length; i++) {
1683 | if (i > 0) {
1684 | this.append(', ');
1685 | }
1686 | this.format(array[i]);
1687 | }
1688 | this.append(' ]');
1689 | };
1690 |
1691 | jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {
1692 | var self = this;
1693 | this.append('{ ');
1694 | var first = true;
1695 |
1696 | this.iterateObject(obj, function(property, isGetter) {
1697 | if (first) {
1698 | first = false;
1699 | } else {
1700 | self.append(', ');
1701 | }
1702 |
1703 | self.append(property);
1704 | self.append(' : ');
1705 | if (isGetter) {
1706 | self.append('');
1707 | } else {
1708 | self.format(obj[property]);
1709 | }
1710 | });
1711 |
1712 | this.append(' }');
1713 | };
1714 |
1715 | jasmine.StringPrettyPrinter.prototype.append = function(value) {
1716 | this.string += value;
1717 | };
1718 | jasmine.Queue = function(env) {
1719 | this.env = env;
1720 | this.blocks = [];
1721 | this.running = false;
1722 | this.index = 0;
1723 | this.offset = 0;
1724 | this.abort = false;
1725 | };
1726 |
1727 | jasmine.Queue.prototype.addBefore = function(block) {
1728 | this.blocks.unshift(block);
1729 | };
1730 |
1731 | jasmine.Queue.prototype.add = function(block) {
1732 | this.blocks.push(block);
1733 | };
1734 |
1735 | jasmine.Queue.prototype.insertNext = function(block) {
1736 | this.blocks.splice((this.index + this.offset + 1), 0, block);
1737 | this.offset++;
1738 | };
1739 |
1740 | jasmine.Queue.prototype.start = function(onComplete) {
1741 | this.running = true;
1742 | this.onComplete = onComplete;
1743 | this.next_();
1744 | };
1745 |
1746 | jasmine.Queue.prototype.isRunning = function() {
1747 | return this.running;
1748 | };
1749 |
1750 | jasmine.Queue.LOOP_DONT_RECURSE = true;
1751 |
1752 | jasmine.Queue.prototype.next_ = function() {
1753 | var self = this;
1754 | var goAgain = true;
1755 |
1756 | while (goAgain) {
1757 | goAgain = false;
1758 |
1759 | if (self.index < self.blocks.length && !this.abort) {
1760 | var calledSynchronously = true;
1761 | var completedSynchronously = false;
1762 |
1763 | var onComplete = function () {
1764 | if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {
1765 | completedSynchronously = true;
1766 | return;
1767 | }
1768 |
1769 | if (self.blocks[self.index].abort) {
1770 | self.abort = true;
1771 | }
1772 |
1773 | self.offset = 0;
1774 | self.index++;
1775 |
1776 | var now = new Date().getTime();
1777 | if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {
1778 | self.env.lastUpdate = now;
1779 | self.env.setTimeout(function() {
1780 | self.next_();
1781 | }, 0);
1782 | } else {
1783 | if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {
1784 | goAgain = true;
1785 | } else {
1786 | self.next_();
1787 | }
1788 | }
1789 | };
1790 | self.blocks[self.index].execute(onComplete);
1791 |
1792 | calledSynchronously = false;
1793 | if (completedSynchronously) {
1794 | onComplete();
1795 | }
1796 |
1797 | } else {
1798 | self.running = false;
1799 | if (self.onComplete) {
1800 | self.onComplete();
1801 | }
1802 | }
1803 | }
1804 | };
1805 |
1806 | jasmine.Queue.prototype.results = function() {
1807 | var results = new jasmine.NestedResults();
1808 | for (var i = 0; i < this.blocks.length; i++) {
1809 | if (this.blocks[i].results) {
1810 | results.addResult(this.blocks[i].results());
1811 | }
1812 | }
1813 | return results;
1814 | };
1815 |
1816 |
1817 | /**
1818 | * Runner
1819 | *
1820 | * @constructor
1821 | * @param {jasmine.Env} env
1822 | */
1823 | jasmine.Runner = function(env) {
1824 | var self = this;
1825 | self.env = env;
1826 | self.queue = new jasmine.Queue(env);
1827 | self.before_ = [];
1828 | self.after_ = [];
1829 | self.suites_ = [];
1830 | };
1831 |
1832 | jasmine.Runner.prototype.execute = function() {
1833 | var self = this;
1834 | if (self.env.reporter.reportRunnerStarting) {
1835 | self.env.reporter.reportRunnerStarting(this);
1836 | }
1837 | self.queue.start(function () {
1838 | self.finishCallback();
1839 | });
1840 | };
1841 |
1842 | jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) {
1843 | beforeEachFunction.typeName = 'beforeEach';
1844 | this.before_.splice(0,0,beforeEachFunction);
1845 | };
1846 |
1847 | jasmine.Runner.prototype.afterEach = function(afterEachFunction) {
1848 | afterEachFunction.typeName = 'afterEach';
1849 | this.after_.splice(0,0,afterEachFunction);
1850 | };
1851 |
1852 |
1853 | jasmine.Runner.prototype.finishCallback = function() {
1854 | this.env.reporter.reportRunnerResults(this);
1855 | };
1856 |
1857 | jasmine.Runner.prototype.addSuite = function(suite) {
1858 | this.suites_.push(suite);
1859 | };
1860 |
1861 | jasmine.Runner.prototype.add = function(block) {
1862 | if (block instanceof jasmine.Suite) {
1863 | this.addSuite(block);
1864 | }
1865 | this.queue.add(block);
1866 | };
1867 |
1868 | jasmine.Runner.prototype.specs = function () {
1869 | var suites = this.suites();
1870 | var specs = [];
1871 | for (var i = 0; i < suites.length; i++) {
1872 | specs = specs.concat(suites[i].specs());
1873 | }
1874 | return specs;
1875 | };
1876 |
1877 | jasmine.Runner.prototype.suites = function() {
1878 | return this.suites_;
1879 | };
1880 |
1881 | jasmine.Runner.prototype.topLevelSuites = function() {
1882 | var topLevelSuites = [];
1883 | for (var i = 0; i < this.suites_.length; i++) {
1884 | if (!this.suites_[i].parentSuite) {
1885 | topLevelSuites.push(this.suites_[i]);
1886 | }
1887 | }
1888 | return topLevelSuites;
1889 | };
1890 |
1891 | jasmine.Runner.prototype.results = function() {
1892 | return this.queue.results();
1893 | };
1894 | /**
1895 | * Internal representation of a Jasmine specification, or test.
1896 | *
1897 | * @constructor
1898 | * @param {jasmine.Env} env
1899 | * @param {jasmine.Suite} suite
1900 | * @param {String} description
1901 | */
1902 | jasmine.Spec = function(env, suite, description) {
1903 | if (!env) {
1904 | throw new Error('jasmine.Env() required');
1905 | }
1906 | if (!suite) {
1907 | throw new Error('jasmine.Suite() required');
1908 | }
1909 | var spec = this;
1910 | spec.id = env.nextSpecId ? env.nextSpecId() : null;
1911 | spec.env = env;
1912 | spec.suite = suite;
1913 | spec.description = description;
1914 | spec.queue = new jasmine.Queue(env);
1915 |
1916 | spec.afterCallbacks = [];
1917 | spec.spies_ = [];
1918 |
1919 | spec.results_ = new jasmine.NestedResults();
1920 | spec.results_.description = description;
1921 | spec.matchersClass = null;
1922 | };
1923 |
1924 | jasmine.Spec.prototype.getFullName = function() {
1925 | return this.suite.getFullName() + ' ' + this.description + '.';
1926 | };
1927 |
1928 |
1929 | jasmine.Spec.prototype.results = function() {
1930 | return this.results_;
1931 | };
1932 |
1933 | /**
1934 | * All parameters are pretty-printed and concatenated together, then written to the spec's output.
1935 | *
1936 | * Be careful not to leave calls to jasmine.log in production code.
1937 | */
1938 | jasmine.Spec.prototype.log = function() {
1939 | return this.results_.log(arguments);
1940 | };
1941 |
1942 | jasmine.Spec.prototype.runs = function (func) {
1943 | var block = new jasmine.Block(this.env, func, this);
1944 | this.addToQueue(block);
1945 | return this;
1946 | };
1947 |
1948 | jasmine.Spec.prototype.addToQueue = function (block) {
1949 | if (this.queue.isRunning()) {
1950 | this.queue.insertNext(block);
1951 | } else {
1952 | this.queue.add(block);
1953 | }
1954 | };
1955 |
1956 | /**
1957 | * @param {jasmine.ExpectationResult} result
1958 | */
1959 | jasmine.Spec.prototype.addMatcherResult = function(result) {
1960 | this.results_.addResult(result);
1961 | };
1962 |
1963 | jasmine.Spec.prototype.expect = function(actual) {
1964 | var positive = new (this.getMatchersClass_())(this.env, actual, this);
1965 | positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
1966 | return positive;
1967 | };
1968 |
1969 | /**
1970 | * Waits a fixed time period before moving to the next block.
1971 | *
1972 | * @deprecated Use waitsFor() instead
1973 | * @param {Number} timeout milliseconds to wait
1974 | */
1975 | jasmine.Spec.prototype.waits = function(timeout) {
1976 | var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
1977 | this.addToQueue(waitsFunc);
1978 | return this;
1979 | };
1980 |
1981 | /**
1982 | * Waits for the latchFunction to return true before proceeding to the next block.
1983 | *
1984 | * @param {Function} latchFunction
1985 | * @param {String} optional_timeoutMessage
1986 | * @param {Number} optional_timeout
1987 | */
1988 | jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
1989 | var latchFunction_ = null;
1990 | var optional_timeoutMessage_ = null;
1991 | var optional_timeout_ = null;
1992 |
1993 | for (var i = 0; i < arguments.length; i++) {
1994 | var arg = arguments[i];
1995 | switch (typeof arg) {
1996 | case 'function':
1997 | latchFunction_ = arg;
1998 | break;
1999 | case 'string':
2000 | optional_timeoutMessage_ = arg;
2001 | break;
2002 | case 'number':
2003 | optional_timeout_ = arg;
2004 | break;
2005 | }
2006 | }
2007 |
2008 | var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);
2009 | this.addToQueue(waitsForFunc);
2010 | return this;
2011 | };
2012 |
2013 | jasmine.Spec.prototype.fail = function (e) {
2014 | var expectationResult = new jasmine.ExpectationResult({
2015 | passed: false,
2016 | message: e ? jasmine.util.formatException(e) : 'Exception',
2017 | trace: { stack: e.stack }
2018 | });
2019 | this.results_.addResult(expectationResult);
2020 | };
2021 |
2022 | jasmine.Spec.prototype.getMatchersClass_ = function() {
2023 | return this.matchersClass || this.env.matchersClass;
2024 | };
2025 |
2026 | jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
2027 | var parent = this.getMatchersClass_();
2028 | var newMatchersClass = function() {
2029 | parent.apply(this, arguments);
2030 | };
2031 | jasmine.util.inherit(newMatchersClass, parent);
2032 | jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
2033 | this.matchersClass = newMatchersClass;
2034 | };
2035 |
2036 | jasmine.Spec.prototype.finishCallback = function() {
2037 | this.env.reporter.reportSpecResults(this);
2038 | };
2039 |
2040 | jasmine.Spec.prototype.finish = function(onComplete) {
2041 | this.removeAllSpies();
2042 | this.finishCallback();
2043 | if (onComplete) {
2044 | onComplete();
2045 | }
2046 | };
2047 |
2048 | jasmine.Spec.prototype.after = function(doAfter) {
2049 | if (this.queue.isRunning()) {
2050 | this.queue.add(new jasmine.Block(this.env, doAfter, this));
2051 | } else {
2052 | this.afterCallbacks.unshift(doAfter);
2053 | }
2054 | };
2055 |
2056 | jasmine.Spec.prototype.execute = function(onComplete) {
2057 | var spec = this;
2058 | if (!spec.env.specFilter(spec)) {
2059 | spec.results_.skipped = true;
2060 | spec.finish(onComplete);
2061 | return;
2062 | }
2063 |
2064 | this.env.reporter.reportSpecStarting(this);
2065 |
2066 | spec.env.currentSpec = spec;
2067 |
2068 | spec.addBeforesAndAftersToQueue();
2069 |
2070 | spec.queue.start(function () {
2071 | spec.finish(onComplete);
2072 | });
2073 | };
2074 |
2075 | jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
2076 | var runner = this.env.currentRunner();
2077 | var i;
2078 |
2079 | for (var suite = this.suite; suite; suite = suite.parentSuite) {
2080 | for (i = 0; i < suite.before_.length; i++) {
2081 | this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
2082 | }
2083 | }
2084 | for (i = 0; i < runner.before_.length; i++) {
2085 | this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
2086 | }
2087 | for (i = 0; i < this.afterCallbacks.length; i++) {
2088 | this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this));
2089 | }
2090 | for (suite = this.suite; suite; suite = suite.parentSuite) {
2091 | for (i = 0; i < suite.after_.length; i++) {
2092 | this.queue.add(new jasmine.Block(this.env, suite.after_[i], this));
2093 | }
2094 | }
2095 | for (i = 0; i < runner.after_.length; i++) {
2096 | this.queue.add(new jasmine.Block(this.env, runner.after_[i], this));
2097 | }
2098 | };
2099 |
2100 | jasmine.Spec.prototype.explodes = function() {
2101 | throw 'explodes function should not have been called';
2102 | };
2103 |
2104 | jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
2105 | if (obj == jasmine.undefined) {
2106 | throw "spyOn could not find an object to spy upon for " + methodName + "()";
2107 | }
2108 |
2109 | if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
2110 | throw methodName + '() method does not exist';
2111 | }
2112 |
2113 | if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
2114 | throw new Error(methodName + ' has already been spied upon');
2115 | }
2116 |
2117 | var spyObj = jasmine.createSpy(methodName);
2118 |
2119 | this.spies_.push(spyObj);
2120 | spyObj.baseObj = obj;
2121 | spyObj.methodName = methodName;
2122 | spyObj.originalValue = obj[methodName];
2123 |
2124 | obj[methodName] = spyObj;
2125 |
2126 | return spyObj;
2127 | };
2128 |
2129 | jasmine.Spec.prototype.removeAllSpies = function() {
2130 | for (var i = 0; i < this.spies_.length; i++) {
2131 | var spy = this.spies_[i];
2132 | spy.baseObj[spy.methodName] = spy.originalValue;
2133 | }
2134 | this.spies_ = [];
2135 | };
2136 |
2137 | /**
2138 | * Internal representation of a Jasmine suite.
2139 | *
2140 | * @constructor
2141 | * @param {jasmine.Env} env
2142 | * @param {String} description
2143 | * @param {Function} specDefinitions
2144 | * @param {jasmine.Suite} parentSuite
2145 | */
2146 | jasmine.Suite = function(env, description, specDefinitions, parentSuite) {
2147 | var self = this;
2148 | self.id = env.nextSuiteId ? env.nextSuiteId() : null;
2149 | self.description = description;
2150 | self.queue = new jasmine.Queue(env);
2151 | self.parentSuite = parentSuite;
2152 | self.env = env;
2153 | self.before_ = [];
2154 | self.after_ = [];
2155 | self.children_ = [];
2156 | self.suites_ = [];
2157 | self.specs_ = [];
2158 | };
2159 |
2160 | jasmine.Suite.prototype.getFullName = function() {
2161 | var fullName = this.description;
2162 | for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
2163 | fullName = parentSuite.description + ' ' + fullName;
2164 | }
2165 | return fullName;
2166 | };
2167 |
2168 | jasmine.Suite.prototype.finish = function(onComplete) {
2169 | this.env.reporter.reportSuiteResults(this);
2170 | this.finished = true;
2171 | if (typeof(onComplete) == 'function') {
2172 | onComplete();
2173 | }
2174 | };
2175 |
2176 | jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {
2177 | beforeEachFunction.typeName = 'beforeEach';
2178 | this.before_.unshift(beforeEachFunction);
2179 | };
2180 |
2181 | jasmine.Suite.prototype.afterEach = function(afterEachFunction) {
2182 | afterEachFunction.typeName = 'afterEach';
2183 | this.after_.unshift(afterEachFunction);
2184 | };
2185 |
2186 | jasmine.Suite.prototype.results = function() {
2187 | return this.queue.results();
2188 | };
2189 |
2190 | jasmine.Suite.prototype.add = function(suiteOrSpec) {
2191 | this.children_.push(suiteOrSpec);
2192 | if (suiteOrSpec instanceof jasmine.Suite) {
2193 | this.suites_.push(suiteOrSpec);
2194 | this.env.currentRunner().addSuite(suiteOrSpec);
2195 | } else {
2196 | this.specs_.push(suiteOrSpec);
2197 | }
2198 | this.queue.add(suiteOrSpec);
2199 | };
2200 |
2201 | jasmine.Suite.prototype.specs = function() {
2202 | return this.specs_;
2203 | };
2204 |
2205 | jasmine.Suite.prototype.suites = function() {
2206 | return this.suites_;
2207 | };
2208 |
2209 | jasmine.Suite.prototype.children = function() {
2210 | return this.children_;
2211 | };
2212 |
2213 | jasmine.Suite.prototype.execute = function(onComplete) {
2214 | var self = this;
2215 | this.queue.start(function () {
2216 | self.finish(onComplete);
2217 | });
2218 | };
2219 | jasmine.WaitsBlock = function(env, timeout, spec) {
2220 | this.timeout = timeout;
2221 | jasmine.Block.call(this, env, null, spec);
2222 | };
2223 |
2224 | jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
2225 |
2226 | jasmine.WaitsBlock.prototype.execute = function (onComplete) {
2227 | if (jasmine.VERBOSE) {
2228 | this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
2229 | }
2230 | this.env.setTimeout(function () {
2231 | onComplete();
2232 | }, this.timeout);
2233 | };
2234 | /**
2235 | * A block which waits for some condition to become true, with timeout.
2236 | *
2237 | * @constructor
2238 | * @extends jasmine.Block
2239 | * @param {jasmine.Env} env The Jasmine environment.
2240 | * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
2241 | * @param {Function} latchFunction A function which returns true when the desired condition has been met.
2242 | * @param {String} message The message to display if the desired condition hasn't been met within the given time period.
2243 | * @param {jasmine.Spec} spec The Jasmine spec.
2244 | */
2245 | jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
2246 | this.timeout = timeout || env.defaultTimeoutInterval;
2247 | this.latchFunction = latchFunction;
2248 | this.message = message;
2249 | this.totalTimeSpentWaitingForLatch = 0;
2250 | jasmine.Block.call(this, env, null, spec);
2251 | };
2252 | jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
2253 |
2254 | jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
2255 |
2256 | jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
2257 | if (jasmine.VERBOSE) {
2258 | this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
2259 | }
2260 | var latchFunctionResult;
2261 | try {
2262 | latchFunctionResult = this.latchFunction.apply(this.spec);
2263 | } catch (e) {
2264 | this.spec.fail(e);
2265 | onComplete();
2266 | return;
2267 | }
2268 |
2269 | if (latchFunctionResult) {
2270 | onComplete();
2271 | } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
2272 | var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
2273 | this.spec.fail({
2274 | name: 'timeout',
2275 | message: message
2276 | });
2277 |
2278 | this.abort = true;
2279 | onComplete();
2280 | } else {
2281 | this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
2282 | var self = this;
2283 | this.env.setTimeout(function() {
2284 | self.execute(onComplete);
2285 | }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
2286 | }
2287 | };
2288 | // Mock setTimeout, clearTimeout
2289 | // Contributed by Pivotal Computer Systems, www.pivotalsf.com
2290 |
2291 | jasmine.FakeTimer = function() {
2292 | this.reset();
2293 |
2294 | var self = this;
2295 | self.setTimeout = function(funcToCall, millis) {
2296 | self.timeoutsMade++;
2297 | self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);
2298 | return self.timeoutsMade;
2299 | };
2300 |
2301 | self.setInterval = function(funcToCall, millis) {
2302 | self.timeoutsMade++;
2303 | self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);
2304 | return self.timeoutsMade;
2305 | };
2306 |
2307 | self.clearTimeout = function(timeoutKey) {
2308 | self.scheduledFunctions[timeoutKey] = jasmine.undefined;
2309 | };
2310 |
2311 | self.clearInterval = function(timeoutKey) {
2312 | self.scheduledFunctions[timeoutKey] = jasmine.undefined;
2313 | };
2314 |
2315 | };
2316 |
2317 | jasmine.FakeTimer.prototype.reset = function() {
2318 | this.timeoutsMade = 0;
2319 | this.scheduledFunctions = {};
2320 | this.nowMillis = 0;
2321 | };
2322 |
2323 | jasmine.FakeTimer.prototype.tick = function(millis) {
2324 | var oldMillis = this.nowMillis;
2325 | var newMillis = oldMillis + millis;
2326 | this.runFunctionsWithinRange(oldMillis, newMillis);
2327 | this.nowMillis = newMillis;
2328 | };
2329 |
2330 | jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {
2331 | var scheduledFunc;
2332 | var funcsToRun = [];
2333 | for (var timeoutKey in this.scheduledFunctions) {
2334 | scheduledFunc = this.scheduledFunctions[timeoutKey];
2335 | if (scheduledFunc != jasmine.undefined &&
2336 | scheduledFunc.runAtMillis >= oldMillis &&
2337 | scheduledFunc.runAtMillis <= nowMillis) {
2338 | funcsToRun.push(scheduledFunc);
2339 | this.scheduledFunctions[timeoutKey] = jasmine.undefined;
2340 | }
2341 | }
2342 |
2343 | if (funcsToRun.length > 0) {
2344 | funcsToRun.sort(function(a, b) {
2345 | return a.runAtMillis - b.runAtMillis;
2346 | });
2347 | for (var i = 0; i < funcsToRun.length; ++i) {
2348 | try {
2349 | var funcToRun = funcsToRun[i];
2350 | this.nowMillis = funcToRun.runAtMillis;
2351 | funcToRun.funcToCall();
2352 | if (funcToRun.recurring) {
2353 | this.scheduleFunction(funcToRun.timeoutKey,
2354 | funcToRun.funcToCall,
2355 | funcToRun.millis,
2356 | true);
2357 | }
2358 | } catch(e) {
2359 | }
2360 | }
2361 | this.runFunctionsWithinRange(oldMillis, nowMillis);
2362 | }
2363 | };
2364 |
2365 | jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {
2366 | this.scheduledFunctions[timeoutKey] = {
2367 | runAtMillis: this.nowMillis + millis,
2368 | funcToCall: funcToCall,
2369 | recurring: recurring,
2370 | timeoutKey: timeoutKey,
2371 | millis: millis
2372 | };
2373 | };
2374 |
2375 | /**
2376 | * @namespace
2377 | */
2378 | jasmine.Clock = {
2379 | defaultFakeTimer: new jasmine.FakeTimer(),
2380 |
2381 | reset: function() {
2382 | jasmine.Clock.assertInstalled();
2383 | jasmine.Clock.defaultFakeTimer.reset();
2384 | },
2385 |
2386 | tick: function(millis) {
2387 | jasmine.Clock.assertInstalled();
2388 | jasmine.Clock.defaultFakeTimer.tick(millis);
2389 | },
2390 |
2391 | runFunctionsWithinRange: function(oldMillis, nowMillis) {
2392 | jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);
2393 | },
2394 |
2395 | scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
2396 | jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);
2397 | },
2398 |
2399 | useMock: function() {
2400 | if (!jasmine.Clock.isInstalled()) {
2401 | var spec = jasmine.getEnv().currentSpec;
2402 | spec.after(jasmine.Clock.uninstallMock);
2403 |
2404 | jasmine.Clock.installMock();
2405 | }
2406 | },
2407 |
2408 | installMock: function() {
2409 | jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;
2410 | },
2411 |
2412 | uninstallMock: function() {
2413 | jasmine.Clock.assertInstalled();
2414 | jasmine.Clock.installed = jasmine.Clock.real;
2415 | },
2416 |
2417 | real: {
2418 | setTimeout: jasmine.getGlobal().setTimeout,
2419 | clearTimeout: jasmine.getGlobal().clearTimeout,
2420 | setInterval: jasmine.getGlobal().setInterval,
2421 | clearInterval: jasmine.getGlobal().clearInterval
2422 | },
2423 |
2424 | assertInstalled: function() {
2425 | if (!jasmine.Clock.isInstalled()) {
2426 | throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
2427 | }
2428 | },
2429 |
2430 | isInstalled: function() {
2431 | return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;
2432 | },
2433 |
2434 | installed: null
2435 | };
2436 | jasmine.Clock.installed = jasmine.Clock.real;
2437 |
2438 | //else for IE support
2439 | jasmine.getGlobal().setTimeout = function(funcToCall, millis) {
2440 | if (jasmine.Clock.installed.setTimeout.apply) {
2441 | return jasmine.Clock.installed.setTimeout.apply(this, arguments);
2442 | } else {
2443 | return jasmine.Clock.installed.setTimeout(funcToCall, millis);
2444 | }
2445 | };
2446 |
2447 | jasmine.getGlobal().setInterval = function(funcToCall, millis) {
2448 | if (jasmine.Clock.installed.setInterval.apply) {
2449 | return jasmine.Clock.installed.setInterval.apply(this, arguments);
2450 | } else {
2451 | return jasmine.Clock.installed.setInterval(funcToCall, millis);
2452 | }
2453 | };
2454 |
2455 | jasmine.getGlobal().clearTimeout = function(timeoutKey) {
2456 | if (jasmine.Clock.installed.clearTimeout.apply) {
2457 | return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
2458 | } else {
2459 | return jasmine.Clock.installed.clearTimeout(timeoutKey);
2460 | }
2461 | };
2462 |
2463 | jasmine.getGlobal().clearInterval = function(timeoutKey) {
2464 | if (jasmine.Clock.installed.clearTimeout.apply) {
2465 | return jasmine.Clock.installed.clearInterval.apply(this, arguments);
2466 | } else {
2467 | return jasmine.Clock.installed.clearInterval(timeoutKey);
2468 | }
2469 | };
2470 |
2471 | jasmine.version_= {
2472 | "major": 1,
2473 | "minor": 1,
2474 | "build": 0,
2475 | "revision": 1315677058
2476 | };
2477 |
--------------------------------------------------------------------------------