The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .gitignore
├── KoansRunner.html
├── LICENSE
├── README.markdown
├── jasmine
    └── runner_specs
    │   ├── TestJSKoansRunner.html
    │   └── suites
    │       └── KoansRunnerSpec.js
├── koans
    ├── AboutApplyingWhatWeHaveLearnt.js
    ├── AboutArrays.js
    ├── AboutExpects.js
    ├── AboutFunctions.js
    ├── AboutHigherOrderFunctions.js
    ├── AboutInheritance.js
    ├── AboutMutability.js
    └── AboutObjects.js
└── lib
    ├── FILL_ME_IN.js
    ├── jasmine
        ├── jasmine-html.js
        ├── jasmine.css
        ├── jasmine.js
        ├── jskoans-jasmine-html.js
        └── jskoans-jasmine.css
    ├── jsTestDriver
        ├── JsTestDriver.jar
        ├── capture_browser.sh
        ├── jsTestDriver.conf
        └── run_all_koans.sh
    └── underscore-min.js


/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | 


--------------------------------------------------------------------------------
/KoansRunner.html:
--------------------------------------------------------------------------------
 1 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 2 |   "http://www.w3.org/TR/html4/loose.dtd">
 3 | <html>
 4 | <head>
 5 |   <title>Javascript Koans</title>
 6 |   <link rel="stylesheet" type="text/css" href="lib/jasmine/jskoans-jasmine.css">
 7 |   <script type="text/javascript" src="lib/jasmine/jasmine.js"></script>
 8 |   <script type="text/javascript" src="lib/jasmine/jasmine-html.js"></script>
 9 |   <script type="text/javascript" src="lib/jasmine/jskoans-jasmine-html.js"></script>
10 |   
11 |   <script type="text/javascript" src="lib/underscore-min.js"></script>
12 | 
13 |   <script type="text/javascript" src="lib/FILL_ME_IN.js"></script>
14 | 
15 |   <script type="text/javascript" src="koans/AboutExpects.js"></script>
16 |   <script type="text/javascript" src="koans/AboutArrays.js"></script>
17 |   <script type="text/javascript" src="koans/AboutFunctions.js"></script>
18 |   <script type="text/javascript" src="koans/AboutObjects.js"></script>
19 |   <script type="text/javascript" src="koans/AboutMutability.js"></script>
20 |   <script type="text/javascript" src="koans/AboutHigherOrderFunctions.js"></script>
21 |   <script type="text/javascript" src="koans/AboutInheritance.js"></script>
22 |   <script type="text/javascript" src="koans/AboutApplyingWhatWeHaveLearnt.js"></script>
23 | 
24 | </head>
25 | <body>
26 | 
27 | <script type="text/javascript">
28 |   jasmine.getEnv().addReporter(new JsKoansReporter());
29 |   jasmine.getEnv().execute();
30 | </script>
31 | 
32 | </body>
33 | </html>
34 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | Copyright (c) 2010-2014 David Laing and Greg Malcolm
 4 | 
 5 | Permission is hereby granted, free of charge, to any person
 6 | obtaining a copy of this software and associated documentation
 7 | files (the "Software"), to deal in the Software without
 8 | restriction, including without limitation the rights to use,
 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the
11 | Software is furnished to do so, subject to the following
12 | conditions:
13 | 
14 | The above copyright notice and this permission notice shall be
15 | included in all copies or substantial portions of the Software.
16 | 
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 | OTHER DEALINGS IN THE SOFTWARE.
25 | 


