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