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