--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
 1 | # javascript-koans
 2 | Based on Edgecase's fantastic 
 3 | [Ruby koans](http://github.com/edgecase/ruby_koans), the goal of the
 4 | Javascript koans is to teach you Javascript programming through
 5 | testing.
 6 | 
 7 | When you first run the koans, you'll be presented with a runtime error and a
 8 | stack trace indicating where the error occurred. Your goal is to make the
 9 | error go away. As you fix each error, you should learn something about the
10 | Javascript language and functional programming in general.
11 | 
12 | Your journey towards Javascript enlightenment starts in the koans/AboutExpects.js file. These
13 | koans will be very simple, so don't overthink them! As you progress through
14 | more koans, more and more Javascript syntax will be introduced which will allow
15 | you to solve more complicated problems and use more advanced techniques.
16 | 
17 | ## Running the Koans
18 | Simply navigate to the Javascript Koans folder using a file browser, and
19 | double click on KoansRunnner.html. 
20 | 
21 | Any browser will do, but for the best results Firefox or Chrome is
22 | recommended. More stack trace information shows up for javascript on these
23 | browsers.
24 | 
25 | The first error will be in koans/AboutExpects.js. Fix the first test and
26 | refresh the browser. Rinse and repeat until all tests turn green.
27 | 
28 | The test runner used is [Jasmine](http://jasmine.github.io/) with a customized report viewer.
29 | 
30 | ### Changelog
31 | *  v3 - Nov 2010  - Moved out of branch of functional-koans project, into own top level project
32 | *  v2 - Sept 2010 - Second version based on jasmine (Thanks Greg Malcolm!)
33 | *  v1 - July 2010 - First version based on jsTestDriver
34 | 
35 | ### Acknowledgements
36 | *  Dick Wall (the Java posse) - for bringing the idea of koans to my attention
37 | *  Edgecase - for the great Ruby Koans
38 | *  Douglas Crockford - for Javascript; the good bits
39 | 
40 | ### [MIT Licensed](LICENSE)
41 | 


--------------------------------------------------------------------------------
/jasmine/runner_specs/TestJSKoansRunner.html:
--------------------------------------------------------------------------------
 1 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 2 |   "http://www.w3.org/TR/html4/loose.dtd">
 3 | <html>
 4 | <head>
 5 |   <title>Jasmine Test Runner</title>
 6 |   <link rel="stylesheet" type="text/css" href="../../jasmine/jasmine.css">
 7 |   <script type="text/javascript" src="../jasmine.js"></script>
 8 |   <script type="text/javascript" src="../jasmine-html.js"></script>
 9 | 
10 |   <!-- include source files here... -->
11 |   <script type="text/javascript" src="../jskoans-jasmine-html.js"></script>
12 | 
13 |   <!-- include spec files here... -->
14 |   <script type="text/javascript" src="suites/KoansRunnerSpec.js"></script>
15 | 
16 | </head>
17 | <body>
18 | <script type="text/javascript">
19 |   jasmine.getEnv().addReporter(new jasmine.TrivialReporter());
20 |   jasmine.getEnv().execute();
21 | </script>
22 | 
23 | </body>
24 | </html>
25 | 


--------------------------------------------------------------------------------
/jasmine/runner_specs/suites/KoansRunnerSpec.js:
--------------------------------------------------------------------------------
  1 | describe("KoansRunner", function() {
  2 |   var env;
  3 |   var reporter;
  4 |   var body;
  5 |   var fakeDocument;
  6 | 
  7 |   beforeEach(function() {
  8 |     env = new jasmine.Env();
  9 |     env.updateInterval = 0;
 10 | 
 11 |     body = document.createElement("body");
 12 |     fakeDocument = { body: body, location: { search: "" } };
 13 |     reporter = new JsKoansReporter(fakeDocument);
 14 |   });
 15 | 
 16 |   function fakeSpec(name) {
 17 |     return {
 18 |       getFullName: function() {
 19 |         return name;
 20 |       }
 21 |     };
 22 |   }
 23 | 
 24 |   function fakeSuite(desc) {
 25 |     return {
 26 |       parentSuite: null,
 27 |       description: desc
 28 |     };
 29 |   }
 30 | 
 31 |   function findElements(divs, withClass) {
 32 |     var els = [];
 33 |     for (var i = 0; i < divs.length; i++) {
 34 |       if (divs[i].className == withClass) els.push(divs[i]);
 35 |     }
 36 |     return els;
 37 |   }
 38 | 
 39 |   function findElement(divs, withClass) {
 40 |     var els = findElements(divs, withClass);
 41 |     if (els.length > 0) return els[0];
 42 |     throw new Error("couldn't find div with class " + withClass);
 43 |   }
 44 | 
 45 |   it("should run only specs beginning with spec parameter", function() {
 46 |     fakeDocument.location.search = "?spec=run%20this";
 47 |     expect(reporter.specFilter(fakeSpec("run this"))).toBeTruthy();
 48 |     expect(reporter.specFilter(fakeSpec("not the right spec"))).toBeFalsy();
 49 |     expect(reporter.specFilter(fakeSpec("not run this"))).toBeFalsy();
 50 |   });
 51 | 
 52 |   it("should display empty divs for every suite when the runner is starting", function() {
 53 |     reporter.reportRunnerStarting({
 54 |       env: env,
 55 |       suites: function() {
 56 |         return [ new jasmine.Suite({}, "suite 1", null, null) ];
 57 |       }
 58 |     });
 59 | 
 60 |     var divs = findElements(body.getElementsByTagName("div"), "suite");
 61 |     expect(divs.length).toEqual(1);
 62 |     expect(divs[0].innerHTML).toContain("suite 1");
 63 |   });
 64 | 
 65 |   describe('Matcher reporting', function () {
 66 |     var getErrorMessageDiv = function (body) {
 67 |       var divs = body.getElementsByTagName("div");
 68 |       for (var i = 0; i < divs.length; i++) {
 69 |         if (divs[i].className.match(/errorMessage/)) {
 70 |           return divs[i];
 71 |         }
 72 |       }
 73 |     };
 74 | 
 75 |     var runner, spec, fakeTimer;
 76 |     beforeEach(function () {
 77 |       fakeTimer = new jasmine.FakeTimer();
 78 |       env.setTimeout = fakeTimer.setTimeout;
 79 |       env.clearTimeout = fakeTimer.clearTimeout;
 80 |       env.setInterval = fakeTimer.setInterval;
 81 |       env.clearInterval = fakeTimer.clearInterval;
 82 |       runner = env.currentRunner();
 83 |       var suite = new jasmine.Suite(env, 'some suite');
 84 |       runner.add(suite);
 85 |       spec = new jasmine.Spec(env, suite, 'some spec');
 86 |       suite.add(spec);
 87 |       fakeDocument.location.search = "?";
 88 |       env.addReporter(reporter);
 89 |     });
 90 | 
 91 |     describe('toContain', function () {
 92 |       it('should show actual and expected', function () {
 93 |         spec.runs(function () {
 94 |           this.expect('foo').toContain('bar');
 95 |         });
 96 |         runner.execute();
 97 |         fakeTimer.tick(0);
 98 | 
 99 |         var resultEl = getErrorMessageDiv(body);
100 |         expect(resultEl.innerHTML).toMatch(/foo/);
101 |         expect(resultEl.innerHTML).toMatch(/bar/);
102 |       });
103 |     });
104 |   });
105 | 
106 | 
107 |   describe("failure messages (integration)", function () {
108 |     var spec, results, expectationResult;
109 | 
110 |     beforeEach(function() {
111 |       results = {
112 |         passed: function() {
113 |           return false;
114 |         },
115 |         getItems: function() {
116 |         }};
117 | 
118 |       var suite1 = new jasmine.Suite(env, "suite 1", null, null);
119 | 
120 |       spec = {
121 |         suite: suite1,
122 |         getFullName: function() {
123 |           return "foo";
124 |         },
125 |         results: function() {
126 |           return results;
127 |         }
128 |       };
129 | 
130 |       reporter.reportRunnerStarting({
131 |         env: env,
132 |         suites: function() {
133 |           return [ suite1 ];
134 |         }
135 |       });
136 |     });
137 | 
138 |     it("should add the failure message to the DOM (non-toEquals matchers)", function() {
139 |       expectationResult = new jasmine.ExpectationResult({
140 |         matcherName: "toBeNull", passed: false, message: "Expected 'a' to be null, but it was not"
141 |       });
142 | 
143 |       spyOn(results, 'getItems').andReturn([expectationResult]);
144 | 
145 |       reporter.reportSpecResults(spec);
146 | 
147 |       var divs = body.getElementsByTagName("div");
148 |       var errorDiv = findElement(divs, 'errorMessage');
149 |       expect(errorDiv.innerHTML).toEqual("Expected 'a' to be null, but it was not");
150 |     });
151 | 
152 |     it("should add the failure message to the DOM (non-toEquals matchers) html escaping", function() {
153 |       expectationResult = new jasmine.ExpectationResult({
154 |         matcherName: "toBeNull", passed: false, message: "Expected '1 < 2' to <b>e null, & it was not"
155 |       });
156 | 
157 |       spyOn(results, 'getItems').andReturn([expectationResult]);
158 | 
159 |       reporter.reportSpecResults(spec);
160 | 
161 |       var divs = body.getElementsByTagName("div");
162 |       var errorDiv = findElement(divs, 'errorMessage');
163 |       expect(errorDiv.innerHTML).toEqual("Expected '1 &lt; 2' to &lt;b&gt;e null, &amp; it was not");
164 |     });
165 |   });
166 | 
167 |   describe("duplicate example names", function() {
168 |     it("should report failures correctly", function() {
169 |       var suite1 = env.describe("suite", function() {
170 |         env.it("will have log messages", function() {
171 |           this.log("this one passes!");
172 |           this.expect(true).toBeTruthy();
173 |         });
174 |       });
175 |       
176 |       var suite2 = env.describe("suite", function() {
177 |         env.it("will have log messages", function() {
178 |           this.log("this one fails!");
179 |           this.expect(true).toBeFalsy();
180 |         });
181 |       });
182 | 
183 |       env.addReporter(reporter);
184 |       env.execute();
185 | 
186 |       var divs = body.getElementsByTagName("div");
187 |       var failedSpecDiv = findElement(divs, 'suite failed');
188 |       expect(failedSpecDiv.className).toEqual('suite failed');
189 |       expect(failedSpecDiv.innerHTML).toContain("damaging your karma");
190 |       expect(failedSpecDiv.innerHTML).not.toContain("has expanded your awareness");
191 | 
192 |       var passedSpecDiv = findElement(divs, 'suite passed');
193 |       expect(passedSpecDiv.className).toEqual('suite passed');
194 |       expect(passedSpecDiv.innerHTML).toContain("has expanded your awareness");
195 |       expect(passedSpecDiv.innerHTML).not.toContain("damaging your karma");
196 |     });
197 |   });
198 | 
199 |   describe('#reportSpecStarting', function() {
200 |     var spec1;
201 |     beforeEach(function () {
202 |       env.describe("suite 1", function() {
203 |         spec1 = env.it("spec 1", function() {
204 |         });
205 |       });
206 |     });
207 | 
208 |     it('should not log running specs by default', function() {
209 |       spyOn(reporter, 'log');
210 | 
211 |       reporter.reportSpecStarting(spec1);
212 | 
213 |       expect(reporter.log).not.toHaveBeenCalled();
214 |     });
215 |   });
216 | 
217 |   describe('showing progress', function() {
218 |     beforeEach(function() {
219 |       env.describe("suite 1", function() {
220 |         env.it("spec A");
221 |         env.it("spec B");
222 |       });
223 |       env.describe("suite 2", function() {
224 |         env.it("spec C");
225 |       });
226 |     });
227 | 
228 |     describe('subjects', function() {
229 |       describe("with no failures", function() {
230 |         beforeEach(function() {
231 |           env.addReporter(reporter);
232 |           env.execute();          
233 |         });
234 | 
235 |         it("should have 2 subjects", function() {
236 |           expect(reporter.noOfSubjects).toBe(2);
237 |         });
238 | 
239 |         it("should not have any failed subjects", function() {
240 |           expect(reporter.failedSubjects).toBe(0);
241 |         });
242 |       });
243 | 
244 |       describe("with 1 failure", function() {
245 |         beforeEach(function() {
246 |           env.describe("suite with error", function() {
247 |             env.it("spec X", function() {
248 |               expect(true).toBeFalsey();
249 |             });
250 |             env.it("spec Y", function() {
251 |               expect(true).toBeFalsey();
252 |             });
253 |           });
254 | 
255 |           env.addReporter(reporter);
256 |           env.execute();
257 |         });
258 | 
259 |         it("should have 3 subjects", function() {
260 |           expect(reporter.noOfSubjects).toBe(3);
261 |         });
262 | 
263 |         it("should have a failure", function() {
264 |           expect(reporter.failedSubjects).toBe(1);
265 |         });
266 |       });
267 | 
268 |       describe("with 2 failures", function() {
269 |         beforeEach(function() {
270 |           env.describe("suite with error", function() {
271 |             env.it("spec X", function() {
272 |               expect(true).toBeFalsey();
273 |             });
274 |           });
275 |           env.describe("suite with error too", function() {
276 |             env.it("spec Y", function() {
277 |               expect(true).toBeFalsey();
278 |             });
279 |           });
280 | 
281 |           env.addReporter(reporter);
282 |           env.execute();
283 |         });
284 | 
285 |         it("should have 4 subjects", function() {
286 |           expect(reporter.noOfSubjects).toBe(4);
287 |         });        
288 |         
289 |         it("should have 2 failures", function() {
290 |           expect(reporter.failedSubjects).toBe(2);
291 |         });  
292 |       });
293 | 
294 |       describe("with embedded suites only outer suites count as subjects", function() {
295 |         beforeEach(function() {
296 |           env.describe("outer suite", function() {
297 |             env.it("spec for outer suite", function() {
298 |               expect(true).toBeFalsey();
299 |             });
300 | 
301 |             env.describe("inner suite", function() {
302 |               env.it("spec for inner suite", function() {
303 |                 expect(true).toBeFalsey();
304 |               });
305 |             });
306 |           });
307 | 
308 |           env.addReporter(reporter);
309 |           env.execute();
310 |         });
311 | 
312 |         it("should have 3 suites", function() {
313 |           expect(reporter.noOfSubjects).toBe(3);
314 |         });        
315 |         
316 |         it("should have 1 failure", function() {
317 |           expect(reporter.failedSubjects).toBe(1);
318 |         });          
319 | 
320 |       });
321 |     });
322 |   });
323 | 
324 |   describe("presentation", function() {
325 |     describe("showing the suite description", function() {
326 |       it("should prefix outer suite descriptions with 'Thinking'", function() {
327 |         suite = fakeSuite("About Pies"); 
328 |         description = reporter.getSuiteDescription(suite);
329 | 
330 |         expect(description).toEqual("Thinking About Pies");
331 |       });
332 | 
333 |       it("should prefix inner suite descriptions with 'Thinking'", function() {
334 |         suite = fakeSuite("cherries");
335 |         suite.parentSuite = "Something";
336 |         description = reporter.getSuiteDescription(suite);
337 | 
338 |         expect(description).toEqual("Considering cherries");
339 |       });
340 |     });
341 |   });
342 | });
343 | 


--------------------------------------------------------------------------------
/koans/AboutApplyingWhatWeHaveLearnt.js:
--------------------------------------------------------------------------------
  1 | var _; //globals
  2 | 
  3 | describe("About Applying What We Have Learnt", function() {
  4 | 
  5 |   var products;
  6 | 
  7 |   beforeEach(function () {
  8 |     products = [
  9 |        { name: "Sonoma", ingredients: ["artichoke", "sundried tomatoes", "mushrooms"], containsNuts: false },
 10 |        { name: "Pizza Primavera", ingredients: ["roma", "sundried tomatoes", "goats cheese", "rosemary"], containsNuts: false },
 11 |        { name: "South Of The Border", ingredients: ["black beans", "jalapenos", "mushrooms"], containsNuts: false },
 12 |        { name: "Blue Moon", ingredients: ["blue cheese", "garlic", "walnuts"], containsNuts: true },
 13 |        { name: "Taste Of Athens", ingredients: ["spinach", "kalamata olives", "sesame seeds"], containsNuts: true }
 14 |     ];
 15 |   });
 16 | 
 17 |   /*********************************************************************************/
 18 | 
 19 |   it("given I'm allergic to nuts and hate mushrooms, it should find a pizza I can eat (imperative)", function () {
 20 | 
 21 |     var i,j,hasMushrooms, productsICanEat = [];
 22 | 
 23 |     for (i = 0; i < products.length; i+=1) {
 24 |         if (products[i].containsNuts === false) {
 25 |             hasMushrooms = false;
 26 |             for (j = 0; j < products[i].ingredients.length; j+=1) {
 27 |                if (products[i].ingredients[j] === "mushrooms") {
 28 |                   hasMushrooms = true;
 29 |                }
 30 |             }
 31 |             if (!hasMushrooms) productsICanEat.push(products[i]);
 32 |         }
 33 |     }
 34 | 
 35 |     expect(productsICanEat.length).toBe(FILL_ME_IN);
 36 |   });
 37 | 
 38 |   it("given I'm allergic to nuts and hate mushrooms, it should find a pizza I can eat (functional)", function () {
 39 | 
 40 |       var productsICanEat = [];
 41 | 
 42 |       /* solve using filter() & all() / any() */
 43 | 
 44 |       expect(productsICanEat.length).toBe(FILL_ME_IN);
 45 |   });
 46 | 
 47 |   /*********************************************************************************/
 48 | 
 49 |   it("should add all the natural numbers below 1000 that are multiples of 3 or 5 (imperative)", function () {
 50 | 
 51 |     var sum = 0;
 52 |     for(var i=1; i<1000; i+=1) {
 53 |       if (i % 3 === 0 || i % 5 === 0) {
 54 |         sum += i;
 55 |       }
 56 |     }
 57 | 
 58 |     expect(sum).toBe(FILL_ME_IN);
 59 |   });
 60 | 
 61 |   it("should add all the natural numbers below 1000 that are multiples of 3 or 5 (functional)", function () {
 62 | 
 63 |     var sum = FILL_ME_IN;    /* try chaining range() and reduce() */
 64 | 
 65 |     expect(233168).toBe(FILL_ME_IN);
 66 |   });
 67 | 
 68 |   /*********************************************************************************/
 69 |    it("should count the ingredient occurrence (imperative)", function () {
 70 |     var ingredientCount = { "{ingredient name}": 0 };
 71 | 
 72 |     for (i = 0; i < products.length; i+=1) {
 73 |         for (j = 0; j < products[i].ingredients.length; j+=1) {
 74 |             ingredientCount[products[i].ingredients[j]] = (ingredientCount[products[i].ingredients[j]] || 0) + 1;
 75 |         }
 76 |     }
 77 | 
 78 |     expect(ingredientCount['mushrooms']).toBe(FILL_ME_IN);
 79 |   });
 80 | 
 81 |   it("should count the ingredient occurrence (functional)", function () {
 82 |     var ingredientCount = { "{ingredient name}": 0 };
 83 | 
 84 |     /* chain() together map(), flatten() and reduce() */
 85 | 
 86 |     expect(ingredientCount['mushrooms']).toBe(FILL_ME_IN);
 87 |   });
 88 | 
 89 |   /*********************************************************************************/
 90 |   /* UNCOMMENT FOR EXTRA CREDIT */
 91 |   /*
 92 |   it("should find the largest prime factor of a composite number", function () {
 93 | 
 94 |   });
 95 | 
 96 |   it("should find the largest palindrome made from the product of two 3 digit numbers", function () {
 97 | 
 98 |   });
 99 | 
100 |   it("should find the smallest number divisible by each of the numbers 1 to 20", function () {
101 | 
102 | 
103 |   });
104 | 
105 |   it("should find the difference between the sum of the squares and the square of the sums", function () {
106 | 
107 |   });
108 | 
109 |   it("should find the 10001st prime", function () {
110 | 
111 |   });
112 |   */
113 | });
114 | 


--------------------------------------------------------------------------------
/koans/AboutArrays.js:
--------------------------------------------------------------------------------
 1 | describe("About Arrays", function() {
 2 | 
 3 |   //We shall contemplate truth by testing reality, via spec expectations.
 4 |   it("should create arrays", function() {
 5 |     var emptyArray = [];
 6 |     expect(typeof(emptyArray)).toBe(FILL_ME_IN); //A mistake? - http://javascript.crockford.com/remedial.html
 7 |     expect(emptyArray.length).toBe(FILL_ME_IN);
 8 | 
 9 |     var multiTypeArray = [0, 1, "two", function () { return 3; }, {value1: 4, value2: 5}, [6, 7]];
10 |     expect(multiTypeArray[0]).toBe(FILL_ME_IN);
11 |     expect(multiTypeArray[2]).toBe(FILL_ME_IN);
12 |     expect(multiTypeArray[3]()).toBe(FILL_ME_IN);
13 |     expect(multiTypeArray[4].value1).toBe(FILL_ME_IN);
14 |     expect(multiTypeArray[4]["value2"]).toBe(FILL_ME_IN);
15 |     expect(multiTypeArray[5][0]).toBe(FILL_ME_IN);
16 |   });
17 | 
18 |   it("should understand array literals", function () {
19 |     var array = [];
20 |     expect(array).toEqual([]);
21 | 
22 |     array[0] = 1;
23 |     expect(array).toEqual([1]);
24 | 
25 |     array[1] = 2;
26 |     expect(array).toEqual([1, FILL_ME_IN]);
27 | 
28 |     array.push(3);
29 |     expect(array).toEqual(FILL_ME_IN);
30 |   });
31 | 
32 |   it("should understand array length", function () {
33 |     var fourNumberArray = [1, 2, 3, 4];
34 | 
35 |     expect(fourNumberArray.length).toBe(FILL_ME_IN);
36 |     fourNumberArray.push(5, 6);
37 |     expect(fourNumberArray.length).toBe(FILL_ME_IN);
38 | 
39 |     var tenEmptyElementArray = new Array(10);
40 |     expect(tenEmptyElementArray.length).toBe(FILL_ME_IN);
41 | 
42 |     tenEmptyElementArray.length = 5;
43 |     expect(tenEmptyElementArray.length).toBe(FILL_ME_IN);
44 |   });
45 | 
46 |   it("should slice arrays", function () {
47 |     var array = ["peanut", "butter", "and", "jelly"];
48 | 
49 |     expect(array.slice(0, 1)).toEqual(FILL_ME_IN);
50 |     expect(array.slice(0, 2)).toEqual(FILL_ME_IN);
51 |     expect(array.slice(2, 2)).toEqual(FILL_ME_IN);
52 |     expect(array.slice(2, 20)).toEqual(FILL_ME_IN);
53 |     expect(array.slice(3, 0)).toEqual(FILL_ME_IN);
54 |     expect(array.slice(3, 100)).toEqual(FILL_ME_IN);
55 |     expect(array.slice(5, 1)).toEqual(FILL_ME_IN);
56 |   });
57 | 
58 |   it("should know array references", function () {
59 |     var array = [ "zero", "one", "two", "three", "four", "five" ];
60 | 
61 |     function passedByReference(refArray) {
62 |         refArray[1] = "changed in function";
63 |     }
64 |     passedByReference(array);
65 |     expect(array[1]).toBe(FILL_ME_IN);
66 | 
67 |     var assignedArray = array;
68 |     assignedArray[5] = "changed in assignedArray";
69 |     expect(array[5]).toBe(FILL_ME_IN);
70 | 
71 |     var copyOfArray = array.slice();
72 |     copyOfArray[3] = "changed in copyOfArray";
73 |     expect(array[3]).toBe(FILL_ME_IN);
74 |   });
75 | 
76 |   it("should push and pop", function () {
77 |     var array = [1, 2];
78 |     array.push(3);
79 | 
80 |     expect(array).toEqual(FILL_ME_IN);
81 | 
82 |     var poppedValue = array.pop();
83 |     expect(poppedValue).toBe(FILL_ME_IN);
84 |     expect(array).toEqual(FILL_ME_IN);
85 |   });
86 | 
87 |   it("should know about shifting arrays", function () {
88 |     var array = [1, 2];
89 | 
90 |     array.unshift(3);
91 |     expect(array).toEqual(FILL_ME_IN);
92 | 
93 |     var shiftedValue = array.shift();
94 |     expect(shiftedValue).toEqual(FILL_ME_IN);
95 |     expect(array).toEqual(FILL_ME_IN);
96 |   });
97 | });
98 | 


--------------------------------------------------------------------------------
/koans/AboutExpects.js:
--------------------------------------------------------------------------------
 1 | describe('About Expects', function() {
 2 | 
 3 |   // We shall contemplate truth by testing reality, via spec expectations.
 4 |   it('should expect true', function() {
 5 | 
 6 |     // Your journey begins here: Replace the word false with true 
 7 |     expect(false).toBeTruthy();
 8 |   });
 9 | 
10 |   // To understand reality, we must compare our expectations against reality.
11 |   it('should expect equality', function() {
12 |     var expectedValue = FILL_ME_IN;
13 |     var actualValue = 1 + 1;
14 | 
15 |     expect(actualValue === expectedValue).toBeTruthy();
16 |   });
17 | 
18 |   // Some ways of asserting equality are better than others.
19 |   it('should assert equality a better way', function() {
20 |     var expectedValue = FILL_ME_IN;
21 |     var actualValue = 1 + 1;
22 | 
23 |   // toEqual() compares using common sense equality.
24 |     expect(actualValue).toEqual(expectedValue);
25 |   });
26 | 
27 |   // Sometimes you need to be precise about what you "type."
28 |   it('should assert equality with ===', function() {
29 |     var expectedValue = FILL_ME_IN;
30 |     var actualValue = (1 + 1).toString();
31 | 
32 |   // toBe() will always use === to compare.
33 |     expect(actualValue).toBe(expectedValue);
34 |   });
35 | 
36 |   // Sometimes we will ask you to fill in the values.
37 |   it('should have filled in values', function() {
38 |     expect(1 + 1).toEqual(FILL_ME_IN);
39 |   });
40 | });
41 | 


--------------------------------------------------------------------------------
/koans/AboutFunctions.js:
--------------------------------------------------------------------------------
  1 | describe("About Functions", function() {
  2 | 
  3 |   it("should declare functions", function() {
  4 | 
  5 |     function add(a, b) {
  6 |       return a + b;
  7 |     }
  8 | 
  9 |     expect(add(1, 2)).toBe(FILL_ME_IN);
 10 |   });
 11 | 
 12 |   it("should know internal variables override outer variables", function () {
 13 |     var message = "Outer";
 14 | 
 15 |     function getMessage() {
 16 |       return message;
 17 |     }
 18 | 
 19 |     function overrideMessage() {
 20 |       var message = "Inner";
 21 |       return message;
 22 |     }
 23 | 
 24 |     expect(getMessage()).toBe(FILL_ME_IN);
 25 |     expect(overrideMessage()).toBe(FILL_ME_IN);
 26 |     expect(message).toBe(FILL_ME_IN);
 27 |   });
 28 | 
 29 |   it("should have lexical scoping", function () {
 30 |     var variable = "top-level";
 31 |     function parentfunction() {
 32 |       var variable = "local";
 33 |       function childfunction() {
 34 |         return variable;
 35 |       }
 36 |       return childfunction();
 37 |     }
 38 |     expect(parentfunction()).toBe(FILL_ME_IN);
 39 |   });
 40 | 
 41 |   it("should use lexical scoping to synthesise functions", function () {
 42 | 
 43 |     function makeMysteryFunction(makerValue)
 44 |     {
 45 |       var newFunction = function doMysteriousThing(param)
 46 |       {
 47 |         return makerValue + param;
 48 |       };
 49 |       return newFunction;
 50 |     }
 51 | 
 52 |     var mysteryFunction3 = makeMysteryFunction(3);
 53 |     var mysteryFunction5 = makeMysteryFunction(5);
 54 | 
 55 |     expect(mysteryFunction3(10) + mysteryFunction5(5)).toBe(FILL_ME_IN);
 56 |   });
 57 | 
 58 |   it("should allow extra function arguments", function () {
 59 | 
 60 |     function returnFirstArg(firstArg) {
 61 |       return firstArg;
 62 |     }
 63 | 
 64 |     expect(returnFirstArg("first", "second", "third")).toBe(FILL_ME_IN);
 65 | 
 66 |     function returnSecondArg(firstArg, secondArg) {
 67 |       return secondArg;
 68 |     }
 69 | 
 70 |     expect(returnSecondArg("only give first arg")).toBe(FILL_ME_IN);
 71 | 
 72 |     function returnAllArgs() {
 73 |       var argsArray = [];
 74 |       for (var i = 0; i < arguments.length; i += 1) {
 75 |         argsArray.push(arguments[i]);
 76 |       }
 77 |       return argsArray.join(",");
 78 |     }
 79 | 
 80 |     expect(returnAllArgs("first", "second", "third")).toBe(FILL_ME_IN);
 81 |   });
 82 | 
 83 |   it("should pass functions as values", function () {
 84 | 
 85 |     var appendRules = function (name) {
 86 |       return name + " rules!";
 87 |     };
 88 | 
 89 |     var appendDoubleRules = function (name) {
 90 |       return name + " totally rules!";
 91 |     };
 92 | 
 93 |     var praiseSinger = { givePraise: appendRules };
 94 |     expect(praiseSinger.givePraise("John")).toBe(FILL_ME_IN);
 95 | 
 96 |     praiseSinger.givePraise = appendDoubleRules;
 97 |     expect(praiseSinger.givePraise("Mary")).toBe(FILL_ME_IN);
 98 | 
 99 |   });
100 | });
101 | 


--------------------------------------------------------------------------------
/koans/AboutHigherOrderFunctions.js:
--------------------------------------------------------------------------------
 1 | var _; //globals
 2 | 
 3 | /* This section uses a functional extension known as Underscore.js - http://documentcloud.github.com/underscore/
 4 |      "Underscore is a utility-belt library for JavaScript that provides a lot of the functional programming support
 5 |       that you would expect in Prototype.js (or Ruby), but without extending any of the built-in JavaScript objects.
 6 |       It's the tie to go along with jQuery's tux."
 7 |  */
 8 | describe("About Higher Order Functions", function () {
 9 | 
10 |   it("should use filter to return array items that meet a criteria", function () {
11 |     var numbers = [1,2,3];
12 |     var odd = _(numbers).filter(function (x) { return x % 2 !== 0 });
13 | 
14 |     expect(odd).toEqual(FILL_ME_IN);
15 |     expect(odd.length).toBe(FILL_ME_IN);
16 |     expect(numbers.length).toBe(FILL_ME_IN);
17 |   });
18 | 
19 |   it("should use 'map' to transform each element", function () {
20 |     var numbers = [1, 2, 3];
21 |     var numbersPlus1 = _(numbers).map(function(x) { return x + 1 });
22 | 
23 |     expect(numbersPlus1).toEqual(FILL_ME_IN);
24 |     expect(numbers).toEqual(FILL_ME_IN);
25 |   });
26 | 
27 |   it("should use 'reduce' to update the same result on each iteration", function () {
28 |     var numbers = [1, 2, 3];
29 |     var reduction = _(numbers).reduce(
30 |             function(/* result from last call */ memo, /* current */ x) { return memo + x }, /* initial */ 0);
31 | 
32 |     expect(reduction).toBe(FILL_ME_IN);
33 |     expect(numbers).toEqual(FILL_ME_IN);
34 |   });
35 | 
36 |   it("should use 'forEach' for simple iteration", function () {
37 |     var numbers = [1,2,3];
38 |     var msg = "";
39 |     var isEven = function (item) {
40 |       msg += (item % 2) === 0;
41 |     };
42 | 
43 |     _(numbers).forEach(isEven);
44 | 
45 |     expect(msg).toEqual(FILL_ME_IN);
46 |     expect(numbers).toEqual(FILL_ME_IN);
47 |   });
48 | 
49 |   it("should use 'all' to test whether all items pass condition", function () {
50 |     var onlyEven = [2,4,6];
51 |     var mixedBag = [2,4,5,6];
52 | 
53 |     var isEven = function(x) { return x % 2 === 0 };
54 | 
55 |     expect(_(onlyEven).all(isEven)).toBe(FILL_ME_IN);
56 |     expect(_(mixedBag).all(isEven)).toBe(FILL_ME_IN);
57 |   });
58 | 
59 |   it("should use 'any' to test if any items passes condition" , function () {
60 |     var onlyEven = [2,4,6];
61 |     var mixedBag = [2,4,5,6];
62 | 
63 |     var isEven = function(x) { return x % 2 === 0 };
64 | 
65 |     expect(_(onlyEven).any(isEven)).toBe(FILL_ME_IN);
66 |     expect(_(mixedBag).any(isEven)).toBe(FILL_ME_IN);
67 |   });
68 | 
69 |   it("should use range to generate an array", function() {
70 |       expect(_.range(3)).toEqual(FILL_ME_IN);
71 |       expect(_.range(1, 4)).toEqual(FILL_ME_IN);
72 |       expect(_.range(0, -4, -1)).toEqual(FILL_ME_IN);
73 |   });
74 | 
75 |   it("should use flatten to make nested arrays easy to work with", function() {
76 |       expect(_([ [1, 2], [3, 4] ]).flatten()).toEqual(FILL_ME_IN);
77 |   });
78 | 
79 |   it("should use chain() ... .value() to use multiple higher order functions", function() {
80 |       var result = _([ [0, 1], 2 ]).chain()
81 |                        .flatten()
82 |                        .map(function(x) { return x+1 } )
83 |                        .reduce(function (sum, x) { return sum + x })
84 |                        .value();
85 | 
86 |       expect(result).toEqual(FILL_ME_IN);
87 |   });
88 | 
89 | });
90 | 
91 | 


--------------------------------------------------------------------------------
/koans/AboutInheritance.js:
--------------------------------------------------------------------------------
 1 | function Muppet(age, hobby) {
 2 |   this.age = age;
 3 |   this.hobby = hobby;
 4 | 
 5 |   this.answerNanny = function(){
 6 | 	return "Everything's cool!";
 7 |   }
 8 | }
 9 | 
10 | function SwedishChef(age, hobby, mood) {
11 |   Muppet.call(this, age, hobby);
12 |   this.mood = mood;
13 | 
14 |   this.cook = function() {
15 |     return "Mmmm soup!";
16 |   }
17 | }
18 | 
19 | SwedishChef.prototype = new Muppet();
20 | 
21 | describe("About inheritance", function() {
22 |   beforeEach(function(){
23 |     this.muppet = new Muppet(2, "coding");
24 | 	this.swedishChef = new SwedishChef(2, "cooking", "chillin");
25 |   });
26 | 
27 |   it("should be able to call a method on the derived object", function() {
28 |     expect(this.swedishChef.cook()).toEqual(FILL_ME_IN);
29 |   });
30 | 
31 |   it("should be able to call a method on the base object", function() {
32 |     expect(this.swedishChef.answerNanny()).toEqual(FILL_ME_IN);
33 |   });
34 | 
35 |   it("should set constructor parameters on the base object", function() {
36 |     expect(this.swedishChef.age).toEqual(FILL_ME_IN);
37 |     expect(this.swedishChef.hobby).toEqual(FILL_ME_IN);
38 |   });
39 | 
40 |   it("should set constructor parameters on the derived object", function() {
41 |     expect(this.swedishChef.mood).toEqual(FILL_ME_IN);
42 |   });
43 | });
44 | 
45 | // http://javascript.crockford.com/prototypal.html
46 | Object.prototype.beget = function () {
47 |   function F() {}
48 |   F.prototype = this;
49 |   return new F();
50 | }
51 | 
52 | function Gonzo(age, hobby, trick) {
53 |   Muppet.call(this, age, hobby);
54 |   this.trick = trick;
55 | 
56 |   this.doTrick = function() {
57 |     return this.trick;
58 |   }
59 | }
60 | 
61 | //no longer need to call the Muppet (base type) constructor
62 | Gonzo.prototype = Muppet.prototype.beget();
63 | 
64 | describe("About Crockford's inheritance improvement", function() {
65 |   beforeEach(function(){
66 |   this.gonzo = new Gonzo(3, "daredevil performer", "eat a tire");
67 |   });
68 | 
69 |   it("should be able to call a method on the derived object", function() {
70 |     expect(this.gonzo.doTrick()).toEqual(FILL_ME_IN);
71 |   });
72 | 
73 |   it("should be able to call a method on the base object", function() {
74 |     expect(this.gonzo.answerNanny()).toEqual(FILL_ME_IN);
75 |   });
76 | 
77 |   it("should set constructor parameters on the base object", function() {
78 |     expect(this.gonzo.age).toEqual(FILL_ME_IN);
79 |     expect(this.gonzo.hobby).toEqual(FILL_ME_IN);
80 |   });
81 | 
82 |   it("should set constructor parameters on the derived object", function() {
83 |     expect(this.gonzo.trick).toEqual(FILL_ME_IN);
84 |   });
85 | });
86 | 


--------------------------------------------------------------------------------
/koans/AboutMutability.js:
--------------------------------------------------------------------------------
 1 | describe("About Mutability", function() {
 2 | 
 3 |   it("should expect object properties to be public and mutable", function () {
 4 |     var aPerson = {firstname: "John", lastname: "Smith" };
 5 |     aPerson.firstname = "Alan";
 6 | 
 7 |     expect(aPerson.firstname).toBe(FILL_ME_IN);
 8 |   });
 9 | 
10 |   it("should understand that constructed properties are public and mutable", function () {
11 |     function Person(firstname, lastname)
12 |     {
13 |       this.firstname = firstname;
14 |       this.lastname = lastname;
15 |     }
16 |     var aPerson = new Person ("John", "Smith");
17 |     aPerson.firstname = "Alan";
18 | 
19 |     expect(aPerson.firstname).toBe(FILL_ME_IN);
20 |   });
21 | 
22 |   it("should expect prototype properties to be public and mutable", function () {
23 |     function Person(firstname, lastname)
24 |     {
25 |       this.firstname = firstname;
26 |       this.lastname = lastname;
27 |     }
28 |     Person.prototype.getFullName = function () {
29 |       return this.firstname + " " + this.lastname;
30 |     };
31 | 
32 |     var aPerson = new Person ("John", "Smith");
33 |     expect(aPerson.getFullName()).toBe(FILL_ME_IN);
34 | 
35 |     aPerson.getFullName = function () {
36 |       return this.lastname + ", " + this.firstname;
37 |     };
38 | 
39 |     expect(aPerson.getFullName()).toBe(FILL_ME_IN);
40 |   });
41 | 
42 |   it("should know that variables inside a constructor and constructor args are private", function () {
43 |     function Person(firstname, lastname)
44 |     {
45 |       var fullName = firstname + " " + lastname;
46 | 
47 |       this.getFirstName = function () { return firstname; };
48 |       this.getLastName = function () { return lastname; };
49 |       this.getFullName = function () { return fullName; };
50 |     }
51 |     var aPerson = new Person ("John", "Smith");
52 | 
53 |     aPerson.firstname = "Penny";
54 |     aPerson.lastname = "Andrews";
55 |     aPerson.fullName = "Penny Andrews";
56 | 
57 |     expect(aPerson.getFirstName()).toBe(FILL_ME_IN);
58 |     expect(aPerson.getLastName()).toBe(FILL_ME_IN);
59 |     expect(aPerson.getFullName()).toBe(FILL_ME_IN);
60 | 
61 |     aPerson.getFullName = function () {
62 |       return aPerson.lastname + ", " + aPerson.firstname;
63 |     };
64 | 
65 |     expect(aPerson.getFullName()).toBe(FILL_ME_IN);
66 |   });
67 | 
68 | });
69 | 


--------------------------------------------------------------------------------
/koans/AboutObjects.js:
--------------------------------------------------------------------------------
  1 | describe("About Objects", function () {
  2 | 
  3 |   describe("Properties", function () {
  4 |     var megalomaniac;
  5 | 
  6 |     beforeEach(function () {
  7 |        megalomaniac = {  mastermind: "Joker", henchwoman: "Harley" };
  8 |     });
  9 | 
 10 |     it("should confirm objects are collections of properties", function () {
 11 |       expect(megalomaniac.mastermind).toBe(FILL_ME_IN);
 12 |     });
 13 | 
 14 |     it("should confirm that properties are case sensitive", function () {
 15 |       expect(megalomaniac.henchwoman).toBe(FILL_ME_IN);
 16 |       expect(megalomaniac.henchWoman).toBe(FILL_ME_IN);
 17 |     });
 18 |   });
 19 | 
 20 | 
 21 |   it("should know properties that are functions act like methods", function () {
 22 |     var megalomaniac = {
 23 |       mastermind : "Brain",
 24 |       henchman: "Pinky",
 25 |       battleCry: function (noOfBrains) {
 26 |         return "They are " + this.henchman + " and the" +
 27 |           Array(noOfBrains + 1).join(" " + this.mastermind);
 28 |       }
 29 |     };
 30 | 
 31 |     var battleCry = megalomaniac.battleCry(4);
 32 |     expect(FILL_ME_IN).toMatch(battleCry);
 33 |   });
 34 | 
 35 |   it("should confirm that when a function is attached to an object, 'this' refers to the object", function () {
 36 |     var currentDate = new Date();
 37 |     var currentYear = (currentDate.getFullYear());
 38 |     var megalomaniac = {
 39 |       mastermind: "James Wood",
 40 |       henchman: "Adam West",
 41 |       birthYear: 1970,
 42 |       calculateAge: function () {
 43 |         return currentYear - this.birthYear;
 44 |       }
 45 |     };
 46 | 
 47 |     expect(currentYear).toBe(FILL_ME_IN);
 48 |     expect(megalomaniac.calculateAge()).toBe(FILL_ME_IN);
 49 |   });
 50 | 
 51 |   describe("'in' keyword", function () {
 52 |     var megalomaniac;
 53 |     beforeEach(function () {
 54 |       megalomaniac = {
 55 |         mastermind: "The Monarch",
 56 |         henchwoman: "Dr Girlfriend",
 57 |         theBomb: true
 58 |       };
 59 |     });
 60 | 
 61 |     it("should have the bomb", function () {
 62 | 
 63 |       var hasBomb = "theBomb" in megalomaniac;
 64 | 
 65 |       expect(hasBomb).toBe(FILL_ME_IN);
 66 |     });
 67 | 
 68 |     it("should not have the detonator however", function () {
 69 | 
 70 |       var hasDetonator = "theDetonator" in megalomaniac;
 71 | 
 72 |       expect(hasDetonator).toBe(FILL_ME_IN);
 73 |     });
 74 |   });
 75 | 
 76 |   it("should know that properties can be added and deleted", function () {
 77 |     var megalomaniac = { mastermind : "Agent Smith", henchman: "Agent Smith" };
 78 | 
 79 |     expect("secretary" in megalomaniac).toBe(FILL_ME_IN);
 80 | 
 81 |     megalomaniac.secretary = "Agent Smith";
 82 |     expect("secretary" in megalomaniac).toBe(FILL_ME_IN);
 83 | 
 84 |     delete megalomaniac.henchman;
 85 |     expect("henchman" in megalomaniac).toBe(FILL_ME_IN);
 86 |   });
 87 | 
 88 | 
 89 |   it("should use prototype to add to all objects", function () {
 90 |       function Circle(radius)
 91 |       {
 92 |         this.radius = radius;
 93 |       }
 94 | 
 95 |       var simpleCircle = new Circle(10);
 96 |       var colouredCircle = new Circle(5);
 97 |       colouredCircle.colour = "red";
 98 | 
 99 |       expect(simpleCircle.colour).toBe(FILL_ME_IN);
100 |       expect(colouredCircle.colour).toBe(FILL_ME_IN);
101 | 
102 |       Circle.prototype.describe = function () {
103 |         return "This circle has a radius of: " + this.radius;
104 |       };
105 | 
106 |       expect(simpleCircle.describe()).toBe(FILL_ME_IN);
107 |       expect(colouredCircle.describe()).toBe(FILL_ME_IN);
108 |   });
109 | });
110 | 


--------------------------------------------------------------------------------
/lib/FILL_ME_IN.js:
--------------------------------------------------------------------------------
1 | var FILL_ME_IN = "Fill this value in";
2 | 


--------------------------------------------------------------------------------
/lib/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 |             "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.onchange = function(evt) {
 74 |     if (evt.target.checked) {
 75 |       self.outerDiv.className += ' show-passed';
 76 |     } else {
 77 |       self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
 78 |     }
 79 |   };
 80 | 
 81 |   showSkipped.onchange = function(evt) {
 82 |     if (evt.target.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) console.log.apply(console, arguments);
166 | };
167 | 
168 | jasmine.TrivialReporter.prototype.getLocation = function() {
169 |   return this.document.location;
170 | };
171 | 
172 | jasmine.TrivialReporter.prototype.specFilter = function(spec) {
173 |   var paramMap = {};
174 |   var params = this.getLocation().search.substring(1).split('&');
175 |   for (var i = 0; i < params.length; i++) {
176 |     var p = params[i].split('=');
177 |     paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
178 |   }
179 | 
180 |   if (!paramMap["spec"]) return true;
181 |   return spec.getFullName().indexOf(paramMap["spec"]) == 0;
182 | };
183 | 


--------------------------------------------------------------------------------
/lib/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 | 


--------------------------------------------------------------------------------
/lib/jasmine/jasmine.js:
--------------------------------------------------------------------------------
   1 | /**
   2 |  * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
   3 |  *
   4 |  * @namespace
   5 |  */
   6 | var jasmine = {};
   7 | 
   8 | /**
   9 |  * @private
  10 |  */
  11 | jasmine.unimplementedMethod_ = function() {
  12 |   throw new Error("unimplemented method");
  13 | };
  14 | 
  15 | /**
  16 |  * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just
  17 |  * a plain old variable and may be redefined by somebody else.
  18 |  *
  19 |  * @private
  20 |  */
  21 | jasmine.undefined = jasmine.___undefined___;
  22 | 
  23 | /**
  24 |  * 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.
  25 |  *
  26 |  */
  27 | jasmine.DEFAULT_UPDATE_INTERVAL = 250;
  28 | 
  29 | /**
  30 |  * Default timeout interval in milliseconds for waitsFor() blocks.
  31 |  */
  32 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
  33 | 
  34 | jasmine.getGlobal = function() {
  35 |   function getGlobal() {
  36 |     return this;
  37 |   }
  38 | 
  39 |   return getGlobal();
  40 | };
  41 | 
  42 | /**
  43 |  * Allows for bound functions to be compared.  Internal use only.
  44 |  *
  45 |  * @ignore
  46 |  * @private
  47 |  * @param base {Object} bound 'this' for the function
  48 |  * @param name {Function} function to find
  49 |  */
  50 | jasmine.bindOriginal_ = function(base, name) {
  51 |   var original = base[name];
  52 |   if (original.apply) {
  53 |     return function() {
  54 |       return original.apply(base, arguments);
  55 |     };
  56 |   } else {
  57 |     // IE support
  58 |     return jasmine.getGlobal()[name];
  59 |   }
  60 | };
  61 | 
  62 | jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
  63 | jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
  64 | jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
  65 | jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
  66 | 
  67 | jasmine.MessageResult = function(values) {
  68 |   this.type = 'log';
  69 |   this.values = values;
  70 |   this.trace = new Error(); // todo: test better
  71 | };
  72 | 
  73 | jasmine.MessageResult.prototype.toString = function() {
  74 |   var text = "";
  75 |   for(var i = 0; i < this.values.length; i++) {
  76 |     if (i > 0) text += " ";
  77 |     if (jasmine.isString_(this.values[i])) {
  78 |       text += this.values[i];
  79 |     } else {
  80 |       text += jasmine.pp(this.values[i]);
  81 |     }
  82 |   }
  83 |   return text;
  84 | };
  85 | 
  86 | jasmine.ExpectationResult = function(params) {
  87 |   this.type = 'expect';
  88 |   this.matcherName = params.matcherName;
  89 |   this.passed_ = params.passed;
  90 |   this.expected = params.expected;
  91 |   this.actual = params.actual;
  92 | 
  93 |   this.message = this.passed_ ? 'Passed.' : params.message;
  94 |   this.trace = this.passed_ ? '' : new Error(this.message);
  95 | };
  96 | 
  97 | jasmine.ExpectationResult.prototype.toString = function () {
  98 |   return this.message;
  99 | };
 100 | 
 101 | jasmine.ExpectationResult.prototype.passed = function () {
 102 |   return this.passed_;
 103 | };
 104 | 
 105 | /**
 106 |  * Getter for the Jasmine environment. Ensures one gets created
 107 |  */
 108 | jasmine.getEnv = function() {
 109 |   return jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
 110 | };
 111 | 
 112 | /**
 113 |  * @ignore
 114 |  * @private
 115 |  * @param value
 116 |  * @returns {Boolean}
 117 |  */
 118 | jasmine.isArray_ = function(value) {
 119 |   return jasmine.isA_("Array", value);  
 120 | };
 121 | 
 122 | /**
 123 |  * @ignore
 124 |  * @private
 125 |  * @param value
 126 |  * @returns {Boolean}
 127 |  */
 128 | jasmine.isString_ = function(value) {
 129 |   return jasmine.isA_("String", value);
 130 | };
 131 | 
 132 | /**
 133 |  * @ignore
 134 |  * @private
 135 |  * @param value
 136 |  * @returns {Boolean}
 137 |  */
 138 | jasmine.isNumber_ = function(value) {
 139 |   return jasmine.isA_("Number", value);
 140 | };
 141 | 
 142 | /**
 143 |  * @ignore
 144 |  * @private
 145 |  * @param {String} typeName
 146 |  * @param value
 147 |  * @returns {Boolean}
 148 |  */
 149 | jasmine.isA_ = function(typeName, value) {
 150 |   return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
 151 | };
 152 | 
 153 | /**
 154 |  * Pretty printer for expecations.  Takes any object and turns it into a human-readable string.
 155 |  *
 156 |  * @param value {Object} an object to be outputted
 157 |  * @returns {String}
 158 |  */
 159 | jasmine.pp = function(value) {
 160 |   var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
 161 |   stringPrettyPrinter.format(value);
 162 |   return stringPrettyPrinter.string;
 163 | };
 164 | 
 165 | /**
 166 |  * Returns true if the object is a DOM Node.
 167 |  *
 168 |  * @param {Object} obj object to check
 169 |  * @returns {Boolean}
 170 |  */
 171 | jasmine.isDomNode = function(obj) {
 172 |   return obj['nodeType'] > 0;
 173 | };
 174 | 
 175 | /**
 176 |  * Returns a matchable 'generic' object of the class type.  For use in expecations of type when values don't matter.
 177 |  *
 178 |  * @example
 179 |  * // don't care about which function is passed in, as long as it's a function
 180 |  * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
 181 |  *
 182 |  * @param {Class} clazz
 183 |  * @returns matchable object of the type clazz
 184 |  */
 185 | jasmine.any = function(clazz) {
 186 |   return new jasmine.Matchers.Any(clazz);
 187 | };
 188 | 
 189 | /**
 190 |  * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
 191 |  *
 192 |  * Spies should be created in test setup, before expectations.  They can then be checked, using the standard Jasmine
 193 |  * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
 194 |  *
 195 |  * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
 196 |  *
 197 |  * Spies are torn down at the end of every spec.
 198 |  *
 199 |  * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
 200 |  *
 201 |  * @example
 202 |  * // a stub
 203 |  * var myStub = jasmine.createSpy('myStub');  // can be used anywhere
 204 |  *
 205 |  * // spy example
 206 |  * var foo = {
 207 |  *   not: function(bool) { return !bool; }
 208 |  * }
 209 |  *
 210 |  * // actual foo.not will not be called, execution stops
 211 |  * spyOn(foo, 'not');
 212 | 
 213 |  // foo.not spied upon, execution will continue to implementation
 214 |  * spyOn(foo, 'not').andCallThrough();
 215 |  *
 216 |  * // fake example
 217 |  * var foo = {
 218 |  *   not: function(bool) { return !bool; }
 219 |  * }
 220 |  *
 221 |  * // foo.not(val) will return val
 222 |  * spyOn(foo, 'not').andCallFake(function(value) {return value;});
 223 |  *
 224 |  * // mock example
 225 |  * foo.not(7 == 7);
 226 |  * expect(foo.not).toHaveBeenCalled();
 227 |  * expect(foo.not).toHaveBeenCalledWith(true);
 228 |  *
 229 |  * @constructor
 230 |  * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
 231 |  * @param {String} name
 232 |  */
 233 | jasmine.Spy = function(name) {
 234 |   /**
 235 |    * The name of the spy, if provided.
 236 |    */
 237 |   this.identity = name || 'unknown';
 238 |   /**
 239 |    *  Is this Object a spy?
 240 |    */
 241 |   this.isSpy = true;
 242 |   /**
 243 |    * The actual function this spy stubs.
 244 |    */
 245 |   this.plan = function() {
 246 |   };
 247 |   /**
 248 |    * Tracking of the most recent call to the spy.
 249 |    * @example
 250 |    * var mySpy = jasmine.createSpy('foo');
 251 |    * mySpy(1, 2);
 252 |    * mySpy.mostRecentCall.args = [1, 2];
 253 |    */
 254 |   this.mostRecentCall = {};
 255 | 
 256 |   /**
 257 |    * Holds arguments for each call to the spy, indexed by call count
 258 |    * @example
 259 |    * var mySpy = jasmine.createSpy('foo');
 260 |    * mySpy(1, 2);
 261 |    * mySpy(7, 8);
 262 |    * mySpy.mostRecentCall.args = [7, 8];
 263 |    * mySpy.argsForCall[0] = [1, 2];
 264 |    * mySpy.argsForCall[1] = [7, 8];
 265 |    */
 266 |   this.argsForCall = [];
 267 |   this.calls = [];
 268 | };
 269 | 
 270 | /**
 271 |  * Tells a spy to call through to the actual implemenatation.
 272 |  *
 273 |  * @example
 274 |  * var foo = {
 275 |  *   bar: function() { // do some stuff }
 276 |  * }
 277 |  *
 278 |  * // defining a spy on an existing property: foo.bar
 279 |  * spyOn(foo, 'bar').andCallThrough();
 280 |  */
 281 | jasmine.Spy.prototype.andCallThrough = function() {
 282 |   this.plan = this.originalValue;
 283 |   return this;
 284 | };
 285 | 
 286 | /**
 287 |  * For setting the return value of a spy.
 288 |  *
 289 |  * @example
 290 |  * // defining a spy from scratch: foo() returns 'baz'
 291 |  * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
 292 |  *
 293 |  * // defining a spy on an existing property: foo.bar() returns 'baz'
 294 |  * spyOn(foo, 'bar').andReturn('baz');
 295 |  *
 296 |  * @param {Object} value
 297 |  */
 298 | jasmine.Spy.prototype.andReturn = function(value) {
 299 |   this.plan = function() {
 300 |     return value;
 301 |   };
 302 |   return this;
 303 | };
 304 | 
 305 | /**
 306 |  * For throwing an exception when a spy is called.
 307 |  *
 308 |  * @example
 309 |  * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
 310 |  * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
 311 |  *
 312 |  * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
 313 |  * spyOn(foo, 'bar').andThrow('baz');
 314 |  *
 315 |  * @param {String} exceptionMsg
 316 |  */
 317 | jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
 318 |   this.plan = function() {
 319 |     throw exceptionMsg;
 320 |   };
 321 |   return this;
 322 | };
 323 | 
 324 | /**
 325 |  * Calls an alternate implementation when a spy is called.
 326 |  *
 327 |  * @example
 328 |  * var baz = function() {
 329 |  *   // do some stuff, return something
 330 |  * }
 331 |  * // defining a spy from scratch: foo() calls the function baz
 332 |  * var foo = jasmine.createSpy('spy on foo').andCall(baz);
 333 |  *
 334 |  * // defining a spy on an existing property: foo.bar() calls an anonymnous function
 335 |  * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
 336 |  *
 337 |  * @param {Function} fakeFunc
 338 |  */
 339 | jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
 340 |   this.plan = fakeFunc;
 341 |   return this;
 342 | };
 343 | 
 344 | /**
 345 |  * Resets all of a spy's the tracking variables so that it can be used again.
 346 |  *
 347 |  * @example
 348 |  * spyOn(foo, 'bar');
 349 |  *
 350 |  * foo.bar();
 351 |  *
 352 |  * expect(foo.bar.callCount).toEqual(1);
 353 |  *
 354 |  * foo.bar.reset();
 355 |  *
 356 |  * expect(foo.bar.callCount).toEqual(0);
 357 |  */
 358 | jasmine.Spy.prototype.reset = function() {
 359 |   this.wasCalled = false;
 360 |   this.callCount = 0;
 361 |   this.argsForCall = [];
 362 |   this.calls = [];
 363 |   this.mostRecentCall = {};
 364 | };
 365 | 
 366 | jasmine.createSpy = function(name) {
 367 | 
 368 |   var spyObj = function() {
 369 |     spyObj.wasCalled = true;
 370 |     spyObj.callCount++;
 371 |     var args = jasmine.util.argsToArray(arguments);
 372 |     spyObj.mostRecentCall.object = this;
 373 |     spyObj.mostRecentCall.args = args;
 374 |     spyObj.argsForCall.push(args);
 375 |     spyObj.calls.push({object: this, args: args});
 376 |     return spyObj.plan.apply(this, arguments);
 377 |   };
 378 | 
 379 |   var spy = new jasmine.Spy(name);
 380 | 
 381 |   for (var prop in spy) {
 382 |     spyObj[prop] = spy[prop];
 383 |   }
 384 | 
 385 |   spyObj.reset();
 386 | 
 387 |   return spyObj;
 388 | };
 389 | 
 390 | /**
 391 |  * Determines whether an object is a spy.
 392 |  *
 393 |  * @param {jasmine.Spy|Object} putativeSpy
 394 |  * @returns {Boolean}
 395 |  */
 396 | jasmine.isSpy = function(putativeSpy) {
 397 |   return putativeSpy && putativeSpy.isSpy;
 398 | };
 399 | 
 400 | /**
 401 |  * Creates a more complicated spy: an Object that has every property a function that is a spy.  Used for stubbing something
 402 |  * large in one call.
 403 |  *
 404 |  * @param {String} baseName name of spy class
 405 |  * @param {Array} methodNames array of names of methods to make spies
 406 |  */
 407 | jasmine.createSpyObj = function(baseName, methodNames) {
 408 |   if (!jasmine.isArray_(methodNames) || methodNames.length == 0) {
 409 |     throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
 410 |   }
 411 |   var obj = {};
 412 |   for (var i = 0; i < methodNames.length; i++) {
 413 |     obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
 414 |   }
 415 |   return obj;
 416 | };
 417 | 
 418 | /**
 419 |  * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
 420 |  *
 421 |  * Be careful not to leave calls to <code>jasmine.log</code> in production code.
 422 |  */
 423 | jasmine.log = function() {
 424 |   var spec = jasmine.getEnv().currentSpec;
 425 |   spec.log.apply(spec, arguments);
 426 | };
 427 | 
 428 | /**
 429 |  * Function that installs a spy on an existing object's method name.  Used within a Spec to create a spy.
 430 |  *
 431 |  * @example
 432 |  * // spy example
 433 |  * var foo = {
 434 |  *   not: function(bool) { return !bool; }
 435 |  * }
 436 |  * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
 437 |  *
 438 |  * @see jasmine.createSpy
 439 |  * @param obj
 440 |  * @param methodName
 441 |  * @returns a Jasmine spy that can be chained with all spy methods
 442 |  */
 443 | var spyOn = function(obj, methodName) {
 444 |   return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
 445 | };
 446 | 
 447 | /**
 448 |  * Creates a Jasmine spec that will be added to the current suite.
 449 |  *
 450 |  * // TODO: pending tests
 451 |  *
 452 |  * @example
 453 |  * it('should be true', function() {
 454 |  *   expect(true).toEqual(true);
 455 |  * });
 456 |  *
 457 |  * @param {String} desc description of this specification
 458 |  * @param {Function} func defines the preconditions and expectations of the spec
 459 |  */
 460 | var it = function(desc, func) {
 461 |   return jasmine.getEnv().it(desc, func);
 462 | };
 463 | 
 464 | /**
 465 |  * Creates a <em>disabled</em> Jasmine spec.
 466 |  *
 467 |  * A convenience method that allows existing specs to be disabled temporarily during development.
 468 |  *
 469 |  * @param {String} desc description of this specification
 470 |  * @param {Function} func defines the preconditions and expectations of the spec
 471 |  */
 472 | var xit = function(desc, func) {
 473 |   return jasmine.getEnv().xit(desc, func);
 474 | };
 475 | 
 476 | /**
 477 |  * Starts a chain for a Jasmine expectation.
 478 |  *
 479 |  * It is passed an Object that is the actual value and should chain to one of the many
 480 |  * jasmine.Matchers functions.
 481 |  *
 482 |  * @param {Object} actual Actual value to test against and expected value
 483 |  */
 484 | var expect = function(actual) {
 485 |   return jasmine.getEnv().currentSpec.expect(actual);
 486 | };
 487 | 
 488 | /**
 489 |  * Defines part of a jasmine spec.  Used in cominbination with waits or waitsFor in asynchrnous specs.
 490 |  *
 491 |  * @param {Function} func Function that defines part of a jasmine spec.
 492 |  */
 493 | var runs = function(func) {
 494 |   jasmine.getEnv().currentSpec.runs(func);
 495 | };
 496 | 
 497 | /**
 498 |  * Waits a fixed time period before moving to the next block.
 499 |  *
 500 |  * @deprecated Use waitsFor() instead
 501 |  * @param {Number} timeout milliseconds to wait
 502 |  */
 503 | var waits = function(timeout) {
 504 |   jasmine.getEnv().currentSpec.waits(timeout);
 505 | };
 506 | 
 507 | /**
 508 |  * Waits for the latchFunction to return true before proceeding to the next block.
 509 |  *
 510 |  * @param {Function} latchFunction
 511 |  * @param {String} optional_timeoutMessage
 512 |  * @param {Number} optional_timeout
 513 |  */
 514 | var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
 515 |   jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
 516 | };
 517 | 
 518 | /**
 519 |  * A function that is called before each spec in a suite.
 520 |  *
 521 |  * Used for spec setup, including validating assumptions.
 522 |  *
 523 |  * @param {Function} beforeEachFunction
 524 |  */
 525 | var beforeEach = function(beforeEachFunction) {
 526 |   jasmine.getEnv().beforeEach(beforeEachFunction);
 527 | };
 528 | 
 529 | /**
 530 |  * A function that is called after each spec in a suite.
 531 |  *
 532 |  * Used for restoring any state that is hijacked during spec execution.
 533 |  *
 534 |  * @param {Function} afterEachFunction
 535 |  */
 536 | var afterEach = function(afterEachFunction) {
 537 |   jasmine.getEnv().afterEach(afterEachFunction);
 538 | };
 539 | 
 540 | /**
 541 |  * Defines a suite of specifications.
 542 |  *
 543 |  * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
 544 |  * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
 545 |  * of setup in some tests.
 546 |  *
 547 |  * @example
 548 |  * // TODO: a simple suite
 549 |  *
 550 |  * // TODO: a simple suite with a nested describe block
 551 |  *
 552 |  * @param {String} description A string, usually the class under test.
 553 |  * @param {Function} specDefinitions function that defines several specs.
 554 |  */
 555 | var describe = function(description, specDefinitions) {
 556 |   return jasmine.getEnv().describe(description, specDefinitions);
 557 | };
 558 | 
 559 | /**
 560 |  * Disables a suite of specifications.  Used to disable some suites in a file, or files, temporarily during development.
 561 |  *
 562 |  * @param {String} description A string, usually the class under test.
 563 |  * @param {Function} specDefinitions function that defines several specs.
 564 |  */
 565 | var xdescribe = function(description, specDefinitions) {
 566 |   return jasmine.getEnv().xdescribe(description, specDefinitions);
 567 | };
 568 | 
 569 | 
 570 | // Provide the XMLHttpRequest class for IE 5.x-6.x:
 571 | jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
 572 |   try {
 573 |     return new ActiveXObject("Msxml2.XMLHTTP.6.0");
 574 |   } catch(e) {
 575 |   }
 576 |   try {
 577 |     return new ActiveXObject("Msxml2.XMLHTTP.3.0");
 578 |   } catch(e) {
 579 |   }
 580 |   try {
 581 |     return new ActiveXObject("Msxml2.XMLHTTP");
 582 |   } catch(e) {
 583 |   }
 584 |   try {
 585 |     return new ActiveXObject("Microsoft.XMLHTTP");
 586 |   } catch(e) {
 587 |   }
 588 |   throw new Error("This browser does not support XMLHttpRequest.");
 589 | } : XMLHttpRequest;
 590 | /**
 591 |  * @namespace
 592 |  */
 593 | jasmine.util = {};
 594 | 
 595 | /**
 596 |  * Declare that a child class inherit it's prototype from the parent class.
 597 |  *
 598 |  * @private
 599 |  * @param {Function} childClass
 600 |  * @param {Function} parentClass
 601 |  */
 602 | jasmine.util.inherit = function(childClass, parentClass) {
 603 |   /**
 604 |    * @private
 605 |    */
 606 |   var subclass = function() {
 607 |   };
 608 |   subclass.prototype = parentClass.prototype;
 609 |   childClass.prototype = new subclass;
 610 | };
 611 | 
 612 | jasmine.util.formatException = function(e) {
 613 |   var lineNumber;
 614 |   if (e.line) {
 615 |     lineNumber = e.line;
 616 |   }
 617 |   else if (e.lineNumber) {
 618 |     lineNumber = e.lineNumber;
 619 |   }
 620 | 
 621 |   var file;
 622 | 
 623 |   if (e.sourceURL) {
 624 |     file = e.sourceURL;
 625 |   }
 626 |   else if (e.fileName) {
 627 |     file = e.fileName;
 628 |   }
 629 | 
 630 |   var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
 631 | 
 632 |   if (file && lineNumber) {
 633 |     message += ' in ' + file + ' (line ' + lineNumber + ')';
 634 |   }
 635 | 
 636 |   return message;
 637 | };
 638 | 
 639 | jasmine.util.htmlEscape = function(str) {
 640 |   if (!str) return str;
 641 |   return str.replace(/&/g, '&amp;')
 642 |     .replace(/</g, '&lt;')
 643 |     .replace(/>/g, '&gt;');
 644 | };
 645 | 
 646 | jasmine.util.argsToArray = function(args) {
 647 |   var arrayOfArgs = [];
 648 |   for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
 649 |   return arrayOfArgs;
 650 | };
 651 | 
 652 | jasmine.util.extend = function(destination, source) {
 653 |   for (var property in source) destination[property] = source[property];
 654 |   return destination;
 655 | };
 656 | 
 657 | /**
 658 |  * Environment for Jasmine
 659 |  *
 660 |  * @constructor
 661 |  */
 662 | jasmine.Env = function() {
 663 |   this.currentSpec = null;
 664 |   this.currentSuite = null;
 665 |   this.currentRunner_ = new jasmine.Runner(this);
 666 | 
 667 |   this.reporter = new jasmine.MultiReporter();
 668 | 
 669 |   this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
 670 |   this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
 671 |   this.lastUpdate = 0;
 672 |   this.specFilter = function() {
 673 |     return true;
 674 |   };
 675 | 
 676 |   this.nextSpecId_ = 0;
 677 |   this.nextSuiteId_ = 0;
 678 |   this.equalityTesters_ = [];
 679 | 
 680 |   // wrap matchers
 681 |   this.matchersClass = function() {
 682 |     jasmine.Matchers.apply(this, arguments);
 683 |   };
 684 |   jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
 685 | 
 686 |   jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
 687 | };
 688 | 
 689 | 
 690 | jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
 691 | jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
 692 | jasmine.Env.prototype.setInterval = jasmine.setInterval;
 693 | jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
 694 | 
 695 | /**
 696 |  * @returns an object containing jasmine version build info, if set.
 697 |  */
 698 | jasmine.Env.prototype.version = function () {
 699 |   if (jasmine.version_) {
 700 |     return jasmine.version_;
 701 |   } else {
 702 |     throw new Error('Version not set');
 703 |   }
 704 | };
 705 | 
 706 | /**
 707 |  * @returns string containing jasmine version build info, if set.
 708 |  */
 709 | jasmine.Env.prototype.versionString = function() {
 710 |   if (jasmine.version_) {
 711 |     var version = this.version();
 712 |     return version.major + "." + version.minor + "." + version.build + " revision " + version.revision;
 713 |   } else {
 714 |     return "version unknown";
 715 |   }
 716 | };
 717 | 
 718 | /**
 719 |  * @returns a sequential integer starting at 0
 720 |  */
 721 | jasmine.Env.prototype.nextSpecId = function () {
 722 |   return this.nextSpecId_++;
 723 | };
 724 | 
 725 | /**
 726 |  * @returns a sequential integer starting at 0
 727 |  */
 728 | jasmine.Env.prototype.nextSuiteId = function () {
 729 |   return this.nextSuiteId_++;
 730 | };
 731 | 
 732 | /**
 733 |  * Register a reporter to receive status updates from Jasmine.
 734 |  * @param {jasmine.Reporter} reporter An object which will receive status updates.
 735 |  */
 736 | jasmine.Env.prototype.addReporter = function(reporter) {
 737 |   this.reporter.addReporter(reporter);
 738 | };
 739 | 
 740 | jasmine.Env.prototype.execute = function() {
 741 |   this.currentRunner_.execute();
 742 | };
 743 | 
 744 | jasmine.Env.prototype.describe = function(description, specDefinitions) {
 745 |   var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
 746 | 
 747 |   var parentSuite = this.currentSuite;
 748 |   if (parentSuite) {
 749 |     parentSuite.add(suite);
 750 |   } else {
 751 |     this.currentRunner_.add(suite);
 752 |   }
 753 | 
 754 |   this.currentSuite = suite;
 755 | 
 756 |   var declarationError = null;
 757 |   try {
 758 |     specDefinitions.call(suite);
 759 |   } catch(e) {
 760 |     declarationError = e;
 761 |   }
 762 | 
 763 |   this.currentSuite = parentSuite;
 764 | 
 765 |   if (declarationError) {
 766 |     this.it("encountered a declaration exception", function() {
 767 |       throw declarationError;
 768 |     });
 769 |   }
 770 | 
 771 |   return suite;
 772 | };
 773 | 
 774 | jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
 775 |   if (this.currentSuite) {
 776 |     this.currentSuite.beforeEach(beforeEachFunction);
 777 |   } else {
 778 |     this.currentRunner_.beforeEach(beforeEachFunction);
 779 |   }
 780 | };
 781 | 
 782 | jasmine.Env.prototype.currentRunner = function () {
 783 |   return this.currentRunner_;
 784 | };
 785 | 
 786 | jasmine.Env.prototype.afterEach = function(afterEachFunction) {
 787 |   if (this.currentSuite) {
 788 |     this.currentSuite.afterEach(afterEachFunction);
 789 |   } else {
 790 |     this.currentRunner_.afterEach(afterEachFunction);
 791 |   }
 792 | 
 793 | };
 794 | 
 795 | jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
 796 |   return {
 797 |     execute: function() {
 798 |     }
 799 |   };
 800 | };
 801 | 
 802 | jasmine.Env.prototype.it = function(description, func) {
 803 |   var spec = new jasmine.Spec(this, this.currentSuite, description);
 804 |   this.currentSuite.add(spec);
 805 |   this.currentSpec = spec;
 806 | 
 807 |   if (func) {
 808 |     spec.runs(func);
 809 |   }
 810 | 
 811 |   return spec;
 812 | };
 813 | 
 814 | jasmine.Env.prototype.xit = function(desc, func) {
 815 |   return {
 816 |     id: this.nextSpecId(),
 817 |     runs: function() {
 818 |     }
 819 |   };
 820 | };
 821 | 
 822 | jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
 823 |   if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
 824 |     return true;
 825 |   }
 826 | 
 827 |   a.__Jasmine_been_here_before__ = b;
 828 |   b.__Jasmine_been_here_before__ = a;
 829 | 
 830 |   var hasKey = function(obj, keyName) {
 831 |     return obj != null && obj[keyName] !== jasmine.undefined;
 832 |   };
 833 | 
 834 |   for (var property in b) {
 835 |     if (!hasKey(a, property) && hasKey(b, property)) {
 836 |       mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
 837 |     }
 838 |   }
 839 |   for (property in a) {
 840 |     if (!hasKey(b, property) && hasKey(a, property)) {
 841 |       mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
 842 |     }
 843 |   }
 844 |   for (property in b) {
 845 |     if (property == '__Jasmine_been_here_before__') continue;
 846 |     if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
 847 |       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.");
 848 |     }
 849 |   }
 850 | 
 851 |   if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
 852 |     mismatchValues.push("arrays were not the same length");
 853 |   }
 854 | 
 855 |   delete a.__Jasmine_been_here_before__;
 856 |   delete b.__Jasmine_been_here_before__;
 857 |   return (mismatchKeys.length == 0 && mismatchValues.length == 0);
 858 | };
 859 | 
 860 | jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
 861 |   mismatchKeys = mismatchKeys || [];
 862 |   mismatchValues = mismatchValues || [];
 863 | 
 864 |   for (var i = 0; i < this.equalityTesters_.length; i++) {
 865 |     var equalityTester = this.equalityTesters_[i];
 866 |     var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
 867 |     if (result !== jasmine.undefined) return result;
 868 |   }
 869 | 
 870 |   if (a === b) return true;
 871 | 
 872 |   if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
 873 |     return (a == jasmine.undefined && b == jasmine.undefined);
 874 |   }
 875 | 
 876 |   if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
 877 |     return a === b;
 878 |   }
 879 | 
 880 |   if (a instanceof Date && b instanceof Date) {
 881 |     return a.getTime() == b.getTime();
 882 |   }
 883 | 
 884 |   if (a instanceof jasmine.Matchers.Any) {
 885 |     return a.matches(b);
 886 |   }
 887 | 
 888 |   if (b instanceof jasmine.Matchers.Any) {
 889 |     return b.matches(a);
 890 |   }
 891 | 
 892 |   if (jasmine.isString_(a) && jasmine.isString_(b)) {
 893 |     return (a == b);
 894 |   }
 895 | 
 896 |   if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {
 897 |     return (a == b);
 898 |   }
 899 | 
 900 |   if (typeof a === "object" && typeof b === "object") {
 901 |     return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
 902 |   }
 903 | 
 904 |   //Straight check
 905 |   return (a === b);
 906 | };
 907 | 
 908 | jasmine.Env.prototype.contains_ = function(haystack, needle) {
 909 |   if (jasmine.isArray_(haystack)) {
 910 |     for (var i = 0; i < haystack.length; i++) {
 911 |       if (this.equals_(haystack[i], needle)) return true;
 912 |     }
 913 |     return false;
 914 |   }
 915 |   return haystack.indexOf(needle) >= 0;
 916 | };
 917 | 
 918 | jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
 919 |   this.equalityTesters_.push(equalityTester);
 920 | };
 921 | /** No-op base class for Jasmine reporters.
 922 |  *
 923 |  * @constructor
 924 |  */
 925 | jasmine.Reporter = function() {
 926 | };
 927 | 
 928 | //noinspection JSUnusedLocalSymbols
 929 | jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
 930 | };
 931 | 
 932 | //noinspection JSUnusedLocalSymbols
 933 | jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
 934 | };
 935 | 
 936 | //noinspection JSUnusedLocalSymbols
 937 | jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
 938 | };
 939 | 
 940 | //noinspection JSUnusedLocalSymbols
 941 | jasmine.Reporter.prototype.reportSpecStarting = function(spec) {
 942 | };
 943 | 
 944 | //noinspection JSUnusedLocalSymbols
 945 | jasmine.Reporter.prototype.reportSpecResults = function(spec) {
 946 | };
 947 | 
 948 | //noinspection JSUnusedLocalSymbols
 949 | jasmine.Reporter.prototype.log = function(str) {
 950 | };
 951 | 
 952 | /**
 953 |  * Blocks are functions with executable code that make up a spec.
 954 |  *
 955 |  * @constructor
 956 |  * @param {jasmine.Env} env
 957 |  * @param {Function} func
 958 |  * @param {jasmine.Spec} spec
 959 |  */
 960 | jasmine.Block = function(env, func, spec) {
 961 |   this.env = env;
 962 |   this.func = func;
 963 |   this.spec = spec;
 964 | };
 965 | 
 966 | jasmine.Block.prototype.execute = function(onComplete) {  
 967 |   try {
 968 |     this.func.apply(this.spec);
 969 |   } catch (e) {
 970 |     this.spec.fail(e);
 971 |   }
 972 |   onComplete();
 973 | };
 974 | /** JavaScript API reporter.
 975 |  *
 976 |  * @constructor
 977 |  */
 978 | jasmine.JsApiReporter = function() {
 979 |   this.started = false;
 980 |   this.finished = false;
 981 |   this.suites_ = [];
 982 |   this.results_ = {};
 983 | };
 984 | 
 985 | jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
 986 |   this.started = true;
 987 |   var suites = runner.topLevelSuites();
 988 |   for (var i = 0; i < suites.length; i++) {
 989 |     var suite = suites[i];
 990 |     this.suites_.push(this.summarize_(suite));
 991 |   }
 992 | };
 993 | 
 994 | jasmine.JsApiReporter.prototype.suites = function() {
 995 |   return this.suites_;
 996 | };
 997 | 
 998 | jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
 999 |   var isSuite = suiteOrSpec instanceof jasmine.Suite;
