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": 1252545239
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 
596   this.updateInterval = 0;
597   this.lastUpdate = 0;
598   this.specFilter = function() {
599     return true;
600   };
601 
602   this.nextSpecId_ = 0;
603   this.nextSuiteId_ = 0;
604   this.equalityTesters_ = [];
605 };
606 
607 
608 jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
609 jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
610 jasmine.Env.prototype.setInterval = jasmine.setInterval;
611 jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
612 
613 jasmine.Env.prototype.version = function () {
614   if (jasmine.version_) {
615     return jasmine.version_;
616   } else {
617     throw new Error('Version not set');
618   }
619 };
620 
621 /**
622  * Register a reporter to receive status updates from Jasmine.
623  * @param {jasmine.Reporter} reporter An object which will receive status updates.
624  */
625 jasmine.Env.prototype.addReporter = function(reporter) {
626   this.reporter.addReporter(reporter);
627 };
628 
629 jasmine.Env.prototype.execute = function() {
630   this.currentRunner.execute();
631 };
632 
633 jasmine.Env.prototype.describe = function(description, specDefinitions) {
634   var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
635 
636   var parentSuite = this.currentSuite;
637   if (parentSuite) {
638     parentSuite.add(suite);
639   } else {
640     this.currentRunner.add(suite);
641   }
642 
643   this.currentSuite = suite;
644 
645   specDefinitions.call(suite);
646 
647   this.currentSuite = parentSuite;
648 
649   return suite;
650 };
651 
652 jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
653   this.currentSuite.beforeEach(beforeEachFunction);
654 };
655 
656 jasmine.Env.prototype.afterEach = function(afterEachFunction) {
657   this.currentSuite.afterEach(afterEachFunction);
658 };
659 
660 jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
661   return {
662     execute: function() {
663     }
664   };
665 };
666 
667 jasmine.Env.prototype.it = function(description, func) {
668   var spec = new jasmine.Spec(this, this.currentSuite, description);
669   this.currentSuite.add(spec);
670   this.currentSpec = spec;
671 
672   if (func) {
673     spec.runs(func);
674   }
675 
676   return spec;
677 };
678 
679 jasmine.Env.prototype.xit = function(desc, func) {
680   return {
681     id: this.nextSpecId_++,
682     runs: function() {
683     }
684   };
685 };
686 
687 jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
688   if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
689     return true;
690   }
691 
692   a.__Jasmine_been_here_before__ = b;
693   b.__Jasmine_been_here_before__ = a;
694 
695   var hasKey = function(obj, keyName) {
696     return obj != null && obj[keyName] !== undefined;
697   };
698 
699   for (var property in b) {
700     if (!hasKey(a, property) && hasKey(b, property)) {
701       mismatchKeys.push("expected has key '" + property + "', but missing from <b>actual</b>.");
702     }
703   }
704   for (property in a) {
705     if (!hasKey(b, property) && hasKey(a, property)) {
706       mismatchKeys.push("<b>expected</b> missing key '" + property + "', but present in actual.");
707     }
708   }
709   for (property in b) {
710     if (property == '__Jasmine_been_here_before__') continue;
711     if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
712       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 />");
713     }
714   }
715 
716   if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
717     mismatchValues.push("arrays were not the same length");
718   }
719 
720   delete a.__Jasmine_been_here_before__;
721   delete b.__Jasmine_been_here_before__;
722   return (mismatchKeys.length == 0 && mismatchValues.length == 0);
723 };
724 
725 jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
726   mismatchKeys = mismatchKeys || [];
727   mismatchValues = mismatchValues || [];
728 
729   if (a === b) return true;
730 
731   if (a === undefined || a === null || b === undefined || b === null) {
732     return (a == undefined && b == undefined);
733   }
734 
735   if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
736     return a === b;
737   }
738 
739   if (a instanceof Date && b instanceof Date) {
740     return a.getTime() == b.getTime();
741   }
742 
743   if (a instanceof jasmine.Matchers.Any) {
744     return a.matches(b);
745   }
746 
747   if (b instanceof jasmine.Matchers.Any) {
748     return b.matches(a);
749   }
750 
751   if (typeof a === "object" && typeof b === "object") {
752     return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
753   }
754 
755   for (var i = 0; i < this.equalityTesters_.length; i++) {
756     var equalityTester = this.equalityTesters_[i];
757     var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
758     if (result !== undefined) return result;
759   }
760 
761   //Straight check
762   return (a === b);
763 };
764 
765 jasmine.Env.prototype.contains_ = function(haystack, needle) {
766   if (jasmine.isArray_(haystack)) {
767     for (var i = 0; i < haystack.length; i++) {
768       if (this.equals_(haystack[i], needle)) return true;
769     }
770     return false;
771   }
772   return haystack.indexOf(needle) >= 0;
773 };
774 
775 jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
776   this.equalityTesters_.push(equalityTester);
777 };
778 /** No-op base class for Jasmine reporters.
779  *
780  * @constructor
781  */
782 jasmine.Reporter = function() {
783 };
784 
785 //noinspection JSUnusedLocalSymbols
786 jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
787 };
788 
789 //noinspection JSUnusedLocalSymbols
790 jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
791 };
792 
793 //noinspection JSUnusedLocalSymbols
794 jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
795 };
796 
797 //noinspection JSUnusedLocalSymbols
798 jasmine.Reporter.prototype.reportSpecResults = function(spec) {
799 };
800 
801 //noinspection JSUnusedLocalSymbols
802 jasmine.Reporter.prototype.log = function(str) {
803 };
804 
805 /**
806  * Blocks are functions with executable code that make up a spec.
807  *
808  * @constructor
809  * @param {jasmine.Env} env
810  * @param {Function} func
811  * @param {jasmine.Spec} spec
812  */
813 jasmine.Block = function(env, func, spec) {
814   this.env = env;
815   this.func = func;
816   this.spec = spec;
817 };
818 
819 jasmine.Block.prototype.execute = function(onComplete) {  
820   try {
821     this.func.apply(this.spec);
822   } catch (e) {
823     this.fail(e);
824   }
825   onComplete();
826 };
827 
828 jasmine.Block.prototype.fail = function(e) {
829   this.spec.results.addResult(new jasmine.ExpectationResult(false, jasmine.util.formatException(e), null));
830 };
831 /** JavaScript API reporter.
832  *
833  * @constructor
834  */
835 jasmine.JsApiReporter = function() {
836   this.started = false;
837   this.finished = false;
838   this.suites = [];
839   this.results = {};
840 };
841 
842 jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
843   this.started = true;
844   var suites = runner.suites();
845   for (var i = 0; i < suites.length; i++) {
846     var suite = suites[i];
847     this.suites.push(this.summarize_(suite));
848   }
849 };
850 
851 jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
852   var isSuite = suiteOrSpec instanceof jasmine.Suite
853   var summary = {
854     id: suiteOrSpec.id,
855     name: suiteOrSpec.description,
856     type: isSuite ? 'suite' : 'spec',
857     children: []
858   };
859   if (isSuite) {
860     var specs = suiteOrSpec.specs();
861     for (var i = 0; i < specs.length; i++) {
862       summary.children.push(this.summarize_(specs[i]));
863     }
864   }
865   return summary;
866 };
867 
868 //noinspection JSUnusedLocalSymbols
869 jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
870   this.finished = true;
871 };
872 
873 //noinspection JSUnusedLocalSymbols
874 jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
875 };
876 
877 //noinspection JSUnusedLocalSymbols
878 jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
879   this.results[spec.id] = {
880     messages: spec.results.getItems(),
881     result: spec.results.failedCount > 0 ? "failed" : "passed"
882   };
883 };
884 
885 //noinspection JSUnusedLocalSymbols
886 jasmine.JsApiReporter.prototype.log = function(str) {
887 };
888 
889 jasmine.Matchers = function(env, actual, results) {
890   this.env = env;
891   this.actual = actual;
892   this.passing_message = 'Passed.';
893   this.results = results || new jasmine.NestedResults();
894 };
895 
896 jasmine.Matchers.pp = function(str) {
897   return jasmine.util.htmlEscape(jasmine.pp(str));
898 };
899 
900 jasmine.Matchers.prototype.getResults = function() {
901   return this.results;
902 };
903 
904 jasmine.Matchers.prototype.report = function(result, failing_message, details) {
905   this.results.addResult(new jasmine.ExpectationResult(result, result ? this.passing_message : failing_message, details));
906   return result;
907 };
908 
909 /**
910  * Matcher that compares the actual to the expected using ===.
911  *
912  * @param expected
913  */
914 jasmine.Matchers.prototype.toBe = function(expected) {
915   return this.report(this.actual === expected, 'Expected<br /><br />' + jasmine.Matchers.pp(expected)
916     + '<br /><br />to be the same object as<br /><br />' + jasmine.Matchers.pp(this.actual)
917     + '<br />');
918 };
919 
920 /**
921  * Matcher that compares the actual to the expected using !==
922  * @param expected
923  */
924 jasmine.Matchers.prototype.toNotBe = function(expected) {
925   return this.report(this.actual !== expected, 'Expected<br /><br />' + jasmine.Matchers.pp(expected)
926     + '<br /><br />to be a different object from actual, but they were the same.');
927 };
928 
929 /**
930  * Matcher that compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
931  *
932  * @param expected
933  */
934 jasmine.Matchers.prototype.toEqual = function(expected) {
935   var mismatchKeys = [];
936   var mismatchValues = [];
937 
938   var formatMismatches = function(name, array) {
939     if (array.length == 0) return '';
940     var errorOutput = '<br /><br />Different ' + name + ':<br />';
941     for (var i = 0; i < array.length; i++) {
942       errorOutput += array[i] + '<br />';
943     }
944     return errorOutput;
945   };
946 
947   return this.report(this.env.equals_(this.actual, expected, mismatchKeys, mismatchValues),
948     'Expected<br /><br />' + jasmine.Matchers.pp(expected)
949       + '<br /><br />but got<br /><br />' + jasmine.Matchers.pp(this.actual)
950       + '<br />'
951       + formatMismatches('Keys', mismatchKeys)
952       + formatMismatches('Values', mismatchValues), {
953     matcherName: 'toEqual', expected: expected, actual: this.actual
954   });
955 };
956 /** @deprecated */
957 jasmine.Matchers.prototype.should_equal = jasmine.Matchers.prototype.toEqual;
958 
959 /**
960  * Matcher that compares the actual to the expected using the ! of jasmine.Matchers.toEqual
961  * @param expected
962  */
963 jasmine.Matchers.prototype.toNotEqual = function(expected) {
964   return this.report(!this.env.equals_(this.actual, expected),
965     'Expected ' + jasmine.Matchers.pp(expected) + ' to not equal ' + jasmine.Matchers.pp(this.actual) + ', but it does.');
966 };
967 /** @deprecated */
968 jasmine.Matchers.prototype.should_not_equal = jasmine.Matchers.prototype.toNotEqual;
969 
970 /**
971  * Matcher that compares the actual to the expected using a regular expression.  Constructs a RegExp, so takes
972  * a pattern or a String.
973  *
974  * @param reg_exp
975  */
976 jasmine.Matchers.prototype.toMatch = function(reg_exp) {
977   return this.report((new RegExp(reg_exp).test(this.actual)),
978     'Expected ' + jasmine.Matchers.pp(this.actual) + ' to match ' + reg_exp + '.');
979 };
980 /** @deprecated */
981 jasmine.Matchers.prototype.should_match = jasmine.Matchers.prototype.toMatch;
982 
983 /**
984  * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
985  * @param reg_exp
986  */
987 jasmine.Matchers.prototype.toNotMatch = function(reg_exp) {
988   return this.report((!new RegExp(reg_exp).test(this.actual)),
989     'Expected ' + jasmine.Matchers.pp(this.actual) + ' to not match ' + reg_exp + '.');
990 };
991 /** @deprecated */
992 jasmine.Matchers.prototype.should_not_match = jasmine.Matchers.prototype.toNotMatch;
993 
994 /**
995  * Matcher that compares the acutal to undefined.
996  */
997 jasmine.Matchers.prototype.toBeDefined = function() {
998   return this.report((this.actual !== undefined),
999     'Expected a value to be defined but it was undefined.');
1000 };
1001 /** @deprecated */
1002 jasmine.Matchers.prototype.should_be_defined = jasmine.Matchers.prototype.toBeDefined;
1003 
1004 /**
1005  * Matcher that compares the actual to null.
1006  *
1007  */
1008 jasmine.Matchers.prototype.toBeNull = function() {
1009   return this.report((this.actual === null),
1010     'Expected a value to be null but it was ' + jasmine.Matchers.pp(this.actual) + '.');
1011 };
1012 /** @deprecated */
1013 jasmine.Matchers.prototype.should_be_null = jasmine.Matchers.prototype.toBeNull;
1014 
1015 /**
1016  * Matcher that boolean not-nots the actual.
1017  */
1018 jasmine.Matchers.prototype.toBeTruthy = function() {
1019   return this.report(!!this.actual,
1020     'Expected a value to be truthy but it was ' + jasmine.Matchers.pp(this.actual) + '.');
1021 };
1022 /** @deprecated */
1023 jasmine.Matchers.prototype.should_be_truthy = jasmine.Matchers.prototype.toBeTruthy;
1024 
1025 /**
1026  * Matcher that boolean nots the actual.
1027  */
1028 jasmine.Matchers.prototype.toBeFalsy = function() {
1029   return this.report(!this.actual,
1030     'Expected a value to be falsy but it was ' + jasmine.Matchers.pp(this.actual) + '.');
1031 };
1032 /** @deprecated */
1033 jasmine.Matchers.prototype.should_be_falsy = jasmine.Matchers.prototype.toBeFalsy;
1034 
1035 /**
1036  * Matcher that checks to see if the acutal, a Jasmine spy, was called.
1037  */
1038 jasmine.Matchers.prototype.wasCalled = function() {
1039   if (!this.actual || !this.actual.isSpy) {
1040     return this.report(false, 'Expected a spy, but got ' + jasmine.Matchers.pp(this.actual) + '.');
1041   }
1042   if (arguments.length > 0) {
1043     return this.report(false, 'wasCalled matcher does not take arguments');
1044   }
1045   return this.report((this.actual.wasCalled),
1046     'Expected spy "' + this.actual.identity + '" to have been called, but it was not.');
1047 };
1048 /** @deprecated */
1049 jasmine.Matchers.prototype.was_called = jasmine.Matchers.prototype.wasCalled;
1050 
1051 /**
1052  * Matcher that checks to see if the acutal, a Jasmine spy, was not called.
1053  */
1054 jasmine.Matchers.prototype.wasNotCalled = function() {
1055   if (!this.actual || !this.actual.isSpy) {
1056     return this.report(false, 'Expected a spy, but got ' + jasmine.Matchers.pp(this.actual) + '.');
1057   }
1058   return this.report((!this.actual.wasCalled),
1059     'Expected spy "' + this.actual.identity + '" to not have been called, but it was.');
1060 };
1061 /** @deprecated */
1062 jasmine.Matchers.prototype.was_not_called = jasmine.Matchers.prototype.wasNotCalled;
1063 
1064 /**
1065  * Matcher that checks to see if the acutal, a Jasmine spy, was called with a set of parameters.
1066  *
1067  * @example
1068  *
1069  */
1070 jasmine.Matchers.prototype.wasCalledWith = function() {
1071   if (!this.actual || !this.actual.isSpy) {
1072     return this.report(false, 'Expected a spy, but got ' + jasmine.Matchers.pp(this.actual) + '.', {
1073       matcherName: 'wasCalledWith'
1074     });
1075   }
1076 
1077   var args = jasmine.util.argsToArray(arguments);
1078 
1079   return this.report(this.env.contains_(this.actual.argsForCall, args),
1080     'Expected ' + jasmine.Matchers.pp(this.actual.argsForCall) + ' to contain ' + jasmine.Matchers.pp(args) + ', but it does not.', {
1081     matcherName: 'wasCalledWith', expected: args, actual: this.actual.argsForCall
1082   });
1083 };
1084 
1085 /**
1086  * Matcher that checks that the expected item is an element in the actual Array.
1087  *
1088  * @param {Object} item
1089  */
1090 jasmine.Matchers.prototype.toContain = function(item) {
1091   return this.report(this.env.contains_(this.actual, item),
1092     'Expected ' + jasmine.Matchers.pp(this.actual) + ' to contain ' + jasmine.Matchers.pp(item) + ', but it does not.', {
1093     matcherName: 'toContain', expected: item, actual: this.actual
1094   });
1095 };
1096 
1097 /**
1098  * Matcher that checks that the expected item is NOT an element in the actual Array.
1099  *
1100  * @param {Object} item
1101  */
1102 jasmine.Matchers.prototype.toNotContain = function(item) {
1103   return this.report(!this.env.contains_(this.actual, item),
1104     'Expected ' + jasmine.Matchers.pp(this.actual) + ' not to contain ' + jasmine.Matchers.pp(item) + ', but it does.');
1105 };
1106 
1107 jasmine.Matchers.prototype.toBeLessThan = function(expected) {
1108   return this.report(this.actual < expected,
1109     'Expected ' + jasmine.Matchers.pp(this.actual) + ' to be less than ' + jasmine.Matchers.pp(expected) + ', but it was not.');
1110 };
1111 
1112 jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {
1113   return this.report(this.actual > expected,
1114     'Expected ' + jasmine.Matchers.pp(this.actual) + ' to be greater than ' + jasmine.Matchers.pp(expected) + ', but it was not.');
1115 };
1116 
1117 /**
1118  * Matcher that checks that the expected exception was thrown by the actual.
1119  *
1120  * @param {String} expectedException
1121  */
1122 jasmine.Matchers.prototype.toThrow = function(expectedException) {
1123   var exception = null;
1124   try {
1125     this.actual();
1126   } catch (e) {
1127     exception = e;
1128   }
1129   if (expectedException !== undefined) {
1130     if (exception == null) {
1131       return this.report(false, "Expected function to throw " + jasmine.Matchers.pp(expectedException) + ", but it did not.");
1132     }
1133     return this.report(
1134       this.env.equals_(
1135         exception.message || exception,
1136         expectedException.message || expectedException),
1137       "Expected function to throw " + jasmine.Matchers.pp(expectedException) + ", but it threw " + jasmine.Matchers.pp(exception) + ".");
1138   } else {
1139     return this.report(exception != null, "Expected function to throw an exception, but it did not.");
1140   }
1141 };
1142 
1143 jasmine.Matchers.Any = function(expectedClass) {
1144   this.expectedClass = expectedClass;
1145 };
1146 
1147 jasmine.Matchers.Any.prototype.matches = function(other) {
1148   if (this.expectedClass == String) {
1149     return typeof other == 'string' || other instanceof String;
1150   }
1151 
1152   if (this.expectedClass == Number) {
1153     return typeof other == 'number' || other instanceof Number;
1154   }
1155 
1156   if (this.expectedClass == Function) {
1157     return typeof other == 'function' || other instanceof Function;
1158   }
1159 
1160   if (this.expectedClass == Object) {
1161     return typeof other == 'object';
1162   }
1163 
1164   return other instanceof this.expectedClass;
1165 };
1166 
1167 jasmine.Matchers.Any.prototype.toString = function() {
1168   return '<jasmine.any(' + this.expectedClass + ')>';
1169 };
1170 
1171 /**
1172  * @constructor
1173  */
1174 jasmine.MultiReporter = function() {
1175   this.subReporters_ = [];
1176 };
1177 jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);
1178 
1179 jasmine.MultiReporter.prototype.addReporter = function(reporter) {
1180   this.subReporters_.push(reporter);
1181 };
1182 
1183 (function() {
1184   var functionNames = ["reportRunnerStarting", "reportRunnerResults", "reportSuiteResults", "reportSpecResults", "log"];
1185   for (var i = 0; i < functionNames.length; i++) {
1186     var functionName = functionNames[i];
1187     jasmine.MultiReporter.prototype[functionName] = (function(functionName) {
1188       return function() {
1189         for (var j = 0; j < this.subReporters_.length; j++) {
1190           var subReporter = this.subReporters_[j];
1191           if (subReporter[functionName]) {
1192             subReporter[functionName].apply(subReporter, arguments);
1193           }
1194         }
1195       };
1196     })(functionName);
1197   }
1198 })();
1199 /**
1200  * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults
1201  *
1202  * @constructor
1203  */
1204 jasmine.NestedResults = function() {
1205   /**
1206    * The total count of results
1207    */
1208   this.totalCount = 0;
1209   /**
1210    * Number of passed results
1211    */
1212   this.passedCount = 0;
1213   /**
1214    * Number of failed results
1215    */
1216   this.failedCount = 0;
1217   /**
1218    * Was this suite/spec skipped?
1219    */
1220   this.skipped = false;
1221   /**
1222    * @ignore
1223    */
1224   this.items_ = [];
1225 };
1226 
1227 /**
1228  * Roll up the result counts.
1229  *
1230  * @param result
1231  */
1232 jasmine.NestedResults.prototype.rollupCounts = function(result) {
1233   this.totalCount += result.totalCount;
1234   this.passedCount += result.passedCount;
1235   this.failedCount += result.failedCount;
1236 };
1237 
1238 /**
1239  * Tracks a result's message.
1240  * @param message
1241  */
1242 jasmine.NestedResults.prototype.log = function(message) {
1243   this.items_.push(new jasmine.MessageResult(message));
1244 };
1245 
1246 /**
1247  * Getter for the results: message & results.
1248  */
1249 jasmine.NestedResults.prototype.getItems = function() {
1250   return this.items_;
1251 };
1252 
1253 /**
1254  * Adds a result, tracking counts (total, passed, & failed)
1255  * @param {jasmine.ExpectationResult|jasmine.NestedResults} result
1256  */
1257 jasmine.NestedResults.prototype.addResult = function(result) {
1258   if (result.type != 'MessageResult') {
1259     if (result.items_) {
1260       this.rollupCounts(result);
1261     } else {
1262       this.totalCount++;
1263       if (result.passed()) {
1264         this.passedCount++;
1265       } else {
1266         this.failedCount++;
1267       }
1268     }
1269   }
1270   this.items_.push(result);
1271 };
1272 
1273 /**
1274  * @returns {Boolean} True if <b>everything</b> below passed
1275  */
1276 jasmine.NestedResults.prototype.passed = function() {
1277   return this.passedCount === this.totalCount;
1278 };
1279 /**
1280  * Base class for pretty printing for expectation results.
1281  */
1282 jasmine.PrettyPrinter = function() {
1283   this.ppNestLevel_ = 0;
1284 };
1285 
1286 /**
1287  * Formats a value in a nice, human-readable string.
1288  *
1289  * @param value
1290  * @returns {String}
1291  */
1292 jasmine.PrettyPrinter.prototype.format = function(value) {
1293   if (this.ppNestLevel_ > 40) {
1294     //    return '(jasmine.pp nested too deeply!)';
1295     throw new Error('jasmine.PrettyPrinter: format() nested too deeply!');
1296   }
1297 
1298   this.ppNestLevel_++;
1299   try {
1300     if (value === undefined) {
1301       this.emitScalar('undefined');
1302     } else if (value === null) {
1303       this.emitScalar('null');
1304     } else if (value.navigator && value.frames && value.setTimeout) {
1305       this.emitScalar('<window>');
1306     } else if (value instanceof jasmine.Matchers.Any) {
1307       this.emitScalar(value.toString());
1308     } else if (typeof value === 'string') {
1309       this.emitString(value);
1310     } else if (typeof value === 'function') {
1311       this.emitScalar('Function');
1312     } else if (typeof value.nodeType === 'number') {
1313       this.emitScalar('HTMLNode');
1314     } else if (value instanceof Date) {
1315       this.emitScalar('Date(' + value + ')');
1316     } else if (value.__Jasmine_been_here_before__) {
1317       this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>');
1318     } else if (jasmine.isArray_(value) || typeof value == 'object') {
1319       value.__Jasmine_been_here_before__ = true;
1320       if (jasmine.isArray_(value)) {
1321         this.emitArray(value);
1322       } else {
1323         this.emitObject(value);
1324       }
1325       delete value.__Jasmine_been_here_before__;
1326     } else {
1327       this.emitScalar(value.toString());
1328     }
1329   } finally {
1330     this.ppNestLevel_--;
1331   }
1332 };
1333 
1334 jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {
1335   for (var property in obj) {
1336     if (property == '__Jasmine_been_here_before__') continue;
1337     fn(property, obj.__lookupGetter__(property) != null);
1338   }
1339 };
1340 
1341 jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;
1342 jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;
1343 jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;
1344 jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;
1345 
1346 jasmine.StringPrettyPrinter = function() {
1347   jasmine.PrettyPrinter.call(this);
1348 
1349   this.string = '';
1350 };
1351 jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);
1352 
1353 jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {
1354   this.append(value);
1355 };
1356 
1357 jasmine.StringPrettyPrinter.prototype.emitString = function(value) {
1358   this.append("'" + value + "'");
1359 };
1360 
1361 jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {
1362   this.append('[ ');
1363   for (var i = 0; i < array.length; i++) {
1364     if (i > 0) {
1365       this.append(', ');
1366     }
1367     this.format(array[i]);
1368   }
1369   this.append(' ]');
1370 };
1371 
1372 jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {
1373   var self = this;
1374   this.append('{ ');
1375   var first = true;
1376 
1377   this.iterateObject(obj, function(property, isGetter) {
1378     if (first) {
1379       first = false;
1380     } else {
1381       self.append(', ');
1382     }
1383 
1384     self.append(property);
1385     self.append(' : ');
1386     if (isGetter) {
1387       self.append('<getter>');
1388     } else {
1389       self.format(obj[property]);
1390     }
1391   });
1392 
1393   this.append(' }');
1394 };
1395 
1396 jasmine.StringPrettyPrinter.prototype.append = function(value) {
1397   this.string += value;
1398 };
1399 jasmine.Queue = function(env) {
1400   this.env = env;
1401   this.blocks = [];
1402   this.running = false;
1403   this.index = 0;
1404   this.offset = 0;
1405 };
1406 
1407 jasmine.Queue.prototype.addBefore = function (block) {
1408   this.blocks.unshift(block);
1409 };
1410 
1411 jasmine.Queue.prototype.add = function(block) {
1412   this.blocks.push(block);
1413 };
1414 
1415 jasmine.Queue.prototype.insertNext = function (block) {
1416   this.blocks.splice((this.index + this.offset + 1), 0, block);
1417   this.offset++;
1418 };
1419 
1420 jasmine.Queue.prototype.start = function(onComplete) {
1421   var self = this;
1422   self.running = true;
1423   self.onComplete = onComplete;
1424   if (self.blocks[0]) {
1425     self.blocks[0].execute(function () {
1426       self._next();
1427     });
1428   } else {
1429     self.finish();
1430   }
1431 };
1432 
1433 jasmine.Queue.prototype.isRunning = function () {
1434   return this.running;
1435 };
1436 
1437 jasmine.Queue.prototype._next = function () {
1438   var self = this;
1439   var doNext = function () {
1440     self.offset = 0;
1441     self.index++;
1442     if (self.index < self.blocks.length) {
1443       self.blocks[self.index].execute(function () {
1444         self._next();
1445       });
1446     } else {
1447       self.finish();
1448     }
1449   };
1450   var now = new Date().getTime();
1451   if (this.env.updateInterval && now - this.env.lastUpdate > this.env.updateInterval) {
1452     this.env.lastUpdate = now;
1453     this.env.setTimeout(doNext, 0);
1454   } else {
1455     doNext();
1456   }
1457 
1458 };
1459 
1460 jasmine.Queue.prototype.finish = function () {
1461   this.running = false;
1462   if (this.onComplete) {
1463     this.onComplete();
1464   }
1465 };
1466 
1467 jasmine.Queue.prototype.getResults = function () {
1468   var results = new jasmine.NestedResults();
1469   for (var i = 0; i < this.blocks.length; i++) {
1470     if (this.blocks[i].getResults) {
1471       results.addResult(this.blocks[i].getResults());
1472     }
1473   }
1474   return results;
1475 };
1476 
1477 
1478 /* JasmineReporters.reporter
1479  *    Base object that will get called whenever a Spec, Suite, or Runner is done.  It is up to
1480  *    descendants of this object to do something with the results (see json_reporter.js)
1481  */
1482 jasmine.Reporters = {};
1483 
1484 jasmine.Reporters.reporter = function(callbacks) {
1485   var that = {
1486     callbacks: callbacks || {},
1487 
1488     doCallback: function(callback, results) {
1489       if (callback) {
1490         callback(results);
1491       }
1492     },
1493 
1494     reportRunnerResults: function(runner) {
1495       that.doCallback(that.callbacks.runnerCallback, runner);
1496     },
1497     reportSuiteResults:  function(suite) {
1498       that.doCallback(that.callbacks.suiteCallback, suite);
1499     },
1500     reportSpecResults:   function(spec) {
1501       that.doCallback(that.callbacks.specCallback, spec);
1502     },
1503     log: function (str) {
1504       if (console && console.log) console.log(str);
1505     }
1506   };
1507 
1508   return that;
1509 };
1510 
1511 /**
1512  * Runner
1513  *
1514  * @constructor
1515  * @param {jasmine.Env} env
1516  */
1517 jasmine.Runner = function(env) {
1518   var self = this;
1519   self.env = env;
1520   self.queue = new jasmine.Queue(env);
1521   self.suites_ = [];
1522 };
1523 
1524 jasmine.Runner.prototype.execute = function() {
1525   var self = this;
1526   if (self.env.reporter.reportRunnerStarting) {
1527     self.env.reporter.reportRunnerStarting(this);
1528   }
1529   self.queue.start(function () {
1530     self.finishCallback();
1531   });
1532 };
1533 
1534 jasmine.Runner.prototype.finishCallback = function() {
1535   this.env.reporter.reportRunnerResults(this);
1536 };
1537 
1538 jasmine.Runner.prototype.addSuite = function(suite) {
1539   this.suites_.push(suite);
1540 };
1541 
1542 jasmine.Runner.prototype.add = function(block) {
1543   if (block instanceof jasmine.Suite) {
1544     this.addSuite(block);
1545   }
1546   this.queue.add(block);
1547 };
1548 
1549 /** @deprecated */
1550 jasmine.Runner.prototype.getAllSuites = function() {
1551   return this.suites_;
1552 };
1553 
1554 
1555 jasmine.Runner.prototype.suites = function() {
1556   return this.suites_;
1557 };
1558 
1559 jasmine.Runner.prototype.getResults = function() {
1560   return this.queue.getResults();
1561 };
1562 /**
1563  * Internal representation of a Jasmine specification, or test.
1564  *
1565  * @constructor
1566  * @param {jasmine.Env} env
1567  * @param {jasmine.Suite} suite
1568  * @param {String} description
1569  */
1570 jasmine.Spec = function(env, suite, description) {
1571   var spec = this;
1572   spec.id = env.nextSpecId_++;
1573   spec.env = env;
1574   spec.suite = suite;
1575   spec.description = description;
1576   spec.queue = new jasmine.Queue(env);
1577 
1578   spec.afterCallbacks = [];
1579   spec.spies_ = [];
1580 
1581   spec.results = new jasmine.NestedResults();
1582   spec.results.description = description;
1583   spec.matchersClass = null;
1584 };
1585 
1586 jasmine.Spec.prototype.getFullName = function() {
1587   return this.suite.getFullName() + ' ' + this.description + '.';
1588 };
1589 
1590 jasmine.Spec.prototype.getResults = function() {
1591   return this.results;
1592 };
1593 
1594 jasmine.Spec.prototype.runs = function (func) {
1595   var block = new jasmine.Block(this.env, func, this);
1596   this.addToQueue(block);
1597   return this;
1598 };
1599 
1600 jasmine.Spec.prototype.addToQueue = function (block) {
1601   if (this.queue.isRunning()) {
1602     this.queue.insertNext(block);
1603   } else {
1604     this.queue.add(block);
1605   }
1606 };
1607 
1608 /**
1609  * @private
1610  * @deprecated
1611  */
1612 jasmine.Spec.prototype.expects_that = function(actual) {
1613   return this.expect(actual);
1614 };
1615 
1616 /**
1617  * @private
1618  */
1619 jasmine.Spec.prototype.expect = function(actual) {
1620   return new (this.getMatchersClass_())(this.env, actual, this.results);
1621 };
1622 
1623 jasmine.Spec.prototype.waits = function(timeout) {
1624   var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
1625   this.addToQueue(waitsFunc);
1626   return this;
1627 };
1628 
1629 jasmine.Spec.prototype.waitsFor = function(timeout, latchFunction, timeoutMessage) {
1630   var waitsForFunc = new jasmine.WaitsForBlock(this.env, timeout, latchFunction, timeoutMessage, this);
1631   this.addToQueue(waitsForFunc);
1632   return this;
1633 };
1634 
1635 jasmine.Spec.prototype.failWithException = function (e) {
1636   this.results.addResult(new jasmine.ExpectationResult(false, jasmine.util.formatException(e), null));
1637 };
1638 
1639 jasmine.Spec.prototype.getMatchersClass_ = function() {
1640   return this.matchersClass || jasmine.Matchers;
1641 };
1642 
1643 jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
1644   var parent = this.getMatchersClass_();
1645   var newMatchersClass = function() {
1646     parent.apply(this, arguments);
1647   };
1648   jasmine.util.inherit(newMatchersClass, parent);
1649   for (var method in matchersPrototype) {
1650     newMatchersClass.prototype[method] = matchersPrototype[method];
1651   }
1652   this.matchersClass = newMatchersClass;
1653 };
1654 
1655 jasmine.Spec.prototype.finishCallback = function() {
1656   this.env.reporter.reportSpecResults(this);
1657 };
1658 
1659 jasmine.Spec.prototype.finish = function(onComplete) {
1660   this.removeAllSpies();
1661   this.finishCallback();
1662   if (onComplete) {
1663     onComplete();
1664   }
1665 };
1666 
1667 jasmine.Spec.prototype.after = function(doAfter, test) {
1668 
1669  if (this.queue.isRunning()) {
1670     this.queue.add(new jasmine.Block(this.env, doAfter, this));
1671   } else {
1672   this.afterCallbacks.unshift(doAfter);
1673   }
1674 };
1675 
1676 jasmine.Spec.prototype.execute = function(onComplete) {
1677   var spec = this;
1678   if (!spec.env.specFilter(spec)) {
1679     spec.results.skipped = true;
1680     spec.finish(onComplete);
1681     return;
1682   }
1683   this.env.reporter.log('>> Jasmine Running ' + this.suite.description + ' ' + this.description + '...');
1684 
1685   spec.env.currentSpec = spec;
1686   spec.env.currentlyRunningTests = true;
1687 
1688   spec.addBeforesAndAftersToQueue();
1689 
1690   spec.queue.start(function () {
1691     spec.finish(onComplete);
1692   });
1693   spec.env.currentlyRunningTests = false;
1694 };
1695 
1696 jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
1697   for (var suite = this.suite; suite; suite = suite.parentSuite) {
1698     if (suite.beforeQueue) {
1699       for (var i = 0; i < suite.beforeQueue.length; i++)
1700         this.queue.addBefore(new jasmine.Block(this.env, suite.beforeQueue[i], this));
1701     }
1702   }
1703   for (i = 0; i < this.afterCallbacks.length; i++) {
1704     this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this));
1705   }
1706   for (suite = this.suite; suite; suite = suite.parentSuite) {
1707   if (suite.afterQueue) {
1708       for (var j = 0; j < suite.afterQueue.length; j++)
1709         this.queue.add(new jasmine.Block(this.env, suite.afterQueue[j], this));
1710     }
1711   }
1712 };
1713 
1714 jasmine.Spec.prototype.explodes = function() {
1715   throw 'explodes function should not have been called';
1716 };
1717 
1718 jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
1719   if (obj == undefined) {
1720     throw "spyOn could not find an object to spy upon for " + methodName + "()";
1721   }
1722 
1723   if (!ignoreMethodDoesntExist && obj[methodName] === undefined) {
1724     throw methodName + '() method does not exist';
1725   }
1726 
1727   if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
1728     throw new Error(methodName + ' has already been spied upon');
1729   }
1730 
1731   var spyObj = jasmine.createSpy(methodName);
1732 
1733   this.spies_.push(spyObj);
1734   spyObj.baseObj = obj;
1735   spyObj.methodName = methodName;
1736   spyObj.originalValue = obj[methodName];
1737 
1738   obj[methodName] = spyObj;
1739 
1740   return spyObj;
1741 };
1742 
1743 jasmine.Spec.prototype.removeAllSpies = function() {
1744   for (var i = 0; i < this.spies_.length; i++) {
1745     var spy = this.spies_[i];
1746     spy.baseObj[spy.methodName] = spy.originalValue;
1747   }
1748   this.spies_ = [];
1749 };
1750 
1751 /**
1752  * Internal representation of a Jasmine suite.
1753  *
1754  * @constructor
1755  * @param {jasmine.Env} env
1756  * @param {String} description
1757  * @param {Function} specDefinitions
1758  * @param {jasmine.Suite} parentSuite
1759  */
1760 jasmine.Suite = function(env, description, specDefinitions, parentSuite) {
1761   var self = this;
1762   self.id = env.nextSuiteId_++;
1763   self.description = description;
1764   self.queue = new jasmine.Queue(env);
1765   self.parentSuite = parentSuite;
1766   self.env = env;
1767   self.beforeQueue = [];
1768   self.afterQueue = [];
1769   self.specs_ = [];
1770 };
1771 
1772 jasmine.Suite.prototype.getFullName = function() {
1773   var fullName = this.description;
1774   for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
1775     fullName = parentSuite.description + ' ' + fullName;
1776   }
1777   return fullName;
1778 };
1779 
1780 jasmine.Suite.prototype.finish = function(onComplete) {
1781   this.env.reporter.reportSuiteResults(this);
1782   this.finished = true;
1783   if (typeof(onComplete) == 'function') {
1784     onComplete();
1785   }
1786 };
1787 
1788 jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {
1789   beforeEachFunction.typeName = 'beforeEach';
1790   this.beforeQueue.push(beforeEachFunction);
1791 };
1792 
1793 jasmine.Suite.prototype.afterEach = function(afterEachFunction) {
1794   afterEachFunction.typeName = 'afterEach';
1795   this.afterQueue.push(afterEachFunction);
1796 };
1797 
1798 jasmine.Suite.prototype.getResults = function() {
1799   return this.queue.getResults();
1800 };
1801 
1802 jasmine.Suite.prototype.add = function(block) {
1803   if (block instanceof jasmine.Suite) {
1804     this.env.currentRunner.addSuite(block);
1805   } else {
1806     this.specs_.push(block);
1807   }
1808   this.queue.add(block);
1809 };
1810 
1811 /** @deprecated */
1812 jasmine.Suite.prototype.specCount = function() {
1813   return this.specs_.length;
1814 };
1815 
1816 jasmine.Suite.prototype.specs = function() {
1817   return this.specs_;
1818 };
1819 
1820 jasmine.Suite.prototype.execute = function(onComplete) {
1821   var self = this;
1822   this.queue.start(function () {
1823     self.finish(onComplete);
1824   });
1825 };
1826 jasmine.WaitsBlock = function(env, timeout, spec) {
1827   this.timeout = timeout;
1828   jasmine.Block.call(this, env, null, spec);
1829 };
1830 
1831 jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
1832 
1833 jasmine.WaitsBlock.prototype.execute = function (onComplete) {
1834   this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
1835   this.env.setTimeout(function () {
1836     onComplete();
1837   }, this.timeout);
1838 };
1839 jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
1840   this.timeout = timeout;
1841   this.latchFunction = latchFunction;
1842   this.message = message;
1843   this.totalTimeSpentWaitingForLatch = 0;
1844   jasmine.Block.call(this, env, null, spec);
1845 };
1846 
1847 jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
1848 
1849 jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 100;
1850 
1851 jasmine.WaitsForBlock.prototype.execute = function (onComplete) {
1852   var self = this;
1853   self.env.reporter.log('>> Jasmine waiting for ' + (self.message || 'something to happen'));
1854   var latchFunctionResult;
1855   try {
1856     latchFunctionResult = self.latchFunction.apply(self.spec);
1857   } catch (e) {
1858     self.fail(e);
1859     onComplete();
1860     return;
1861   }
1862 
1863   if (latchFunctionResult) {
1864     onComplete();
1865   } else if (self.totalTimeSpentWaitingForLatch >= self.timeout) {
1866     var message = 'timed out after ' + self.timeout + ' msec waiting for ' + (self.message || 'something to happen');
1867     self.fail({
1868       name: 'timeout',
1869       message: message
1870     });
1871     self.spec._next();
1872   } else {
1873     self.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
1874     self.env.setTimeout(function () { self.execute(onComplete); }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
1875   }
1876 };
1877 // Mock setTimeout, clearTimeout
1878 // Contributed by Pivotal Computer Systems, www.pivotalsf.com
1879 
1880 jasmine.FakeTimer = function() {
1881   this.reset();
1882 
1883   var self = this;
1884   self.setTimeout = function(funcToCall, millis) {
1885     self.timeoutsMade++;
1886     self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);
1887     return self.timeoutsMade;
1888   };
1889 
1890   self.setInterval = function(funcToCall, millis) {
1891     self.timeoutsMade++;
1892     self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);
1893     return self.timeoutsMade;
1894   };
1895 
1896   self.clearTimeout = function(timeoutKey) {
1897     self.scheduledFunctions[timeoutKey] = undefined;
1898   };
1899 
1900   self.clearInterval = function(timeoutKey) {
1901     self.scheduledFunctions[timeoutKey] = undefined;
1902   };
1903 
1904 };
1905 
1906 jasmine.FakeTimer.prototype.reset = function() {
1907   this.timeoutsMade = 0;
1908   this.scheduledFunctions = {};
1909   this.nowMillis = 0;
1910 };
1911 
1912 jasmine.FakeTimer.prototype.tick = function(millis) {
1913   var oldMillis = this.nowMillis;
1914   var newMillis = oldMillis + millis;
1915   this.runFunctionsWithinRange(oldMillis, newMillis);
1916   this.nowMillis = newMillis;
1917 };
1918 
1919 jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {
1920   var scheduledFunc;
1921   var funcsToRun = [];
1922   for (var timeoutKey in this.scheduledFunctions) {
1923     scheduledFunc = this.scheduledFunctions[timeoutKey];
1924     if (scheduledFunc != undefined &&
1925         scheduledFunc.runAtMillis >= oldMillis &&
1926         scheduledFunc.runAtMillis <= nowMillis) {
1927       funcsToRun.push(scheduledFunc);
1928       this.scheduledFunctions[timeoutKey] = undefined;
1929     }
1930   }
1931 
1932   if (funcsToRun.length > 0) {
1933     funcsToRun.sort(function(a, b) {
1934       return a.runAtMillis - b.runAtMillis;
1935     });
1936     for (var i = 0; i < funcsToRun.length; ++i) {
1937       try {
1938         var funcToRun = funcsToRun[i];
1939         this.nowMillis = funcToRun.runAtMillis;
1940         funcToRun.funcToCall();
1941         if (funcToRun.recurring) {
1942           this.scheduleFunction(funcToRun.timeoutKey,
1943             funcToRun.funcToCall,
1944             funcToRun.millis,
1945             true);
1946         }
1947       } catch(e) {
1948       }
1949     }
1950     this.runFunctionsWithinRange(oldMillis, nowMillis);
1951   }
1952 };
1953 
1954 jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {
1955   this.scheduledFunctions[timeoutKey] = {
1956     runAtMillis: this.nowMillis + millis,
1957     funcToCall: funcToCall,
1958     recurring: recurring,
1959     timeoutKey: timeoutKey,
1960     millis: millis
1961   };
1962 };
1963 
1964 
1965 jasmine.Clock = {
1966   defaultFakeTimer: new jasmine.FakeTimer(),
1967 
1968   reset: function() {
1969     jasmine.Clock.assertInstalled();
1970     jasmine.Clock.defaultFakeTimer.reset();
1971   },
1972 
1973   tick: function(millis) {
1974     jasmine.Clock.assertInstalled();
1975     jasmine.Clock.defaultFakeTimer.tick(millis);
1976   },
1977 
1978   runFunctionsWithinRange: function(oldMillis, nowMillis) {
1979     jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);
1980   },
1981 
1982   scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
1983     jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);
1984   },
1985 
1986   useMock: function() {
1987     var spec = jasmine.getEnv().currentSpec;
1988     spec.after(jasmine.Clock.uninstallMock);
1989 
1990     jasmine.Clock.installMock();
1991   },
1992 
1993   installMock: function() {
1994     jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;
1995   },
1996 
1997   uninstallMock: function() {
1998     jasmine.Clock.assertInstalled();
1999     jasmine.Clock.installed = jasmine.Clock.real;
2000   },
2001 
2002   real: {
2003     setTimeout: window.setTimeout,
2004     clearTimeout: window.clearTimeout,
2005     setInterval: window.setInterval,
2006     clearInterval: window.clearInterval
2007   },
2008 
2009   assertInstalled: function() {
2010     if (jasmine.Clock.installed != jasmine.Clock.defaultFakeTimer) {
2011       throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
2012     }
2013   },  
2014 
2015   installed: null
2016 };
2017 jasmine.Clock.installed = jasmine.Clock.real;
2018 
2019 window.setTimeout = function(funcToCall, millis) {
2020   return jasmine.Clock.installed.setTimeout.apply(this, arguments);
2021 };
2022 
2023 window.setInterval = function(funcToCall, millis) {
2024   return jasmine.Clock.installed.setInterval.apply(this, arguments);
2025 };
2026 
2027 window.clearTimeout = function(timeoutKey) {
2028   return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
2029 };
2030 
2031 window.clearInterval = function(timeoutKey) {
2032   return jasmine.Clock.installed.clearInterval.apply(this, arguments);
2033 };
2034 
2035