// ========================================================================== // Project: SproutCore Costello - Property Observing Library // Copyright: ©2006-2011 Strobe Inc. and contributors. // Portions ©2008-2010 Apple Inc. All rights reserved. // License: Licensed under MIT license (see license.js) // ========================================================================== /*globals CoreTest module */ /** * jsDump * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php) * Date: 5/15/2008 * @projectDescription Advanced and extensible data dumping for Javascript. * @version 1.0.0 * @author Ariel Flesler * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} */ (function(){ var reName, jsDump; function quote( str ){ return '"' + str.toString().replace(/"/g, '\\"') + '"'; } function literal( o ){ return o + ''; } function join( pre, arr, post ){ var s = jsDump.separator(), base = jsDump.indent(), inner = jsDump.indent(1); if( arr.join ) arr = arr.join( ',' + s + inner ); if( !arr ) return pre + post; return [ pre, inner + arr, base + post ].join(s); } function array( arr ){ var i = arr.length, ret = new Array(i); this.up(); while( i-- ) ret[i] = this._parse( arr[i] ); this.down(); return join( '[', ret, ']' ); } reName = /^function (\w+)/; jsDump = CoreTest.jsDump = { parse: function(obj, type) { if (obj && obj.toString) { var toString = obj.toString; if ((toString !== Object.prototype.toString) && (toString !== Array.toString)) return obj.toString(); } if (obj && obj.inspect) return obj.inspect(); this.seen = []; var ret = this._parse(obj, type); this.seen = null; return ret ; }, //type is used mostly internally, you can fix a (custom)type in advance _parse: function( obj, type ) { var parser = this.parsers[ type || this.typeOf(obj) ]; type = typeof parser; // avoid recursive loops if ((parser === this.parsers.object) && (this.seen.indexOf(obj)>=0)) { return '(recursive)'; } this.seen.push(obj); return type == 'function' ? parser.call( this, obj ) : type == 'string' ? parser : this.parsers.error; }, typeOf:function( obj ){ var type = typeof obj, f = 'function';//we'll use it 3 times, save it if (obj && (obj.isObject || obj.isClass)) return 'scobj'; return type != 'object' && type != f ? type : !obj ? 'null' : obj.exec ? 'regexp' :// some browsers (FF) consider regexps functions obj.getHours ? 'date' : obj.scrollBy ? 'window' : obj.nodeName == '#document' ? 'document' : obj.nodeName ? 'node' : obj.item ? 'nodelist' : // Safari reports nodelists as functions obj.callee ? 'arguments' : obj.call || obj.constructor != Array && //an array would also fall on this hack (obj+'').indexOf(f) != -1 ? f : //IE reports functions like alert, as objects 'length' in obj ? 'array' : type; }, separator:function(){ return this.multiline ? this.HTML ? '
' : '\n' : this.HTML ? ' ' : ' '; }, indent:function( extra ){// extra can be a number, shortcut for increasing-calling-decreasing if( !this.multiline ) return ''; var chr = this.indentChar; if( this.HTML ) chr = chr.replace(/\t/g,' ').replace(/ /g,' '); return (new Array( this._depth_ + (extra||0) )).join(chr); }, up:function( a ){ this._depth_ += a || 1; }, down:function( a ){ this._depth_ -= a || 1; }, setParser:function( name, parser ){ this.parsers[name] = parser; }, // The next 3 are exposed so you can use them quote:quote, literal:literal, join:join, // _depth_: 1, // This is the list of parsers, to modify them, use jsDump.setParser parsers:{ window: '[Window]', document: '[Document]', error:'[ERROR]', //when no parser is found, shouldn't happen unknown: '[Unknown]', 'null':'null', 'undefined':'undefined', 'function':function( fn ){ var ret = 'function', name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE if( name ) ret += ' ' + name; ret += '('; ret = [ ret, this._parse( fn, 'functionArgs' ), '){'].join(''); return join( ret, this._parse(fn,'functionCode'), '}' ); }, array: array, nodelist: array, 'arguments': array, scobj: function(obj) { return obj.toString(); }, object:function( map ){ var ret = [ ]; this.up(); for( var key in map ) { ret.push( this._parse(key,'key') + ': ' + this._parse(map[key]) ); } this.down(); return join( '{', ret, '}' ); }, node:function( node ){ var open = this.HTML ? '<' : '<', close = this.HTML ? '>' : '>'; var tag = node.nodeName.toLowerCase(), ret = open + tag; for( var a in this.DOMAttrs ){ var val = node[this.DOMAttrs[a]]; if( val ) { ret += ' ' + a + '=' + this._parse( val, 'attribute' ); } } return ret + close + open + '/' + tag + close; }, functionArgs:function( fn ){//function calls it internally, it's the arguments part of the function var l = fn.length; if( !l ) return ''; var args = new Array(l); while( l-- ) args[l] = String.fromCharCode(97+l);//97 is 'a' return ' ' + args.join(', ') + ' '; }, key:quote, //object calls it internally, the key part of an item in a map functionCode:'[code]', //function calls it internally, it's the content of the function attribute:quote, //node calls it internally, it's an html attribute value string:quote, date:quote, regexp:literal, //regex number:literal, 'boolean':literal }, DOMAttrs:{//attributes to dump from nodes, name=>realName id:'id', name:'name', 'class':'className' }, HTML:true,//if true, entities are escaped ( <, >, \t, space and \n ) indentChar:' ',//indentation unit multiline:true //if true, items in a collection, are separated by a \n, else just a space. }; CoreTest.dump = function dump(obj,type) { return CoreTest.jsDump.parse(obj, type); }; })();