1000 |   var summary = {
1001 |     id: suiteOrSpec.id,
1002 |     name: suiteOrSpec.description,
1003 |     type: isSuite ? 'suite' : 'spec',
1004 |     children: []
1005 |   };
1006 |   
1007 |   if (isSuite) {
1008 |     var children = suiteOrSpec.children();
1009 |     for (var i = 0; i < children.length; i++) {
1010 |       summary.children.push(this.summarize_(children[i]));
1011 |     }
1012 |   }
1013 |   return summary;
1014 | };
1015 | 
1016 | jasmine.JsApiReporter.prototype.results = function() {
1017 |   return this.results_;
1018 | };
1019 | 
1020 | jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {
1021 |   return this.results_[specId];
1022 | };
1023 | 
1024 | //noinspection JSUnusedLocalSymbols
1025 | jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
1026 |   this.finished = true;
1027 | };
1028 | 
1029 | //noinspection JSUnusedLocalSymbols
1030 | jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
1031 | };
1032 | 
1033 | //noinspection JSUnusedLocalSymbols
1034 | jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
1035 |   this.results_[spec.id] = {
1036 |     messages: spec.results().getItems(),
1037 |     result: spec.results().failedCount > 0 ? "failed" : "passed"
1038 |   };
1039 | };
1040 | 
1041 | //noinspection JSUnusedLocalSymbols
1042 | jasmine.JsApiReporter.prototype.log = function(str) {
1043 | };
1044 | 
1045 | jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){
1046 |   var results = {};
1047 |   for (var i = 0; i < specIds.length; i++) {
1048 |     var specId = specIds[i];
1049 |     results[specId] = this.summarizeResult_(this.results_[specId]);
1050 |   }
1051 |   return results;
1052 | };
1053 | 
1054 | jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
1055 |   var summaryMessages = [];
1056 |   var messagesLength = result.messages.length;
1057 |   for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
1058 |     var resultMessage = result.messages[messageIndex];
1059 |     summaryMessages.push({
1060 |       text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
1061 |       passed: resultMessage.passed ? resultMessage.passed() : true,
1062 |       type: resultMessage.type,
1063 |       message: resultMessage.message,
1064 |       trace: {
1065 |         stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
1066 |       }
1067 |     });
1068 |   }
1069 | 
1070 |   return {
1071 |     result : result.result,
1072 |     messages : summaryMessages
1073 |   };
1074 | };
1075 | 
1076 | /**
1077 |  * @constructor
1078 |  * @param {jasmine.Env} env
1079 |  * @param actual
1080 |  * @param {jasmine.Spec} spec
1081 |  */
1082 | jasmine.Matchers = function(env, actual, spec, opt_isNot) {
1083 |   this.env = env;
1084 |   this.actual = actual;
1085 |   this.spec = spec;
1086 |   this.isNot = opt_isNot || false;
1087 |   this.reportWasCalled_ = false;
1088 | };
1089 | 
1090 | // todo: @deprecated as of Jasmine 0.11, remove soon [xw]
1091 | jasmine.Matchers.pp = function(str) {
1092 |   throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
1093 | };
1094 | 
1095 | // todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
1096 | jasmine.Matchers.prototype.report = function(result, failing_message, details) {
1097 |   throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
1098 | };
1099 | 
1100 | jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {
1101 |   for (var methodName in prototype) {
1102 |     if (methodName == 'report') continue;
1103 |     var orig = prototype[methodName];
1104 |     matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
1105 |   }
1106 | };
1107 | 
1108 | jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
1109 |   return function() {
1110 |     var matcherArgs = jasmine.util.argsToArray(arguments);
1111 |     var result = matcherFunction.apply(this, arguments);
1112 | 
1113 |     if (this.isNot) {
1114 |       result = !result;
1115 |     }
1116 | 
1117 |     if (this.reportWasCalled_) return result;
1118 | 
1119 |     var message;
1120 |     if (!result) {
1121 |       if (this.message) {
1122 |         message = this.message.apply(this, arguments);
1123 |         if (jasmine.isArray_(message)) {
1124 |           message = message[this.isNot ? 1 : 0];
1125 |         }
1126 |       } else {
1127 |         var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
1128 |         message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;
1129 |         if (matcherArgs.length > 0) {
1130 |           for (var i = 0; i < matcherArgs.length; i++) {
1131 |             if (i > 0) message += ",";
1132 |             message += " " + jasmine.pp(matcherArgs[i]);
1133 |           }
1134 |         }
1135 |         message += ".";
1136 |       }
1137 |     }
1138 |     var expectationResult = new jasmine.ExpectationResult({
1139 |       matcherName: matcherName,
1140 |       passed: result,
1141 |       expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],
1142 |       actual: this.actual,
1143 |       message: message
1144 |     });
1145 |     this.spec.addMatcherResult(expectationResult);
1146 |     return jasmine.undefined;
1147 |   };
1148 | };
1149 | 
1150 | 
1151 | 
1152 | 
1153 | /**
1154 |  * toBe: compares the actual to the expected using ===
1155 |  * @param expected
1156 |  */
1157 | jasmine.Matchers.prototype.toBe = function(expected) {
1158 |   return this.actual === expected;
1159 | };
1160 | 
1161 | /**
1162 |  * toNotBe: compares the actual to the expected using !==
1163 |  * @param expected
1164 |  * @deprecated as of 1.0. Use not.toBe() instead.
1165 |  */
1166 | jasmine.Matchers.prototype.toNotBe = function(expected) {
1167 |   return this.actual !== expected;
1168 | };
1169 | 
1170 | /**
1171 |  * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
1172 |  *
1173 |  * @param expected
1174 |  */
1175 | jasmine.Matchers.prototype.toEqual = function(expected) {
1176 |   return this.env.equals_(this.actual, expected);
1177 | };
1178 | 
1179 | /**
1180 |  * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
1181 |  * @param expected
1182 |  * @deprecated as of 1.0. Use not.toNotEqual() instead.
1183 |  */
1184 | jasmine.Matchers.prototype.toNotEqual = function(expected) {
1185 |   return !this.env.equals_(this.actual, expected);
1186 | };
1187 | 
1188 | /**
1189 |  * Matcher that compares the actual to the expected using a regular expression.  Constructs a RegExp, so takes
1190 |  * a pattern or a String.
1191 |  *
1192 |  * @param expected
1193 |  */
1194 | jasmine.Matchers.prototype.toMatch = function(expected) {
1195 |   return new RegExp(expected).test(this.actual);
1196 | };
1197 | 
1198 | /**
1199 |  * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
1200 |  * @param expected
1201 |  * @deprecated as of 1.0. Use not.toMatch() instead.
1202 |  */
1203 | jasmine.Matchers.prototype.toNotMatch = function(expected) {
1204 |   return !(new RegExp(expected).test(this.actual));
1205 | };
1206 | 
1207 | /**
1208 |  * Matcher that compares the actual to jasmine.undefined.
1209 |  */
1210 | jasmine.Matchers.prototype.toBeDefined = function() {
1211 |   return (this.actual !== jasmine.undefined);
1212 | };
1213 | 
1214 | /**
1215 |  * Matcher that compares the actual to jasmine.undefined.
1216 |  */
1217 | jasmine.Matchers.prototype.toBeUndefined = function() {
1218 |   return (this.actual === jasmine.undefined);
1219 | };
1220 | 
1221 | /**
1222 |  * Matcher that compares the actual to null.
1223 |  */
1224 | jasmine.Matchers.prototype.toBeNull = function() {
1225 |   return (this.actual === null);
1226 | };
1227 | 
1228 | /**
1229 |  * Matcher that boolean not-nots the actual.
1230 |  */
1231 | jasmine.Matchers.prototype.toBeTruthy = function() {
1232 |   return !!this.actual;
1233 | };
1234 | 
1235 | 
1236 | /**
1237 |  * Matcher that boolean nots the actual.
1238 |  */
1239 | jasmine.Matchers.prototype.toBeFalsy = function() {
1240 |   return !this.actual;
1241 | };
1242 | 
1243 | 
1244 | /**
1245 |  * Matcher that checks to see if the actual, a Jasmine spy, was called.
1246 |  */
1247 | jasmine.Matchers.prototype.toHaveBeenCalled = function() {
1248 |   if (arguments.length > 0) {
1249 |     throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
1250 |   }
1251 | 
1252 |   if (!jasmine.isSpy(this.actual)) {
1253 |     throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1254 |   }
1255 | 
1256 |   this.message = function() {
1257 |     return [
1258 |       "Expected spy " + this.actual.identity + " to have been called.",
1259 |       "Expected spy " + this.actual.identity + " not to have been called."
1260 |     ];
1261 |   };
1262 | 
1263 |   return this.actual.wasCalled;
1264 | };
1265 | 
1266 | /** @deprecated Use expect(xxx).toHaveBeenCalled() instead */
1267 | jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;
1268 | 
1269 | /**
1270 |  * Matcher that checks to see if the actual, a Jasmine spy, was not called.
1271 |  *
1272 |  * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead
1273 |  */
1274 | jasmine.Matchers.prototype.wasNotCalled = function() {
1275 |   if (arguments.length > 0) {
1276 |     throw new Error('wasNotCalled does not take arguments');
1277 |   }
1278 | 
1279 |   if (!jasmine.isSpy(this.actual)) {
1280 |     throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1281 |   }
1282 | 
1283 |   this.message = function() {
1284 |     return [
1285 |       "Expected spy " + this.actual.identity + " to not have been called.",
1286 |       "Expected spy " + this.actual.identity + " to have been called."
1287 |     ];
1288 |   };
1289 | 
1290 |   return !this.actual.wasCalled;
1291 | };
1292 | 
1293 | /**
1294 |  * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.
1295 |  *
1296 |  * @example
1297 |  *
1298 |  */
1299 | jasmine.Matchers.prototype.toHaveBeenCalledWith = function() {
1300 |   var expectedArgs = jasmine.util.argsToArray(arguments);
1301 |   if (!jasmine.isSpy(this.actual)) {
1302 |     throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1303 |   }
1304 |   this.message = function() {
1305 |     if (this.actual.callCount == 0) {
1306 |       // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw]
1307 |       return [
1308 |         "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.",
1309 |         "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was."
1310 |       ];
1311 |     } else {
1312 |       return [
1313 |         "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall),
1314 |         "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall)
1315 |       ];
1316 |     }
1317 |   };
1318 | 
1319 |   return this.env.contains_(this.actual.argsForCall, expectedArgs);
1320 | };
1321 | 
1322 | /** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */
1323 | jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;
1324 | 
1325 | /** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */
1326 | jasmine.Matchers.prototype.wasNotCalledWith = function() {
1327 |   var expectedArgs = jasmine.util.argsToArray(arguments);
1328 |   if (!jasmine.isSpy(this.actual)) {
1329 |     throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1330 |   }
1331 | 
1332 |   this.message = function() {
1333 |     return [
1334 |       "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",
1335 |       "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"
1336 |     ]
1337 |   };
1338 | 
1339 |   return !this.env.contains_(this.actual.argsForCall, expectedArgs);
1340 | };
1341 | 
1342 | /**
1343 |  * Matcher that checks that the expected item is an element in the actual Array.
1344 |  *
1345 |  * @param {Object} expected
1346 |  */
1347 | jasmine.Matchers.prototype.toContain = function(expected) {
1348 |   return this.env.contains_(this.actual, expected);
1349 | };
1350 | 
1351 | /**
1352 |  * Matcher that checks that the expected item is NOT an element in the actual Array.
1353 |  *
1354 |  * @param {Object} expected
1355 |  * @deprecated as of 1.0. Use not.toNotContain() instead.
1356 |  */
1357 | jasmine.Matchers.prototype.toNotContain = function(expected) {
1358 |   return !this.env.contains_(this.actual, expected);
1359 | };
1360 | 
1361 | jasmine.Matchers.prototype.toBeLessThan = function(expected) {
1362 |   return this.actual < expected;
1363 | };
1364 | 
1365 | jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {
1366 |   return this.actual > expected;
1367 | };
1368 | 
1369 | /**
1370 |  * Matcher that checks that the expected exception was thrown by the actual.
1371 |  *
1372 |  * @param {String} expected
1373 |  */
1374 | jasmine.Matchers.prototype.toThrow = function(expected) {
1375 |   var result = false;
1376 |   var exception;
1377 |   if (typeof this.actual != 'function') {
1378 |     throw new Error('Actual is not a function');
1379 |   }
1380 |   try {
1381 |     this.actual();
1382 |   } catch (e) {
1383 |     exception = e;
1384 |   }
1385 |   if (exception) {
1386 |     result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
1387 |   }
1388 | 
1389 |   var not = this.isNot ? "not " : "";
1390 | 
1391 |   this.message = function() {
1392 |     if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
1393 |       return ["Expected function " + not + "to throw", expected ? expected.message || expected : " an exception", ", but it threw", exception.message || exception].join(' ');
1394 |     } else {
1395 |       return "Expected function to throw an exception.";
1396 |     }
1397 |   };
1398 | 
1399 |   return result;
1400 | };
1401 | 
1402 | jasmine.Matchers.Any = function(expectedClass) {
1403 |   this.expectedClass = expectedClass;
1404 | };
1405 | 
1406 | jasmine.Matchers.Any.prototype.matches = function(other) {
1407 |   if (this.expectedClass == String) {
1408 |     return typeof other == 'string' || other instanceof String;
1409 |   }
1410 | 
1411 |   if (this.expectedClass == Number) {
1412 |     return typeof other == 'number' || other instanceof Number;
1413 |   }
1414 | 
1415 |   if (this.expectedClass == Function) {
1416 |     return typeof other == 'function' || other instanceof Function;
1417 |   }
1418 | 
1419 |   if (this.expectedClass == Object) {
1420 |     return typeof other == 'object';
1421 |   }
1422 | 
1423 |   return other instanceof this.expectedClass;
1424 | };
1425 | 
1426 | jasmine.Matchers.Any.prototype.toString = function() {
1427 |   return '<jasmine.any(' + this.expectedClass + ')>';
1428 | };
1429 | 
1430 | /**
1431 |  * @constructor
1432 |  */
1433 | jasmine.MultiReporter = function() {
1434 |   this.subReporters_ = [];
1435 | };
1436 | jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);
1437 | 
1438 | jasmine.MultiReporter.prototype.addReporter = function(reporter) {
1439 |   this.subReporters_.push(reporter);
1440 | };
1441 | 
1442 | (function() {
1443 |   var functionNames = [
1444 |     "reportRunnerStarting",
1445 |     "reportRunnerResults",
1446 |     "reportSuiteResults",
1447 |     "reportSpecStarting",
1448 |     "reportSpecResults",
1449 |     "log"
1450 |   ];
1451 |   for (var i = 0; i < functionNames.length; i++) {
1452 |     var functionName = functionNames[i];
1453 |     jasmine.MultiReporter.prototype[functionName] = (function(functionName) {
1454 |       return function() {
1455 |         for (var j = 0; j < this.subReporters_.length; j++) {
1456 |           var subReporter = this.subReporters_[j];
1457 |           if (subReporter[functionName]) {
1458 |             subReporter[functionName].apply(subReporter, arguments);
1459 |           }
1460 |         }
1461 |       };
1462 |     })(functionName);
1463 |   }
1464 | })();
1465 | /**
1466 |  * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults
1467 |  *
1468 |  * @constructor
1469 |  */
1470 | jasmine.NestedResults = function() {
1471 |   /**
1472 |    * The total count of results
1473 |    */
1474 |   this.totalCount = 0;
1475 |   /**
1476 |    * Number of passed results
1477 |    */
1478 |   this.passedCount = 0;
1479 |   /**
1480 |    * Number of failed results
1481 |    */
1482 |   this.failedCount = 0;
1483 |   /**
1484 |    * Was this suite/spec skipped?
1485 |    */
1486 |   this.skipped = false;
1487 |   /**
1488 |    * @ignore
1489 |    */
1490 |   this.items_ = [];
1491 | };
1492 | 
1493 | /**
1494 |  * Roll up the result counts.
1495 |  *
1496 |  * @param result
1497 |  */
1498 | jasmine.NestedResults.prototype.rollupCounts = function(result) {
1499 |   this.totalCount += result.totalCount;
1500 |   this.passedCount += result.passedCount;
1501 |   this.failedCount += result.failedCount;
1502 | };
1503 | 
1504 | /**
1505 |  * Adds a log message.
1506 |  * @param values Array of message parts which will be concatenated later.
1507 |  */
1508 | jasmine.NestedResults.prototype.log = function(values) {
1509 |   this.items_.push(new jasmine.MessageResult(values));
1510 | };
1511 | 
1512 | /**
1513 |  * Getter for the results: message & results.
1514 |  */
1515 | jasmine.NestedResults.prototype.getItems = function() {
1516 |   return this.items_;
1517 | };
1518 | 
1519 | /**
1520 |  * Adds a result, tracking counts (total, passed, & failed)
1521 |  * @param {jasmine.ExpectationResult|jasmine.NestedResults} result
1522 |  */
1523 | jasmine.NestedResults.prototype.addResult = function(result) {
1524 |   if (result.type != 'log') {
1525 |     if (result.items_) {
1526 |       this.rollupCounts(result);
1527 |     } else {
1528 |       this.totalCount++;
1529 |       if (result.passed()) {
1530 |         this.passedCount++;
1531 |       } else {
1532 |         this.failedCount++;
1533 |       }
1534 |     }
1535 |   }
1536 |   this.items_.push(result);
1537 | };
1538 | 
1539 | /**
1540 |  * @returns {Boolean} True if <b>everything</b> below passed
1541 |  */
1542 | jasmine.NestedResults.prototype.passed = function() {
1543 |   return this.passedCount === this.totalCount;
1544 | };
1545 | /**
1546 |  * Base class for pretty printing for expectation results.
1547 |  */
1548 | jasmine.PrettyPrinter = function() {
1549 |   this.ppNestLevel_ = 0;
1550 | };
1551 | 
1552 | /**
1553 |  * Formats a value in a nice, human-readable string.
1554 |  *
1555 |  * @param value
1556 |  */
1557 | jasmine.PrettyPrinter.prototype.format = function(value) {
1558 |   if (this.ppNestLevel_ > 40) {
1559 |     throw new Error('jasmine.PrettyPrinter: format() nested too deeply!');
1560 |   }
1561 | 
1562 |   this.ppNestLevel_++;
1563 |   try {
1564 |     if (value === jasmine.undefined) {
1565 |       this.emitScalar('undefined');
1566 |     } else if (value === null) {
1567 |       this.emitScalar('null');
1568 |     } else if (value === jasmine.getGlobal()) {
1569 |       this.emitScalar('<global>');
1570 |     } else if (value instanceof jasmine.Matchers.Any) {
1571 |       this.emitScalar(value.toString());
1572 |     } else if (typeof value === 'string') {
1573 |       this.emitString(value);
1574 |     } else if (jasmine.isSpy(value)) {
1575 |       this.emitScalar("spy on " + value.identity);
1576 |     } else if (value instanceof RegExp) {
1577 |       this.emitScalar(value.toString());
1578 |     } else if (typeof value === 'function') {
1579 |       this.emitScalar('Function');
1580 |     } else if (typeof value.nodeType === 'number') {
1581 |       this.emitScalar('HTMLNode');
1582 |     } else if (value instanceof Date) {
1583 |       this.emitScalar('Date(' + value + ')');
1584 |     } else if (value.__Jasmine_been_here_before__) {
1585 |       this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>');
1586 |     } else if (jasmine.isArray_(value) || typeof value == 'object') {
1587 |       value.__Jasmine_been_here_before__ = true;
1588 |       if (jasmine.isArray_(value)) {
1589 |         this.emitArray(value);
1590 |       } else {
1591 |         this.emitObject(value);
1592 |       }
1593 |       delete value.__Jasmine_been_here_before__;
1594 |     } else {
1595 |       this.emitScalar(value.toString());
1596 |     }
1597 |   } finally {
1598 |     this.ppNestLevel_--;
1599 |   }
1600 | };
1601 | 
1602 | jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {
1603 |   for (var property in obj) {
1604 |     if (property == '__Jasmine_been_here_before__') continue;
1605 |     fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) != null) : false);
1606 |   }
1607 | };
1608 | 
1609 | jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;
1610 | jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;
1611 | jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;
1612 | jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;
1613 | 
1614 | jasmine.StringPrettyPrinter = function() {
1615 |   jasmine.PrettyPrinter.call(this);
1616 | 
1617 |   this.string = '';
1618 | };
1619 | jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);
1620 | 
1621 | jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {
1622 |   this.append(value);
1623 | };
1624 | 
1625 | jasmine.StringPrettyPrinter.prototype.emitString = function(value) {
1626 |   this.append("'" + value + "'");
1627 | };
1628 | 
1629 | jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {
1630 |   this.append('[ ');
1631 |   for (var i = 0; i < array.length; i++) {
1632 |     if (i > 0) {
1633 |       this.append(', ');
1634 |     }
1635 |     this.format(array[i]);
1636 |   }
1637 |   this.append(' ]');
1638 | };
1639 | 
1640 | jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {
1641 |   var self = this;
1642 |   this.append('{ ');
1643 |   var first = true;
1644 | 
1645 |   this.iterateObject(obj, function(property, isGetter) {
1646 |     if (first) {
1647 |       first = false;
1648 |     } else {
1649 |       self.append(', ');
1650 |     }
1651 | 
1652 |     self.append(property);
1653 |     self.append(' : ');
1654 |     if (isGetter) {
1655 |       self.append('<getter>');
1656 |     } else {
1657 |       self.format(obj[property]);
1658 |     }
1659 |   });
1660 | 
1661 |   this.append(' }');
1662 | };
1663 | 
1664 | jasmine.StringPrettyPrinter.prototype.append = function(value) {
1665 |   this.string += value;
1666 | };
1667 | jasmine.Queue = function(env) {
1668 |   this.env = env;
1669 |   this.blocks = [];
1670 |   this.running = false;
1671 |   this.index = 0;
1672 |   this.offset = 0;
1673 |   this.abort = false;
1674 | };
1675 | 
1676 | jasmine.Queue.prototype.addBefore = function(block) {
1677 |   this.blocks.unshift(block);
1678 | };
1679 | 
1680 | jasmine.Queue.prototype.add = function(block) {
1681 |   this.blocks.push(block);
1682 | };
1683 | 
1684 | jasmine.Queue.prototype.insertNext = function(block) {
1685 |   this.blocks.splice((this.index + this.offset + 1), 0, block);
1686 |   this.offset++;
1687 | };
1688 | 
1689 | jasmine.Queue.prototype.start = function(onComplete) {
1690 |   this.running = true;
1691 |   this.onComplete = onComplete;
1692 |   this.next_();
1693 | };
1694 | 
1695 | jasmine.Queue.prototype.isRunning = function() {
1696 |   return this.running;
1697 | };
1698 | 
1699 | jasmine.Queue.LOOP_DONT_RECURSE = true;
1700 | 
1701 | jasmine.Queue.prototype.next_ = function() {
1702 |   var self = this;
1703 |   var goAgain = true;
1704 | 
1705 |   while (goAgain) {
1706 |     goAgain = false;
1707 |     
1708 |     if (self.index < self.blocks.length && !this.abort) {
1709 |       var calledSynchronously = true;
1710 |       var completedSynchronously = false;
1711 | 
1712 |       var onComplete = function () {
1713 |         if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {
1714 |           completedSynchronously = true;
1715 |           return;
1716 |         }
1717 | 
1718 |         if (self.blocks[self.index].abort) {
1719 |           self.abort = true;
1720 |         }
1721 | 
1722 |         self.offset = 0;
1723 |         self.index++;
1724 | 
1725 |         var now = new Date().getTime();
1726 |         if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {
1727 |           self.env.lastUpdate = now;
1728 |           self.env.setTimeout(function() {
1729 |             self.next_();
1730 |           }, 0);
1731 |         } else {
1732 |           if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {
1733 |             goAgain = true;
1734 |           } else {
1735 |             self.next_();
1736 |           }
1737 |         }
1738 |       };
1739 |       self.blocks[self.index].execute(onComplete);
1740 | 
1741 |       calledSynchronously = false;
1742 |       if (completedSynchronously) {
1743 |         onComplete();
1744 |       }
1745 |       
1746 |     } else {
1747 |       self.running = false;
1748 |       if (self.onComplete) {
1749 |         self.onComplete();
1750 |       }
1751 |     }
1752 |   }
1753 | };
1754 | 
1755 | jasmine.Queue.prototype.results = function() {
1756 |   var results = new jasmine.NestedResults();
1757 |   for (var i = 0; i < this.blocks.length; i++) {
1758 |     if (this.blocks[i].results) {
1759 |       results.addResult(this.blocks[i].results());
1760 |     }
1761 |   }
1762 |   return results;
1763 | };
1764 | 
1765 | 
1766 | /**
1767 |  * Runner
1768 |  *
1769 |  * @constructor
1770 |  * @param {jasmine.Env} env
1771 |  */
1772 | jasmine.Runner = function(env) {
1773 |   var self = this;
1774 |   self.env = env;
1775 |   self.queue = new jasmine.Queue(env);
1776 |   self.before_ = [];
1777 |   self.after_ = [];
1778 |   self.suites_ = [];
1779 | };
1780 | 
1781 | jasmine.Runner.prototype.execute = function() {
1782 |   var self = this;
1783 |   if (self.env.reporter.reportRunnerStarting) {
1784 |     self.env.reporter.reportRunnerStarting(this);
1785 |   }
1786 |   self.queue.start(function () {
1787 |     self.finishCallback();
1788 |   });
1789 | };
1790 | 
1791 | jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) {
1792 |   beforeEachFunction.typeName = 'beforeEach';
1793 |   this.before_.splice(0,0,beforeEachFunction);
1794 | };
1795 | 
1796 | jasmine.Runner.prototype.afterEach = function(afterEachFunction) {
1797 |   afterEachFunction.typeName = 'afterEach';
1798 |   this.after_.splice(0,0,afterEachFunction);
1799 | };
1800 | 
1801 | 
1802 | jasmine.Runner.prototype.finishCallback = function() {
1803 |   this.env.reporter.reportRunnerResults(this);
1804 | };
1805 | 
1806 | jasmine.Runner.prototype.addSuite = function(suite) {
1807 |   this.suites_.push(suite);
1808 | };
1809 | 
1810 | jasmine.Runner.prototype.add = function(block) {
1811 |   if (block instanceof jasmine.Suite) {
1812 |     this.addSuite(block);
1813 |   }
1814 |   this.queue.add(block);
1815 | };
1816 | 
1817 | jasmine.Runner.prototype.specs = function () {
1818 |   var suites = this.suites();
1819 |   var specs = [];
1820 |   for (var i = 0; i < suites.length; i++) {
1821 |     specs = specs.concat(suites[i].specs());
1822 |   }
1823 |   return specs;
1824 | };
1825 | 
1826 | jasmine.Runner.prototype.suites = function() {
1827 |   return this.suites_;
1828 | };
1829 | 
1830 | jasmine.Runner.prototype.topLevelSuites = function() {
1831 |   var topLevelSuites = [];
1832 |   for (var i = 0; i < this.suites_.length; i++) {
1833 |     if (!this.suites_[i].parentSuite) {
1834 |       topLevelSuites.push(this.suites_[i]);
1835 |     }
1836 |   }
1837 |   return topLevelSuites;
1838 | };
1839 | 
1840 | jasmine.Runner.prototype.results = function() {
1841 |   return this.queue.results();
1842 | };
1843 | /**
1844 |  * Internal representation of a Jasmine specification, or test.
1845 |  *
1846 |  * @constructor
1847 |  * @param {jasmine.Env} env
1848 |  * @param {jasmine.Suite} suite
1849 |  * @param {String} description
1850 |  */
1851 | jasmine.Spec = function(env, suite, description) {
1852 |   if (!env) {
1853 |     throw new Error('jasmine.Env() required');
1854 |   }
1855 |   if (!suite) {
1856 |     throw new Error('jasmine.Suite() required');
1857 |   }
1858 |   var spec = this;
1859 |   spec.id = env.nextSpecId ? env.nextSpecId() : null;
1860 |   spec.env = env;
1861 |   spec.suite = suite;
1862 |   spec.description = description;
1863 |   spec.queue = new jasmine.Queue(env);
1864 | 
1865 |   spec.afterCallbacks = [];
1866 |   spec.spies_ = [];
1867 | 
1868 |   spec.results_ = new jasmine.NestedResults();
1869 |   spec.results_.description = description;
1870 |   spec.matchersClass = null;
1871 | };
1872 | 
1873 | jasmine.Spec.prototype.getFullName = function() {
1874 |   return this.suite.getFullName() + ' ' + this.description + '.';
1875 | };
1876 | 
1877 | 
1878 | jasmine.Spec.prototype.results = function() {
1879 |   return this.results_;
1880 | };
1881 | 
1882 | /**
1883 |  * All parameters are pretty-printed and concatenated together, then written to the spec's output.
1884 |  *
1885 |  * Be careful not to leave calls to <code>jasmine.log</code> in production code.
1886 |  */
1887 | jasmine.Spec.prototype.log = function() {
1888 |   return this.results_.log(arguments);
1889 | };
1890 | 
1891 | jasmine.Spec.prototype.runs = function (func) {
1892 |   var block = new jasmine.Block(this.env, func, this);
1893 |   this.addToQueue(block);
1894 |   return this;
1895 | };
1896 | 
1897 | jasmine.Spec.prototype.addToQueue = function (block) {
1898 |   if (this.queue.isRunning()) {
1899 |     this.queue.insertNext(block);
1900 |   } else {
1901 |     this.queue.add(block);
1902 |   }
1903 | };
1904 | 
1905 | /**
1906 |  * @param {jasmine.ExpectationResult} result
1907 |  */
1908 | jasmine.Spec.prototype.addMatcherResult = function(result) {
1909 |   this.results_.addResult(result);
1910 | };
1911 | 
1912 | jasmine.Spec.prototype.expect = function(actual) {
1913 |   var positive = new (this.getMatchersClass_())(this.env, actual, this);
1914 |   positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
1915 |   return positive;
1916 | };
1917 | 
1918 | /**
1919 |  * Waits a fixed time period before moving to the next block.
1920 |  *
1921 |  * @deprecated Use waitsFor() instead
1922 |  * @param {Number} timeout milliseconds to wait
1923 |  */
1924 | jasmine.Spec.prototype.waits = function(timeout) {
1925 |   var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
1926 |   this.addToQueue(waitsFunc);
1927 |   return this;
1928 | };
1929 | 
1930 | /**
1931 |  * Waits for the latchFunction to return true before proceeding to the next block.
1932 |  *
1933 |  * @param {Function} latchFunction
1934 |  * @param {String} optional_timeoutMessage
1935 |  * @param {Number} optional_timeout
1936 |  */
1937 | jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
1938 |   var latchFunction_ = null;
1939 |   var optional_timeoutMessage_ = null;
1940 |   var optional_timeout_ = null;
1941 | 
1942 |   for (var i = 0; i < arguments.length; i++) {
1943 |     var arg = arguments[i];
1944 |     switch (typeof arg) {
1945 |       case 'function':
1946 |         latchFunction_ = arg;
1947 |         break;
1948 |       case 'string':
1949 |         optional_timeoutMessage_ = arg;
1950 |         break;
1951 |       case 'number':
1952 |         optional_timeout_ = arg;
1953 |         break;
1954 |     }
1955 |   }
1956 | 
1957 |   var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);
1958 |   this.addToQueue(waitsForFunc);
1959 |   return this;
1960 | };
1961 | 
1962 | jasmine.Spec.prototype.fail = function (e) {
1963 |   var expectationResult = new jasmine.ExpectationResult({
1964 |     passed: false,
1965 |     message: e ? jasmine.util.formatException(e) : 'Exception'
1966 |   });
1967 |   this.results_.addResult(expectationResult);
1968 | };
1969 | 
1970 | jasmine.Spec.prototype.getMatchersClass_ = function() {
1971 |   return this.matchersClass || this.env.matchersClass;
1972 | };
1973 | 
1974 | jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
1975 |   var parent = this.getMatchersClass_();
1976 |   var newMatchersClass = function() {
1977 |     parent.apply(this, arguments);
1978 |   };
1979 |   jasmine.util.inherit(newMatchersClass, parent);
1980 |   jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
1981 |   this.matchersClass = newMatchersClass;
1982 | };
1983 | 
1984 | jasmine.Spec.prototype.finishCallback = function() {
1985 |   this.env.reporter.reportSpecResults(this);
1986 | };
1987 | 
1988 | jasmine.Spec.prototype.finish = function(onComplete) {
1989 |   this.removeAllSpies();
1990 |   this.finishCallback();
1991 |   if (onComplete) {
1992 |     onComplete();
1993 |   }
1994 | };
1995 | 
1996 | jasmine.Spec.prototype.after = function(doAfter) {
1997 |   if (this.queue.isRunning()) {
1998 |     this.queue.add(new jasmine.Block(this.env, doAfter, this));
1999 |   } else {
2000 |     this.afterCallbacks.unshift(doAfter);
2001 |   }
2002 | };
2003 | 
2004 | jasmine.Spec.prototype.execute = function(onComplete) {
2005 |   var spec = this;
2006 |   if (!spec.env.specFilter(spec)) {
2007 |     spec.results_.skipped = true;
2008 |     spec.finish(onComplete);
2009 |     return;
2010 |   }
2011 | 
2012 |   this.env.reporter.reportSpecStarting(this);
2013 | 
2014 |   spec.env.currentSpec = spec;
2015 | 
2016 |   spec.addBeforesAndAftersToQueue();
2017 | 
2018 |   spec.queue.start(function () {
2019 |     spec.finish(onComplete);
2020 |   });
2021 | };
2022 | 
2023 | jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
2024 |   var runner = this.env.currentRunner();
2025 |   var i;
2026 | 
2027 |   for (var suite = this.suite; suite; suite = suite.parentSuite) {
2028 |     for (i = 0; i < suite.before_.length; i++) {
2029 |       this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
2030 |     }
2031 |   }
2032 |   for (i = 0; i < runner.before_.length; i++) {
2033 |     this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
2034 |   }
2035 |   for (i = 0; i < this.afterCallbacks.length; i++) {
2036 |     this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this));
2037 |   }
2038 |   for (suite = this.suite; suite; suite = suite.parentSuite) {
2039 |     for (i = 0; i < suite.after_.length; i++) {
2040 |       this.queue.add(new jasmine.Block(this.env, suite.after_[i], this));
2041 |     }
2042 |   }
2043 |   for (i = 0; i < runner.after_.length; i++) {
2044 |     this.queue.add(new jasmine.Block(this.env, runner.after_[i], this));
2045 |   }
2046 | };
2047 | 
2048 | jasmine.Spec.prototype.explodes = function() {
2049 |   throw 'explodes function should not have been called';
2050 | };
2051 | 
2052 | jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
2053 |   if (obj == jasmine.undefined) {
2054 |     throw "spyOn could not find an object to spy upon for " + methodName + "()";
2055 |   }
2056 | 
2057 |   if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
2058 |     throw methodName + '() method does not exist';
2059 |   }
2060 | 
2061 |   if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
2062 |     throw new Error(methodName + ' has already been spied upon');
2063 |   }
2064 | 
2065 |   var spyObj = jasmine.createSpy(methodName);
2066 | 
2067 |   this.spies_.push(spyObj);
2068 |   spyObj.baseObj = obj;
2069 |   spyObj.methodName = methodName;
2070 |   spyObj.originalValue = obj[methodName];
2071 | 
2072 |   obj[methodName] = spyObj;
2073 | 
2074 |   return spyObj;
2075 | };
2076 | 
2077 | jasmine.Spec.prototype.removeAllSpies = function() {
2078 |   for (var i = 0; i < this.spies_.length; i++) {
2079 |     var spy = this.spies_[i];
2080 |     spy.baseObj[spy.methodName] = spy.originalValue;
2081 |   }
2082 |   this.spies_ = [];
2083 | };
2084 | 
2085 | /**
2086 |  * Internal representation of a Jasmine suite.
2087 |  *
2088 |  * @constructor
2089 |  * @param {jasmine.Env} env
2090 |  * @param {String} description
2091 |  * @param {Function} specDefinitions
2092 |  * @param {jasmine.Suite} parentSuite
2093 |  */
2094 | jasmine.Suite = function(env, description, specDefinitions, parentSuite) {
2095 |   var self = this;
2096 |   self.id = env.nextSuiteId ? env.nextSuiteId() : null;
2097 |   self.description = description;
2098 |   self.queue = new jasmine.Queue(env);
2099 |   self.parentSuite = parentSuite;
2100 |   self.env = env;
2101 |   self.before_ = [];
2102 |   self.after_ = [];
2103 |   self.children_ = [];
2104 |   self.suites_ = [];
2105 |   self.specs_ = [];
2106 | };
2107 | 
2108 | jasmine.Suite.prototype.getFullName = function() {
2109 |   var fullName = this.description;
2110 |   for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
2111 |     fullName = parentSuite.description + ' ' + fullName;
2112 |   }
2113 |   return fullName;
2114 | };
2115 | 
2116 | jasmine.Suite.prototype.finish = function(onComplete) {
2117 |   this.env.reporter.reportSuiteResults(this);
2118 |   this.finished = true;
2119 |   if (typeof(onComplete) == 'function') {
2120 |     onComplete();
2121 |   }
2122 | };
2123 | 
2124 | jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {
2125 |   beforeEachFunction.typeName = 'beforeEach';
2126 |   this.before_.unshift(beforeEachFunction);
2127 | };
2128 | 
2129 | jasmine.Suite.prototype.afterEach = function(afterEachFunction) {
2130 |   afterEachFunction.typeName = 'afterEach';
2131 |   this.after_.unshift(afterEachFunction);
2132 | };
2133 | 
2134 | jasmine.Suite.prototype.results = function() {
2135 |   return this.queue.results();
2136 | };
2137 | 
2138 | jasmine.Suite.prototype.add = function(suiteOrSpec) {
2139 |   this.children_.push(suiteOrSpec);
2140 |   if (suiteOrSpec instanceof jasmine.Suite) {
2141 |     this.suites_.push(suiteOrSpec);
2142 |     this.env.currentRunner().addSuite(suiteOrSpec);
2143 |   } else {
2144 |     this.specs_.push(suiteOrSpec);
2145 |   }
2146 |   this.queue.add(suiteOrSpec);
2147 | };
2148 | 
2149 | jasmine.Suite.prototype.specs = function() {
2150 |   return this.specs_;
2151 | };
2152 | 
2153 | jasmine.Suite.prototype.suites = function() {
2154 |   return this.suites_;
2155 | };
2156 | 
2157 | jasmine.Suite.prototype.children = function() {
2158 |   return this.children_;
2159 | };
2160 | 
2161 | jasmine.Suite.prototype.execute = function(onComplete) {
2162 |   var self = this;
2163 |   this.queue.start(function () {
2164 |     self.finish(onComplete);
2165 |   });
2166 | };
2167 | jasmine.WaitsBlock = function(env, timeout, spec) {
2168 |   this.timeout = timeout;
2169 |   jasmine.Block.call(this, env, null, spec);
2170 | };
2171 | 
2172 | jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
2173 | 
2174 | jasmine.WaitsBlock.prototype.execute = function (onComplete) {
2175 |   this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
2176 |   this.env.setTimeout(function () {
2177 |     onComplete();
2178 |   }, this.timeout);
2179 | };
2180 | /**
2181 |  * A block which waits for some condition to become true, with timeout.
2182 |  *
2183 |  * @constructor
2184 |  * @extends jasmine.Block
2185 |  * @param {jasmine.Env} env The Jasmine environment.
2186 |  * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
2187 |  * @param {Function} latchFunction A function which returns true when the desired condition has been met.
2188 |  * @param {String} message The message to display if the desired condition hasn't been met within the given time period.
2189 |  * @param {jasmine.Spec} spec The Jasmine spec.
2190 |  */
2191 | jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
2192 |   this.timeout = timeout || env.defaultTimeoutInterval;
2193 |   this.latchFunction = latchFunction;
2194 |   this.message = message;
2195 |   this.totalTimeSpentWaitingForLatch = 0;
2196 |   jasmine.Block.call(this, env, null, spec);
2197 | };
2198 | jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
2199 | 
2200 | jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
2201 | 
2202 | jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
2203 |   this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
2204 |   var latchFunctionResult;
2205 |   try {
2206 |     latchFunctionResult = this.latchFunction.apply(this.spec);
2207 |   } catch (e) {
2208 |     this.spec.fail(e);
2209 |     onComplete();
2210 |     return;
2211 |   }
2212 | 
2213 |   if (latchFunctionResult) {
2214 |     onComplete();
2215 |   } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
2216 |     var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
2217 |     this.spec.fail({
2218 |       name: 'timeout',
2219 |       message: message
2220 |     });
2221 | 
2222 |     this.abort = true;
2223 |     onComplete();
2224 |   } else {
2225 |     this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
2226 |     var self = this;
2227 |     this.env.setTimeout(function() {
2228 |       self.execute(onComplete);
2229 |     }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
2230 |   }
2231 | };
2232 | // Mock setTimeout, clearTimeout
2233 | // Contributed by Pivotal Computer Systems, www.pivotalsf.com
2234 | 
2235 | jasmine.FakeTimer = function() {
2236 |   this.reset();
2237 | 
2238 |   var self = this;
2239 |   self.setTimeout = function(funcToCall, millis) {
2240 |     self.timeoutsMade++;
2241 |     self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);
2242 |     return self.timeoutsMade;
2243 |   };
2244 | 
2245 |   self.setInterval = function(funcToCall, millis) {
2246 |     self.timeoutsMade++;
2247 |     self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);
2248 |     return self.timeoutsMade;
2249 |   };
2250 | 
2251 |   self.clearTimeout = function(timeoutKey) {
2252 |     self.scheduledFunctions[timeoutKey] = jasmine.undefined;
2253 |   };
2254 | 
2255 |   self.clearInterval = function(timeoutKey) {
2256 |     self.scheduledFunctions[timeoutKey] = jasmine.undefined;
2257 |   };
2258 | 
2259 | };
2260 | 
2261 | jasmine.FakeTimer.prototype.reset = function() {
2262 |   this.timeoutsMade = 0;
2263 |   this.scheduledFunctions = {};
2264 |   this.nowMillis = 0;
2265 | };
2266 | 
2267 | jasmine.FakeTimer.prototype.tick = function(millis) {
2268 |   var oldMillis = this.nowMillis;
2269 |   var newMillis = oldMillis + millis;
2270 |   this.runFunctionsWithinRange(oldMillis, newMillis);
2271 |   this.nowMillis = newMillis;
2272 | };
2273 | 
2274 | jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {
2275 |   var scheduledFunc;
2276 |   var funcsToRun = [];
2277 |   for (var timeoutKey in this.scheduledFunctions) {
2278 |     scheduledFunc = this.scheduledFunctions[timeoutKey];
2279 |     if (scheduledFunc != jasmine.undefined &&
2280 |         scheduledFunc.runAtMillis >= oldMillis &&
2281 |         scheduledFunc.runAtMillis <= nowMillis) {
2282 |       funcsToRun.push(scheduledFunc);
2283 |       this.scheduledFunctions[timeoutKey] = jasmine.undefined;
2284 |     }
2285 |   }
2286 | 
2287 |   if (funcsToRun.length > 0) {
2288 |     funcsToRun.sort(function(a, b) {
2289 |       return a.runAtMillis - b.runAtMillis;
2290 |     });
2291 |     for (var i = 0; i < funcsToRun.length; ++i) {
2292 |       try {
2293 |         var funcToRun = funcsToRun[i];
2294 |         this.nowMillis = funcToRun.runAtMillis;
2295 |         funcToRun.funcToCall();
2296 |         if (funcToRun.recurring) {
2297 |           this.scheduleFunction(funcToRun.timeoutKey,
2298 |               funcToRun.funcToCall,
2299 |               funcToRun.millis,
2300 |               true);
2301 |         }
2302 |       } catch(e) {
2303 |       }
2304 |     }
2305 |     this.runFunctionsWithinRange(oldMillis, nowMillis);
2306 |   }
2307 | };
2308 | 
2309 | jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {
2310 |   this.scheduledFunctions[timeoutKey] = {
2311 |     runAtMillis: this.nowMillis + millis,
2312 |     funcToCall: funcToCall,
2313 |     recurring: recurring,
2314 |     timeoutKey: timeoutKey,
2315 |     millis: millis
2316 |   };
2317 | };
2318 | 
2319 | /**
2320 |  * @namespace
2321 |  */
2322 | jasmine.Clock = {
2323 |   defaultFakeTimer: new jasmine.FakeTimer(),
2324 | 
2325 |   reset: function() {
2326 |     jasmine.Clock.assertInstalled();
2327 |     jasmine.Clock.defaultFakeTimer.reset();
2328 |   },
2329 | 
2330 |   tick: function(millis) {
2331 |     jasmine.Clock.assertInstalled();
2332 |     jasmine.Clock.defaultFakeTimer.tick(millis);
2333 |   },
2334 | 
2335 |   runFunctionsWithinRange: function(oldMillis, nowMillis) {
2336 |     jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);
2337 |   },
2338 | 
2339 |   scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
2340 |     jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);
2341 |   },
2342 | 
2343 |   useMock: function() {
2344 |     if (!jasmine.Clock.isInstalled()) {
2345 |       var spec = jasmine.getEnv().currentSpec;
2346 |       spec.after(jasmine.Clock.uninstallMock);
2347 | 
2348 |       jasmine.Clock.installMock();
2349 |     }
2350 |   },
2351 | 
2352 |   installMock: function() {
2353 |     jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;
2354 |   },
2355 | 
2356 |   uninstallMock: function() {
2357 |     jasmine.Clock.assertInstalled();
2358 |     jasmine.Clock.installed = jasmine.Clock.real;
2359 |   },
2360 | 
2361 |   real: {
2362 |     setTimeout: jasmine.getGlobal().setTimeout,
2363 |     clearTimeout: jasmine.getGlobal().clearTimeout,
2364 |     setInterval: jasmine.getGlobal().setInterval,
2365 |     clearInterval: jasmine.getGlobal().clearInterval
2366 |   },
2367 | 
2368 |   assertInstalled: function() {
2369 |     if (!jasmine.Clock.isInstalled()) {
2370 |       throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
2371 |     }
2372 |   },
2373 | 
2374 |   isInstalled: function() {
2375 |     return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;
2376 |   },
2377 | 
2378 |   installed: null
2379 | };
2380 | jasmine.Clock.installed = jasmine.Clock.real;
2381 | 
2382 | //else for IE support
2383 | jasmine.getGlobal().setTimeout = function(funcToCall, millis) {
2384 |   if (jasmine.Clock.installed.setTimeout.apply) {
2385 |     return jasmine.Clock.installed.setTimeout.apply(this, arguments);
2386 |   } else {
2387 |     return jasmine.Clock.installed.setTimeout(funcToCall, millis);
2388 |   }
2389 | };
2390 | 
2391 | jasmine.getGlobal().setInterval = function(funcToCall, millis) {
2392 |   if (jasmine.Clock.installed.setInterval.apply) {
2393 |     return jasmine.Clock.installed.setInterval.apply(this, arguments);
2394 |   } else {
2395 |     return jasmine.Clock.installed.setInterval(funcToCall, millis);
2396 |   }
2397 | };
2398 | 
2399 | jasmine.getGlobal().clearTimeout = function(timeoutKey) {
2400 |   if (jasmine.Clock.installed.clearTimeout.apply) {
2401 |     return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
2402 |   } else {
2403 |     return jasmine.Clock.installed.clearTimeout(timeoutKey);
2404 |   }
2405 | };
2406 | 
2407 | jasmine.getGlobal().clearInterval = function(timeoutKey) {
2408 |   if (jasmine.Clock.installed.clearTimeout.apply) {
2409 |     return jasmine.Clock.installed.clearInterval.apply(this, arguments);
2410 |   } else {
2411 |     return jasmine.Clock.installed.clearInterval(timeoutKey);
2412 |   }
2413 | };
2414 | 
2415 | 
2416 | jasmine.version_= {
2417 |   "major": 1,
2418 |   "minor": 0,
2419 |   "build": "0.rc1",
2420 |   "revision": 1282853377
2421 | };
2422 | 


