/** * 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} */ var jsDump; (function(){ 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 = Array(i); this.up(); while( i-- ) ret[i] = this.parse( arr[i] ); this.down(); return join( '[', ret, ']' ); } var reName = /^function (\w+)/; jsDump = { doParse:function(obj) { this.alreadySeenObjects = []; return this.parse(obj); }, parse:function( obj, type ){//type is used mostly internally, you can fix a (custom)type in advance var parser = this.parsers[ type || this.typeOf(obj) ]; type = typeof parser; return type == 'function' ? parser.call( this, obj ) : type == 'string' ? parser : this.parsers.error; }, typeOf:function( obj ){ var type = typeof obj, kind; if ( type == 'object' || type == 'function' ) { if ( obj === null ) return 'null'; // Extract Staff from [Object Staff] kind = Object.prototype.toString.call(obj).slice(8, -1); switch ( kind ) { case 'Array': return 'array'; case 'Date': return 'date'; case 'RegExp': return 'regexp'; case 'Window': //Firefox, IE, Opera case 'DOMWindow': //WebKit case 'global': return 'window'; case 'HTMLDocument': //WebKit, Firefox, Opera case 'Document': // IE return 'document'; case 'NodeList': return 'nodelist'; default: if ( 'callee' in obj ) // Opera: Object.prototype.toString.call(arguments) == 'Object' :( return 'arguments'; else if ( 'ownerDocument' in obj && 'defaultView' in obj.ownerDocument && obj instanceof obj.ownerDocument.defaultView.Node ) return 'node'; } } return 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 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, object:function( map ){ var ret = [ ]; this.up(); var keys = []; for (var key in map) { keys.push(key); } keys = keys.sort(); for( var i = 0; i < keys.length; ++i ) { var key = keys[i]; if (!this.ignoreFunctions || this.typeOf(map[key]) != 'function') { var value = "<< LOOP >>"; if ((typeof map[key] != 'object') || this.alreadySeenObjects.indexOf(map[key]) == -1) { this.alreadySeenObjects.push(map[key]); value = this.parse(map[key]); } ret.push( this.parse(key,'key') + ': ' + value ); } } 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 = 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' }, alreadySeenObjects:[], ignoreFunctions:true, HTML:false,//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. }; })(); if (typeof exports !== 'undefined') { module.exports = jsDump; }