4 * Pass me a data structure {} and I'll output all the key/value pairs - recursively
6 * @example var HTML = debugData( oElem.style, "Element.style", { keys: "top,left,width,height", recurse: true, sort: true, display: true, returnHTML: true });
8 * @param Object o_Data A JSON-style data structure
9 * @param String s_Title Title for dialog (optional)
10 * @param Hash options Pass additional options in a hash
12 function debugData (o_Data, s_Title, options) {
13 options = options || {};
15 str=(s_Title||s_Title==='' ? s_Title : 'DATA')
16 , dType=$.type(o_Data)
17 // maintain backward compatibility with OLD 'recurseData' param
18 , recurse=($.type(options)==='boolean' ? options : options.recurse !==false)
19 , keys=(options.keys?','+options.keys+',':false)
20 , display=options.display !==false
21 , html=options.returnHTML !==false
23 , prefix=options.indent ? ' ' : ''
24 , D=[], i=0 // Array to hold data, i=counter
26 , k, t, skip, x, type // loop vars
28 if (dType!=='object' && dType!=='array') {
29 if (options.display) alert( (s_Title || 'debugData') +': '+ o_Data );
32 if (dType==='object' && $.isPlainObject(o_Data))
36 str=s_Title+'jQuery Collection ('+ o_Data.length +')\n context="'+ o_Data.context +'"';
38 else if (o_Data.nodeName) {
39 str=s_Title+o_Data.tagName;
40 var id = o_Data.id, cls=o_Data.className, src=o_Data.src, hrf=o_Data.href;
41 if (id) str+='\n id="'+ id+'"';
42 if (cls) str+='\n class="'+ cls+'"';
43 if (src) str+='\n src="'+ src+'"';
44 if (hrf) str+='\n href="'+ hrf+'"';
47 parse(o_Data,prefix,dType); // recursive parsing
48 if (sort && !hasSubKeys) D.sort(); // sort by keyName - but NOT if has subKeys!
49 if (str) str += '\n***'+ '****************************'.substr(0,str.length) +'\n';
50 str += D.join('\n'); // add line-breaks
53 if (display) alert(str); // display data
54 if (html) str=str.replace(/\n/g, ' <br>').replace(/ /g, ' '); // format as HTML
57 function parse ( data, prefix, parentType ) {
60 $.each( data, function (key, val) {
61 skip = (keys && keys.indexOf(','+key+',') === -1);
63 if (type==='object' && $.isPlainObject(val))
65 k = prefix + (first ? ' ' : ', ');
68 if (parentType!=='array') // no key-names for array items
69 k += key+': '; // NOT an array
71 if (type==="date" || type==="regexp") {
75 if (type==="string") { // STRING
76 if (!skip) D[i++] = k +'"'+ val +'"';
78 // NULL, UNDEFINED, NUMBER or BOOLEAN
79 else if (/^(null|undefined|number|boolean)/.test(type)) {
80 if (!skip) D[i++] = k + val;
82 else if (type==="function") { // FUNCTION
83 if (!skip) D[i++] = k +'function()';
85 else if (type==="array") { // ARRAY
88 parse( val, prefix+' ',type); // RECURSE
89 D[i++] = prefix +' ]';
92 else if (val.jquery) { // JQUERY OBJECT
93 if (!skip) D[i++] = k +'jQuery ('+ val.length +') context="'+ val.context +'"';
95 else if (val.nodeName) { // DOM ELEMENT
96 var id = val.id, cls=val.className, src=val.src, hrf=val.href;
97 if (skip) D[i++] = k +' '+
99 src ? 'src="'+ src+'"' :
100 hrf ? 'href="'+ hrf+'"' :
101 cls ? 'class="'+cls+'"' :
104 else if (type==="hash") { // JSON
105 if (!recurse || $.isEmptyObject(val)) { // show an empty hash
106 if (!skip) D[i++] = k +'{ }';
108 else { // recurse into JSON hash - indent output
110 parse( val, prefix+' ',type); // RECURSE
111 D[i++] = prefix +' }';
115 if (!skip) D[i++] = k +'OBJECT'; // NOT a hash
122 function debugStackTrace (s_Title, options) {
125 , isCallstackPopulated = false
128 i.dont.exist += 0; // doesn't exist- that's the point
130 if (e.stack) { // Firefox
131 var lines = e.stack.split('\n');
132 for (var i=0, len=lines.length; i<len; i++) {
133 if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
134 callstack.push(lines[i]);
137 //Remove call to printStackTrace()
139 isCallstackPopulated = true;
141 else if (window.opera && e.message) { // Opera
142 var lines = e.message.split('\n');
143 for (var i=0, len=lines.length; i<len; i++) {
144 if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
145 var entry = lines[i];
146 //Append next line also since it has the file info
148 entry += ' at ' + lines[i+1];
151 callstack.push(entry);
154 //Remove call to printStackTrace()
156 isCallstackPopulated = true;
160 if (!isCallstackPopulated) { // IE and Safari
161 var currentFunction = arguments.callee.caller;
162 while (currentFunction) {
163 var fn = currentFunction.toString();
164 var fname = fn.substring(fn.indexOf('function') + 8, fn.indexOf('')) || 'anonymous';
165 callstack.push(fname);
166 currentFunction = currentFunction.caller;
170 debugData( callstack, s_Title, options );
173 if (!window.console) window.console = { log: debugData };
175 if (!window.console.trace)
176 window.console.trace = function (s_Title) {
177 window.console.log( debugStackTrace(s_Title, { display: false, returnHTML: false, sort: false }) );
180 // add method to output 'hash data' inside an string
181 window.console.data = function (data, title) {
182 var w = { array: ['[',']'], object: ['{','}'], string: ['"','"'], number: ['',''], function: ['','()'] }
184 , obj = x.match(/(object|array)/)
185 , delim = !obj ? ['',''] : x === 'object' ? ['{\n','\n}'] : ['[\n','\n]']
186 , opts = { display: false, returnHTML: false, sort: false }
187 , debug = debugData( data, '', opts)
190 (title ? title +' = ' : '')
192 + ($.type(debug) === 'string' ? debug.replace(/ /g, '\t') : debug)
201 * Utility for debug timing of events
202 * Can track multiple timers and returns either a total time or interval from last event
203 * @param String timerName Name of the timer - defaults to debugTimer
204 * @param String action Keyword for action or return-value...
205 * action: 'reset' = reset; 'clear' = delete; 'total' = ms since init; 'step' or '' = ms since last event
210 * Utility method for timing performance
211 * Can track multiple timers and returns either a total time or interval from last event
213 * returns time-data: {
215 * , last: Date Object
216 * , step: 99 // time since 'last'
217 * , total: 99 // time since 'start'
222 * timer('name'); // create/init timer
223 * timer('name', 'reset'); // re-init timer
224 * timer('name', 'clear'); // clear/remove timer
225 * var i = timer('name'); // how long since last timer request?
226 * var i = timer('name', 'total'); // how long since timer started?
228 * @param String timerName Name of the timer - defaults to debugTimer
229 * @param String action Keyword for action or return-value...
230 * @param Hash options Options to customize return data
231 * action: 'reset' = reset; 'clear' = delete; 'total' = ms since init; 'step' or '' = ms since last event
233 function timer (timerName, action, options) {
235 name = timerName || 'debugTimer'
236 , Timer = window[ name ]
245 // init the timer first time called
246 if (!Timer || action == 'reset') { // init timer
247 Timer = window[ name ] = {
250 , step: 0 // time since 'last'
251 , total: 0 // time since 'start'
252 , options: $.extend({}, defaults, options)
255 else if (action == 'clear') { // remove timer
256 window[ name ] = null;
259 else { // update existing timer
260 Timer.step = (new Date()) - Timer.last; // time since 'last'
261 Timer.total = (new Date()) - Timer.start; // time since 'start'
262 Timer.last = new Date();
266 time = (action == 'total') ? Timer.total : Timer.step
267 , o = Timer.options // alias
270 if (o.returnString) {
271 time += ""; // convert integer to string
272 // pad time to 4 chars with underscores
274 switch (time.length) {
275 case 1: time = "   "+ time; break;
276 case 2: time = "  "+ time; break;
277 case 3: time = " "+ time; break;
279 // add prefix and suffix
280 if (o.timePrefix || o.timeSuffix)
281 time = o.timePrefix + time + o.timeSuffix;
291 * Pass a layout-options object, and the pane/key you want to display
293 function showOptions (Layout, key, debugOpts) {
294 var data = Layout.options;
295 $.each(key.split("."), function() {
296 data = data[this]; // recurse through multiple key-levels
298 debugData( data, 'options.'+key, debugOpts );
304 * Pass a layout-options object, and the pane/key you want to display
306 function showState (Layout, key, debugOpts) {
307 var data = Layout.state;
308 $.each(key.split("."), function() {
309 data = data[this]; // recurse through multiple key-levels
311 debugData( data, 'state.'+key, debugOpts );
317 function debugWindow ( content, options ) {
324 $.extend( true, (options || {}), defaults );
325 var $W = $('<div></div>')
326 .html( content.replace(/\n/g, '<br>').replace(/ /g, ' ') ) // format as HTML