--------------------------------------------------------------------------------
/lib/jasmine/jskoans-jasmine-html.js:
--------------------------------------------------------------------------------
  1 | JsKoansReporter = function(doc) {
  2 |   this.document = doc || document;
  3 |   this.suiteDivs = {};
  4 |   this.failedSpecs = 0;
  5 | 
  6 |   this.noOfSubjects = 0;
  7 |   this.failedSubjects = 0;
  8 | };
  9 | 
 10 | JsKoansReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
 11 |   var el = document.createElement(type);
 12 | 
 13 |   for (var i = 2; i < arguments.length; i++) {
 14 |     var child = arguments[i];
 15 | 
 16 |     if (typeof child === 'string') {
 17 |       el.appendChild(document.createTextNode(child));
 18 |     } else {
 19 |       if (child) { el.appendChild(child); }
 20 |     }
 21 |   }
 22 | 
 23 |   for (var attr in attrs) {
 24 |     if (attr == "className") {
 25 |       el[attr] = attrs[attr];
 26 |     } else {
 27 |       el.setAttribute(attr, attrs[attr]);
 28 |     }
 29 |   }
 30 | 
 31 |   return el;
 32 | };
 33 | 
 34 | JsKoansReporter.prototype.reportRunnerStarting = function(runner) {
 35 |   this.outerDiv = this.createDom('div', { className: 'jasmine_reporter show-passed' },
 36 |       this.createDom('h1', { }, "Javascript Koans"),
 37 |       this.runnerDiv = this.createDom('div', { className: 'runner running' },
 38 |         this.runnerMessageSpan = this.createDom('span', { classname: 'running' }, "Contemplating naval..."),
 39 |         this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
 40 |       );
 41 | 
 42 |   this.document.body.appendChild(this.outerDiv);
 43 | 
 44 |   var suites = runner.suites();
 45 |   for (var i = 0; i < suites.length; i++) {
 46 |     var suite = suites[i];
 47 |     var suiteDiv = this.createDom('div', { className: 'suite' },
 48 |         this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, 
 49 |           this.getSuiteDescription(suite)));
 50 |     this.suiteDivs[suite.id] = suiteDiv;
 51 |     var parentDiv = this.outerDiv;
 52 |     if (suite.parentSuite) {
 53 |       parentDiv = this.suiteDivs[suite.parentSuite.id];
 54 |     }
 55 |     parentDiv.appendChild(suiteDiv);
 56 |   }
 57 | 
 58 |   this.footerDiv = this.createDom('div', { className: 'banner' },
 59 |       this.createDom('div', { className: 'logo' },
 60 |         "Test runner: Jasmine",
 61 |         this.createDom('span', { className: 'version' }, runner.env.versionString()))
 62 |       );
 63 | 
 64 |   this.outerDiv.appendChild(this.footerDiv);
 65 | 
 66 |   this.startedAt = new Date();
 67 | };
 68 | 
 69 | JsKoansReporter.prototype.reportRunnerResults = function(runner) {
 70 |   var results = runner.results();
 71 |   var className = "progress";
 72 |   this.runnerDiv.setAttribute("class", className);
 73 |   //do it twice for IE
 74 |   this.runnerDiv.setAttribute("className", className);
 75 |   var specs = runner.specs();
 76 |   var specCount = 0;
 77 |   for (var i = 0; i < specs.length; i++) {
 78 |     if (this.specFilter(specs[i])) {
 79 |       specCount++;
 80 |     }
 81 |   }
 82 | 
 83 |   var enlightenmentMessage;
 84 |   if (this.failedSpecs === 0) {
 85 |     status = 'passed';
 86 |     enlightenmentMessage = "Enlightenment!";
 87 |   } else {
 88 |     status = 'failed';
 89 |     enlightenmentMessage = "You have not yet reached enlightenment...";
 90 |   }
 91 | 
 92 |   var suitesCount = runner.suites().length;
 93 |   var specsCount = runner.specs().length;
 94 |   var showPassed;
 95 |   var showAllFailed;
 96 |   
 97 |   var progress = this.createDom('div', {}, 
 98 |       this.createDom('div', { className: 'enlightenment-' + status }, enlightenmentMessage),
 99 |       this.createDom('div', { className: 'completion' }, 
100 |         this.createDom('div', {},
101 |           this.createDom('span', { className: 'key' }, "Subjects covered: "),
102 |           this.createDom('span', { className: 'value' }, this.noOfSubjects - this.failedSubjects + "/" + this.noOfSubjects)
103 |           ),
104 |         this.createDom('div', {},
105 |           this.createDom('span', { className: 'key' }, "Koans learned: "),
106 |           this.createDom('span', { className: 'value' }, specsCount - this.failedSpecs + "/" + runner.specs().length)
107 |           ),
108 |         this.createDom('div', { className: 'options' },
109 |           this.createDom('label', { "for": "__jsKoans_showPassed__" }, " Show passed koans"),
110 |           showPassed = this.createDom('input', { id: "__jsKoans_showPassed__", type: 'checkbox', checked: '' }),
111 |           this.createDom('label', { "for": "__jsKoans_showAllFailed__" }, " Look ahead"),
112 |           showAllFailed = this.createDom('input', { id: "__jsKoans_showAllFailed__", type: 'checkbox' })
113 |           )
114 |         )      
115 |       );
116 |   this.runnerMessageSpan.replaceChild(this.createDom('div', { className: 'description', href: '?'}, progress), this.runnerMessageSpan.firstChild);
117 |   
118 |   var self = this;
119 |   showPassed.onchange = function(evt) {
120 |     if (evt.target.checked) {
121 |       self.outerDiv.className += ' show-passed';
122 |     } else {
123 |       self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
124 |     }
125 |   };
126 |   showAllFailed.onchange = function(evt) {
127 |     if (evt.target.checked) {
128 |       self.outerDiv.className += ' show-skipped';
129 |     } else {
130 |       self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
131 |     }
132 |   };
133 | };
134 | 
135 | JsKoansReporter.prototype.reportSuiteResults = function(suite) {
136 |   var results = suite.results();
137 |   var status = results.passed() ? 'passed' : 'failed';
138 |   if (results.totalCount == 0 || this.failedSubjects > 0) {
139 |     status += '-skipped';  
140 |   }
141 | 
142 |   if (suite.parentSuite == null) {
143 |     this.noOfSubjects +=1;
144 |     if (this.failedSpecs > 0) {
145 |       this.failedSubjects += 1;
146 |     }
147 |   }
148 | 
149 |   this.suiteDivs[suite.id].className += " " + status;
150 | };
151 | 
152 | JsKoansReporter.prototype.reportSpecStarting = function(spec) {
153 | };
154 | 
155 | JsKoansReporter.prototype.reportSpecResults = function(spec) {
156 |   var results = spec.results();
157 |   var status = results.passed() ? 'passed' : 'failed';
158 |   var skipStatus = status;
159 |   
160 |   if (results.skipped || this.failedSpecs > 0) {
161 |     skipStatus += '-skipped';
162 |   }
163 | 
164 |   var description;
165 |   if ( !results.passed() ) {
166 |     this.failedSpecs += 1;
167 | 
168 |     description = "It " + spec.description + ". It is damaging your karma."
169 |   } else {
170 |     description = "Knowing it " + spec.description + " has expanded your awareness."
171 |   }
172 | 
173 |   var specDiv = this.createDom('div', { className: 'spec '  + skipStatus },
174 |       this.createDom('a', { className: 'run_spec_' + status, href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "meditate again"),
175 |       this.createDom('a', {
176 |         className: 'description',
177 |         href: '?spec=' + encodeURIComponent(spec.getFullName()),
178 |         title: spec.getFullName()
179 |       }, description));
180 | 
181 |   var resultItems = results.getItems();
182 |   var messagesDiv = this.createDom('div', { className: 'messages'});
183 | 
184 |   for (var i = 0; i < resultItems.length; i++) {
185 |     var result = resultItems[i];
186 | 
187 |     if (result.type == 'expect' && result.passed && !result.passed()) {
188 |       messagesDiv.appendChild(
189 |           this.createDom('div', { className: 'errorMessage' }, result.message)
190 |           );
191 |       messagesDiv.appendChild(
192 |           this.createDom('div', { className: 'description' }, "Please meditate on the following code:")
193 |           );
194 | 
195 |       if (result.trace.stack) {
196 |         var lines = result.trace.stack.split('\n');
197 |         var stack = lines[0];
198 |         for (var i = 1; i < lines.length; i++) {
199 |           if (lines[i].search('/koans/') != -1) {
200 |             stack += '\n' + lines[i]
201 |           }
202 |         }
203 |         messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, stack.trim()));
204 |       }
205 | 
206 |       break;
207 |     }
208 |   }
209 |   if (messagesDiv.childNodes.length > 0) {
210 |     specDiv.appendChild(messagesDiv);
211 |   }
212 | 
213 |   this.suiteDivs[spec.suite.id].appendChild(specDiv);
214 |   
215 | };
216 | 
217 | JsKoansReporter.prototype.log = function() {
218 |   var console = jasmine.getGlobal().console;
219 |   if (console && console.log) console.log.apply(console, arguments);
220 | };
221 | 
222 | JsKoansReporter.prototype.getLocation = function() {
223 |   return this.document.location;
224 | };
225 | 
226 | JsKoansReporter.prototype.specFilter = function(spec) {
227 |   var paramMap = {};
228 |   var params = this.getLocation().search.substring(1).split('&');
229 |   for (var i = 0; i < params.length; i++) {
230 |     var p = params[i].split('=');
231 |     paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
232 |   }
233 | 
234 |   if (!paramMap["spec"]) return true;
235 |   return spec.getFullName().indexOf(paramMap["spec"]) == 0;
236 | };
237 | 
238 | JsKoansReporter.prototype.getSuiteDescription = function(suite) {
239 |   if (null === suite.parentSuite) {
240 |     return "Thinking " + suite.description;
241 |   } else {
242 |     return "Considering " + suite.description;
243 |   }
244 | };
245 | 
246 | 


