1 /**
  2  * Base class for pretty printing for expectation results.
  3  */
  4 jasmine.PrettyPrinter = function() {
  5   this.ppNestLevel_ = 0;
  6 };
  7 
  8 /**
  9  * Formats a value in a nice, human-readable string.
 10  *
 11  * @param value
 12  */
 13 jasmine.PrettyPrinter.prototype.format = function(value) {
 14   if (this.ppNestLevel_ > 40) {
 15     throw new Error('jasmine.PrettyPrinter: format() nested too deeply!');
 16   }
 17 
 18   this.ppNestLevel_++;
 19   try {
 20     if (value === undefined) {
 21       this.emitScalar('undefined');
 22     } else if (value === null) {
 23       this.emitScalar('null');
 24     } else if (value.navigator /* && value.frames */ && value.setTimeout) {
 25       this.emitScalar('<window>');
 26     } else if (value instanceof jasmine.Matchers.Any) {
 27       this.emitScalar(value.toString());
 28     } else if (typeof value === 'string') {
 29       this.emitString(value);
 30     } else if (jasmine.isSpy(value)) {
 31       this.emitScalar("spy on " + value.identity);
 32     } else if (typeof value === 'function') {
 33       this.emitScalar('Function');
 34     } else if (typeof value.nodeType === 'number') {
 35       this.emitScalar('HTMLNode');
 36     } else if (value instanceof Date) {
 37       this.emitScalar('Date(' + value + ')');
 38     } else if (value instanceof RegExp) {
 39       this.emitScalar(value.toString());
 40     } else if (value.__Jasmine_been_here_before__) {
 41       this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>');
 42     } else if (jasmine.isArray_(value) || typeof value == 'object') {
 43       value.__Jasmine_been_here_before__ = true;
 44       if (jasmine.isArray_(value)) {
 45         this.emitArray(value);
 46       } else {
 47         this.emitObject(value);
 48       }
 49       delete value.__Jasmine_been_here_before__;
 50     } else {
 51       this.emitScalar(value.toString());
 52     }
 53   } finally {
 54     this.ppNestLevel_--;
 55   }
 56 };
 57 
 58 jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {
 59   for (var property in obj) {
 60     if (property == '__Jasmine_been_here_before__') continue;
 61     fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) != null) : false);
 62   }
 63 };
 64 
 65 jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;
 66 jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;
 67 jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;
 68 jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;
 69 
 70 jasmine.StringPrettyPrinter = function() {
 71   jasmine.PrettyPrinter.call(this);
 72 
 73   this.string = '';
 74 };
 75 jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);
 76 
 77 jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {
 78   this.append(value);
 79 };
 80 
 81 jasmine.StringPrettyPrinter.prototype.emitString = function(value) {
 82   this.append("'" + value + "'");
 83 };
 84 
 85 jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {
 86   this.append('[ ');
 87   for (var i = 0; i < array.length; i++) {
 88     if (i > 0) {
 89       this.append(', ');
 90     }
 91     this.format(array[i]);
 92   }
 93   this.append(' ]');
 94 };
 95 
 96 jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {
 97   var self = this;
 98   this.append('{ ');
 99   var first = true;
100 
101   this.iterateObject(obj, function(property, isGetter) {
102     if (first) {
103       first = false;
104     } else {
105       self.append(', ');
106     }
107 
108     self.append(property);
109     self.append(' : ');
110     if (isGetter) {
111       self.append('<getter>');
112     } else {
113       self.format(obj[property]);
114     }
115   });
116 
117   this.append(' }');
118 };
119 
120 jasmine.StringPrettyPrinter.prototype.append = function(value) {
121   this.string += value;
122 };
123