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