--------------------------------------------------------------------------------
/lib/jasmine/jskoans-jasmine.css:
--------------------------------------------------------------------------------
  1 | body {
  2 |   font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif;
  3 | }
  4 | 
  5 | h1 {
  6 |   text-align: center;
  7 |   font-weight: bold;
  8 |   color: #78f;
  9 | }
 10 | 
 11 | .jasmine_reporter a:visited, .jasmine_reporter a {
 12 |   color: #303; 
 13 | }
 14 | 
 15 | .jasmine_reporter a:hover, .jasmine_reporter a:active {
 16 |   color: blue; 
 17 | }
 18 | 
 19 | .run_spec_failed, .run_spec_skipped {
 20 |   float:right;
 21 |   padding-right: 5px;
 22 |   font-size: .8em;
 23 |   text-decoration: none;
 24 | }
 25 | 
 26 | .run_spec_passed {
 27 |   display: none;
 28 | }
 29 | 
 30 | .jasmine_reporter {
 31 |   margin: 0 5px;
 32 | }
 33 | 
 34 | .banner {
 35 |   color: #303;
 36 |   background-color: #fef;
 37 |   padding: 5px;
 38 |   font-size: 0.6em;
 39 | }
 40 | 
 41 | .logo {
 42 | }
 43 | 
 44 | .logo .version {
 45 |   font-size: 0.9em;
 46 |   padding-left: 1em;
 47 | }
 48 | 
 49 | .runner.running {
 50 |   background-color: yellow;
 51 | }
 52 | 
 53 | .options {
 54 |   padding-top: 0.5px;
 55 | }
 56 | 
 57 | .suite {
 58 |   border: 1px outset gray;
 59 |   margin: 5px 0;
 60 |   padding-left: 1em;
 61 | }
 62 | 
 63 | .suite .suite {
 64 |   margin: 5px; 
 65 | }
 66 | 
 67 | .suite.passed, .suite.passed-skipped {
 68 |   background-color: #dfd;
 69 | }
 70 | 
 71 | .suite.failed, .suite.failed-skipped {
 72 |   background-color: #fdd;
 73 | }
 74 | 
 75 | .spec {
 76 |   margin: 5px;
 77 |   padding-left: 1em;
 78 |   clear: both;
 79 | }
 80 | 
 81 | .spec.failed, .spec.passed, .spec.passed-skipped, .spec.failed-skipped {
 82 |   padding-bottom: 5px;
 83 |   border: 1px solid gray;
 84 | }
 85 | 
 86 | .spec.failed, .spec.failed-skipped {
 87 |   background-color: #fbb;
 88 |   border-color: red;
 89 | }
 90 | 
 91 | .spec.passed, .spec.passed-skipped {
 92 |   background-color: #bfb;
 93 |   border-color: green;
 94 | }
 95 | 
 96 | .messages {
 97 |   border-left: 1px;
 98 | }
 99 | 
