1 // ========================================================================== 2 // Project: SproutCore Costello - Property Observing Library 3 // Copyright: ©2006-2011 Strobe Inc. and contributors. 4 // Portions ©2008-2011 Apple Inc. All rights reserved. 5 // License: Licensed under MIT license (see license.js) 6 // ========================================================================== 7 8 /*globals CoreTest module */ 9 10 /** 11 * jsDump 12 * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com 13 * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php) 14 * Date: 5/15/2008 15 * @projectDescription Advanced and extensible data dumping for Javascript. 16 * @version 1.0.0 17 * @author Ariel Flesler 18 * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} 19 */ 20 (function(){ 21 var reName, jsDump; 22 23 function quote( str ){ 24 return '"' + str.toString().replace(/"/g, '\\"') + '"'; 25 } 26 27 function literal( o ){ 28 return o + ''; 29 } 30 31 function join( pre, arr, post ){ 32 var s = jsDump.separator(), 33 base = jsDump.indent(), 34 inner = jsDump.indent(1); 35 36 if( arr.join ) arr = arr.join( ',' + s + inner ); 37 if( !arr ) return pre + post; 38 39 return [ pre, inner + arr, base + post ].join(s); 40 } 41 42 function array( arr ){ 43 var i = arr.length, ret = new Array(i); 44 this.up(); 45 while( i-- ) ret[i] = this._parse( arr[i] ); 46 this.down(); 47 return join( '[', ret, ']' ); 48 } 49 50 reName = /^function (\w+)/; 51 52 jsDump = CoreTest.jsDump = { 53 54 parse: function(obj, type) { 55 if (obj && obj.toString) { 56 var toString = obj.toString; 57 if ((toString !== Object.prototype.toString) && (toString !== Array.toString)) return obj.toString(); 58 } 59 if (obj && obj.inspect) return obj.inspect(); 60 61 this.seen = []; 62 var ret = this._parse(obj, type); 63 this.seen = null; 64 return ret ; 65 }, 66 67 //type is used mostly internally, you can fix a (custom)type in advance 68 _parse: function( obj, type ) { 69 70 71 var parser = this.parsers[ type || this.typeOf(obj) ]; 72 type = typeof parser; 73 74 // avoid recursive loops 75 if ((parser === this.parsers.object) && (this.seen.indexOf(obj)>=0)) { 76 return '(recursive)'; 77 } 78 this.seen.push(obj); 79 80 return type == 'function' ? parser.call( this, obj ) : 81 type == 'string' ? parser : 82 this.parsers.error; 83 }, 84 typeOf:function( obj ){ 85 var type = typeof obj, 86 f = 'function';//we'll use it 3 times, save it 87 88 if (obj && (obj.isObject || obj.isClass)) return 'scobj'; 89 return type != 'object' && type != f ? type : 90 !obj ? 'null' : 91 obj.exec ? 'regexp' :// some browsers (FF) consider regexps functions 92 obj.getHours ? 'date' : 93 obj.scrollBy ? 'window' : 94 obj.nodeName == '#document' ? 'document' : 95 obj.nodeName ? 'node' : 96 obj.item ? 'nodelist' : // Safari reports nodelists as functions 97 obj.callee ? 'arguments' : 98 obj.call || obj.constructor != Array && //an array would also fall on this hack 99 (obj+'').indexOf(f) != -1 ? f : //IE reports functions like alert, as objects 100 'length' in obj ? 'array' : 101 type; 102 }, 103 separator:function(){ 104 return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? ' ' : ' '; 105 }, 106 indent:function( extra ){// extra can be a number, shortcut for increasing-calling-decreasing 107 if( !this.multiline ) return ''; 108 109 var chr = this.indentChar; 110 if( this.HTML ) chr = chr.replace(/\t/g,' ').replace(/ /g,' '); 111 return (new Array( this._depth_ + (extra||0) )).join(chr); 112 }, 113 up:function( a ){ 114 this._depth_ += a || 1; 115 }, 116 down:function( a ){ 117 this._depth_ -= a || 1; 118 }, 119 setParser:function( name, parser ){ 120 this.parsers[name] = parser; 121 }, 122 // The next 3 are exposed so you can use them 123 quote:quote, 124 literal:literal, 125 join:join, 126 // 127 _depth_: 1, 128 // This is the list of parsers, to modify them, use jsDump.setParser 129 parsers:{ 130 window: '[Window]', 131 document: '[Document]', 132 error:'[ERROR]', //when no parser is found, shouldn't happen 133 unknown: '[Unknown]', 134 'null':'null', 135 'undefined':'undefined', 136 'function':function( fn ){ 137 var ret = 'function', 138 name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE 139 if( name ) ret += ' ' + name; 140 ret += '('; 141 142 ret = [ ret, this._parse( fn, 'functionArgs' ), '){'].join(''); 143 return join( ret, this._parse(fn,'functionCode'), '}' ); 144 }, 145 array: array, 146 nodelist: array, 147 'arguments': array, 148 scobj: function(obj) { return obj.toString(); }, 149 object:function( map ){ 150 151 var ret = [ ]; 152 this.up(); 153 for( var key in map ) { 154 ret.push( this._parse(key,'key') + ': ' + this._parse(map[key]) ); 155 } 156 this.down(); 157 return join( '{', ret, '}' ); 158 }, 159 node:function( node ){ 160 var open = this.HTML ? '<' : '<', 161 close = this.HTML ? '>' : '>'; 162 163 var tag = node.nodeName.toLowerCase(), 164 ret = open + tag; 165 166 for( var a in this.DOMAttrs ){ 167 var val = node[this.DOMAttrs[a]]; 168 if( val ) { 169 ret += ' ' + a + '=' + this._parse( val, 'attribute' ); 170 } 171 } 172 return ret + close + open + '/' + tag + close; 173 }, 174 functionArgs:function( fn ){//function calls it internally, it's the arguments part of the function 175 var l = fn.length; 176 if( !l ) return ''; 177 178 var args = new Array(l); 179 while( l-- ) args[l] = String.fromCharCode(97+l);//97 is 'a' 180 return ' ' + args.join(', ') + ' '; 181 }, 182 key:quote, //object calls it internally, the key part of an item in a map 183 functionCode:'[code]', //function calls it internally, it's the content of the function 184 attribute:quote, //node calls it internally, it's an html attribute value 185 string:quote, 186 date:quote, 187 regexp:literal, //regex 188 number:literal, 189 'boolean':literal 190 }, 191 DOMAttrs:{//attributes to dump from nodes, name=>realName 192 id:'id', 193 name:'name', 194 'class':'className' 195 }, 196 HTML:true,//if true, entities are escaped ( <, >, \t, space and \n ) 197 indentChar:' ',//indentation unit 198 multiline:true //if true, items in a collection, are separated by a \n, else just a space. 199 }; 200 201 CoreTest.dump = function dump(obj,type) { 202 return CoreTest.jsDump.parse(obj, type); 203 }; 204 205 })(); 206