100 | .errorMessage {
101 |   font-family: "Menlo", "Monaco", "Andale Mono", "Courier New", "Courier", sans-serif; 
102 |   color: #055;
103 |   white-space: pre;
104 |   padding: .2em 1em;
105 |   margin-left: 10px;
106 |   margin-right: 5px;
107 |   margin-bottom: 10px;
108 |   background: #eef;
109 | }
110 | 
111 | .meditate {
112 |   padding-top: 1em;
113 | }
114 | 
115 | .passed {
116 |   background-color: #cfc;
117 |   display: none;
118 | }
119 | 
120 | .failed {
121 |   background-color: #fbb;
122 |   display: block;
123 | }
124 | 
125 | .passed-skipped, .failed-skipped {
126 |    display: none;
127 | }   
128 | 
129 | .resultMessage span.result {
130 |   display: block;
131 |   line-height: 2em;
132 |   color: black;
133 | }
134 | 
135 | .resultMessage .mismatch {
136 |   color: black;
137 | }
138 | 
139 | .stackTrace {
140 |   white-space: pre;
141 |   font-size: .8em;
142 |   margin-left: 10px;
143 |   margin-right: 5px;
144 |   max-height: 5em;
145 |   overflow: auto;
146 |   border: 1px inset red;
147 |   padding: 1em;
148 |   color: #eef;
149 |   background: #000;
150 | }
151 | 
152 | .finished-at {
153 |   padding-left: 1em;
154 |   font-size: .6em;
155 | }
156 | 
157 | .show-skipped .passed-skipped {
158 |   display: block;
159 | }
160 | 
161 | .show-skipped .failed-skipped {
162 |   display: block;
163 | }
164 | 
165 | .show-passed .passed {
166 |   display: block;
167 | }
168 | .show-failed .failed {
169 |   display: block;
170 | }
171 | 
172 | #jasmine_content {
173 |   position:fixed;
174 |   right: 100%;
175 | }
176 | 
177 | .runner {
178 |   border: 1px solid gray;
179 |   display: block;
180 |   margin: 5px 0;
181 |   padding: 2px 0 2px 10px;
182 | }
183 | 
184 | .progress {
185 | }
186 | 
187 | .completion {
188 |   font-size: .9em;
189 |   padding-top: 10px;
190 | }
191 | 
192 | .key {
193 |   color: #077;
194 | }
195 | 
196 | .value {
197 |   color: #444;
198 | }
199 | 
200 | .description {
201 |   text-decoration: none;
202 | }
203 | 
204 | .enlightenment-passed {
205 |   color: #990;
206 |   font-weight: bold;
207 | }
208 | .enlightenment-failed {
209 |   color: #550;
210 | }
211 | 


--------------------------------------------------------------------------------
/lib/jsTestDriver/JsTestDriver.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrdavidlaing/javascript-koans/1593981b62a63734f70ac9a959e850680c93aac8/lib/jsTestDriver/JsTestDriver.jar


--------------------------------------------------------------------------------
/lib/jsTestDriver/capture_browser.sh:
--------------------------------------------------------------------------------
1 | java -jar JsTestDriver.jar --port 42442 --runnerMode DEBUG --browser /Applications/Firefox-3.app/Contents/MacOS/firefox-bin


--------------------------------------------------------------------------------
/lib/jsTestDriver/jsTestDriver.conf:
--------------------------------------------------------------------------------
 1 | server: http://localhost:42442
 2 | 
 3 | load:
 4 |   - src/dojo-release-1.5.0/dojo/dojo.js
 5 |   - src/dojo-release-1.5.0/dojox/lang/functional/*.js
 6 |   - src/*.js
 7 | 
 8 |   - src-test/PathToEnlightenment.js
 9 |   - src-test/AboutAsserts.js
10 |   - src-test/AboutArrays.js
11 |   - src-test/AboutFunctions.js
12 |   - src-test/AboutObjects.js
13 |   - src-test/AboutMutability.js
14 |   - src-test/AboutLambda.js
15 |   - src-test/AboutHigherOrderFunctions.js
16 |   - src-test/ApplyingWhatWeveLearnt.js
17 | 


--------------------------------------------------------------------------------
/lib/jsTestDriver/run_all_koans.sh:
--------------------------------------------------------------------------------
1 | java -jar JsTestDriver.jar --reset --tests all


--------------------------------------------------------------------------------
/lib/underscore-min.js:
--------------------------------------------------------------------------------
 1 | // Underscore.js 1.1.6
 2 | // (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
 3 | // Underscore is freely distributable under the MIT license.
 4 | // Portions of Underscore are inspired or borrowed from Prototype,
 5 | // Oliver Steele's Functional, and John Resig's Micro-Templating.
 6 | // For all details and documentation:
 7 | // http://documentcloud.github.com/underscore
 8 | (function(){var p=this,C=p._,m={},i=Array.prototype,n=Object.prototype,f=i.slice,D=i.unshift,E=n.toString,l=n.hasOwnProperty,s=i.forEach,t=i.map,u=i.reduce,v=i.reduceRight,w=i.filter,x=i.every,y=i.some,o=i.indexOf,z=i.lastIndexOf;n=Array.isArray;var F=Object.keys,q=Function.prototype.bind,b=function(a){return new j(a)};typeof module!=="undefined"&&module.exports?(module.exports=b,b._=b):p._=b;b.VERSION="1.1.6";var h=b.each=b.forEach=function(a,c,d){if(a!=null)if(s&&a.forEach===s)a.forEach(c,d);else if(b.isNumber(a.length))for(var e=
 9 | 0,k=a.length;e<k;e++){if(c.call(d,a[e],e,a)===m)break}else for(e in a)if(l.call(a,e)&&c.call(d,a[e],e,a)===m)break};b.map=function(a,c,b){var e=[];if(a==null)return e;if(t&&a.map===t)return a.map(c,b);h(a,function(a,g,G){e[e.length]=c.call(b,a,g,G)});return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var k=d!==void 0;a==null&&(a=[]);if(u&&a.reduce===u)return e&&(c=b.bind(c,e)),k?a.reduce(c,d):a.reduce(c);h(a,function(a,b,f){!k&&b===0?(d=a,k=!0):d=c.call(e,d,a,b,f)});if(!k)throw new TypeError("Reduce of empty array with no initial value");
10 | return d};b.reduceRight=b.foldr=function(a,c,d,e){a==null&&(a=[]);if(v&&a.reduceRight===v)return e&&(c=b.bind(c,e)),d!==void 0?a.reduceRight(c,d):a.reduceRight(c);a=(b.isArray(a)?a.slice():b.toArray(a)).reverse();return b.reduce(a,c,d,e)};b.find=b.detect=function(a,c,b){var e;A(a,function(a,g,f){if(c.call(b,a,g,f))return e=a,!0});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(w&&a.filter===w)return a.filter(c,b);h(a,function(a,g,f){c.call(b,a,g,f)&&(e[e.length]=a)});return e};
11 | b.reject=function(a,c,b){var e=[];if(a==null)return e;h(a,function(a,g,f){c.call(b,a,g,f)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=!0;if(a==null)return e;if(x&&a.every===x)return a.every(c,b);h(a,function(a,g,f){if(!(e=e&&c.call(b,a,g,f)))return m});return e};var A=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=!1;if(a==null)return e;if(y&&a.some===y)return a.some(c,d);h(a,function(a,b,f){if(e=c.call(d,a,b,f))return m});return e};b.include=b.contains=function(a,c){var b=
12 | !1;if(a==null)return b;if(o&&a.indexOf===o)return a.indexOf(c)!=-1;A(a,function(a){if(b=a===c)return!0});return b};b.invoke=function(a,c){var d=f.call(arguments,2);return b.map(a,function(a){return(c.call?c||a:a[c]).apply(a,d)})};b.pluck=function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);var e={computed:-Infinity};h(a,function(a,b,f){b=c?c.call(d,a,b,f):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,
13 | c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};h(a,function(a,b,f){b=c?c.call(d,a,b,f):a;b<e.computed&&(e={value:a,computed:b})});return e.value};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(a,b,f){return{value:a,criteria:c.call(d,a,b,f)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c<d?-1:c>d?1:0}),"value")};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=
14 | function(a){if(!a)return[];if(a.toArray)return a.toArray();if(b.isArray(a))return a;if(b.isArguments(a))return f.call(a);return b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=b.head=function(a,b,d){return b!=null&&!d?f.call(a,0,b):a[0]};b.rest=b.tail=function(a,b,d){return f.call(a,b==null||d?1:b)};b.last=function(a){return a[a.length-1]};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a){return b.reduce(a,function(a,d){if(b.isArray(d))return a.concat(b.flatten(d));
15 | a[a.length]=d;return a},[])};b.without=function(a){var c=f.call(arguments,1);return b.filter(a,function(a){return!b.include(c,a)})};b.uniq=b.unique=function(a,c){return b.reduce(a,function(a,e,f){if(0==f||(c===!0?b.last(a)!=e:!b.include(a,e)))a[a.length]=e;return a},[])};b.intersect=function(a){var c=f.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.zip=function(){for(var a=f.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),
16 | e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,d){if(a==null)return-1;var e;if(d)return d=b.sortedIndex(a,c),a[d]===c?d:-1;if(o&&a.indexOf===o)return a.indexOf(c);d=0;for(e=a.length;d<e;d++)if(a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(z&&a.lastIndexOf===z)return a.lastIndexOf(b);for(var d=a.length;d--;)if(a[d]===b)return d;return-1};b.range=function(a,b,d){arguments.length<=1&&(b=a||0,a=0);d=arguments[2]||1;for(var e=Math.max(Math.ceil((b-a)/
17 | d),0),f=0,g=Array(e);f<e;)g[f++]=a,a+=d;return g};b.bind=function(a,b){if(a.bind===q&&q)return q.apply(a,f.call(arguments,1));var d=f.call(arguments,2);return function(){return a.apply(b,d.concat(f.call(arguments)))}};b.bindAll=function(a){var c=f.call(arguments,1);c.length==0&&(c=b.functions(a));h(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,c){var d={};c||(c=b.identity);return function(){var b=c.apply(this,arguments);return l.call(d,b)?d[b]:d[b]=a.apply(this,arguments)}};b.delay=
18 | function(a,b){var d=f.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(f.call(arguments,1)))};var B=function(a,b,d){var e;return function(){var f=this,g=arguments,h=function(){e=null;a.apply(f,g)};d&&clearTimeout(e);if(d||!e)e=setTimeout(h,b)}};b.throttle=function(a,b){return B(a,b,!1)};b.debounce=function(a,b){return B(a,b,!0)};b.once=function(a){var b=!1,d;return function(){if(b)return d;b=!0;return d=a.apply(this,arguments)}};
19 | b.wrap=function(a,b){return function(){var d=[a].concat(f.call(arguments));return b.apply(this,d)}};b.compose=function(){var a=f.call(arguments);return function(){for(var b=f.call(arguments),d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return function(){if(--a<1)return b.apply(this,arguments)}};b.keys=F||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)l.call(a,d)&&(b[b.length]=d);return b};b.values=function(a){return b.map(a,
20 | b.identity)};b.functions=b.methods=function(a){return b.filter(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a){h(f.call(arguments,1),function(b){for(var d in b)b[d]!==void 0&&(a[d]=b[d])});return a};b.defaults=function(a){h(f.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,c){if(a===c)return!0;var d=typeof a;if(d!=
21 | typeof c)return!1;if(a==c)return!0;if(!a&&c||a&&!c)return!1;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return!1;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return!1;if(a.length&&a.length!==c.length)return!1;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return!1;
22 | for(var f in a)if(!(f in c)||!b.isEqual(a[f],c[f]))return!1;return!0};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(l.call(a,c))return!1;return!0};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=n||function(a){return E.call(a)==="[object Array]"};b.isArguments=function(a){return!(!a||!l.call(a,"callee"))};b.isFunction=function(a){return!(!a||!a.constructor||!a.call||!a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};
23 | b.isNumber=function(a){return!!(a===0||a&&a.toExponential&&a.toFixed)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===!0||a===!1};b.isDate=function(a){return!(!a||!a.getTimezoneOffset||!a.setUTCFullYear)};b.isRegExp=function(a){return!(!a||!a.test||!a.exec||!(a.ignoreCase||a.ignoreCase===!1))};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.noConflict=function(){p._=C;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=
24 | 0;e<a;e++)b.call(d,e)};b.mixin=function(a){h(b.functions(a),function(c){H(c,b[c]=a[c])})};var I=0;b.uniqueId=function(a){var b=I++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g};b.template=function(a,c){var d=b.templateSettings;d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.interpolate,function(a,b){return"',"+b.replace(/\\'/g,"'")+",'"}).replace(d.evaluate||
25 | null,function(a,b){return"');"+b.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+"__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');";d=new Function("obj",d);return c?d(c):d};var j=function(a){this._wrapped=a};b.prototype=j.prototype;var r=function(a,c){return c?b(a).chain():a},H=function(a,c){j.prototype[a]=function(){var a=f.call(arguments);D.call(a,this._wrapped);return r(c.apply(b,a),this._chain)}};b.mixin(b);h(["pop","push","reverse","shift","sort",
26 | "splice","unshift"],function(a){var b=i[a];j.prototype[a]=function(){b.apply(this._wrapped,arguments);return r(this._wrapped,this._chain)}});h(["concat","join","slice"],function(a){var b=i[a];j.prototype[a]=function(){return r(b.apply(this._wrapped,arguments),this._chain)}});j.prototype.chain=function(){this._chain=!0;return this};j.prototype.value=function(){return this._wrapped}})();
27 | 


--------------------------------------------------------------------------------