1 define("dojo/_firebug/firebug", [ |
|
2 "../_base/kernel", |
|
3 "require", |
|
4 "../_base/html", |
|
5 "../sniff", |
|
6 "../_base/array", |
|
7 "../_base/lang", |
|
8 "../_base/event", |
|
9 "../_base/unload"], function(dojo, require, html, has){ |
|
10 |
|
11 // module: |
|
12 // dojo/_firebug/firebug |
|
13 // summary: |
|
14 // Firebug Lite, the baby brother to Joe Hewitt's Firebug for Mozilla Firefox |
|
15 // description: |
|
16 // Opens a console for logging, debugging, and error messages. |
|
17 // Contains partial functionality to Firebug. See function list below. |
|
18 // |
|
19 // NOTE: |
|
20 // Firebug is a Firefox extension created by Joe Hewitt (see license). You do not need Dojo to run Firebug. |
|
21 // Firebug Lite is included in Dojo by permission from Joe Hewitt |
|
22 // If you are new to Firebug, or used to the Dojo 0.4 dojo.debug, you can learn Firebug |
|
23 // functionality by reading the function comments below or visiting http://www.getfirebug.com/docs.html |
|
24 // |
|
25 // NOTE: |
|
26 // To test Firebug Lite in Firefox: |
|
27 // |
|
28 // - FF2: set "console = null" before loading dojo and set djConfig.isDebug=true |
|
29 // - FF3: disable Firebug and set djConfig.isDebug=true |
|
30 // |
|
31 // example: |
|
32 // Supports inline objects in object inspector window (only simple trace of dom nodes, however) |
|
33 // | console.log("my object", {foo:"bar"}) |
|
34 // example: |
|
35 // Option for console to open in popup window |
|
36 // | var djConfig = {isDebug: true, popup:true }; |
|
37 // example: |
|
38 // Option for console height (ignored for popup) |
|
39 // | var djConfig = {isDebug: true, debugHeight:100 } |
|
40 |
|
41 |
|
42 var isNewIE = (/Trident/.test(window.navigator.userAgent)); |
|
43 if(isNewIE){ |
|
44 // Fixing IE's console |
|
45 // IE doesn't insert space between arguments. How annoying. |
|
46 var calls = ["log", "info", "debug", "warn", "error"]; |
|
47 for(var i=0;i<calls.length;i++){ |
|
48 var m = calls[i]; |
|
49 if(!console[m] ||console[m]._fake){ |
|
50 // IE9 doesn't have console.debug method, a fake one is added later |
|
51 continue; |
|
52 } |
|
53 var n = "_"+calls[i]; |
|
54 console[n] = console[m]; |
|
55 console[m] = (function(){ |
|
56 var type = n; |
|
57 return function(){ |
|
58 console[type](Array.prototype.join.call(arguments, " ")); |
|
59 }; |
|
60 })(); |
|
61 } |
|
62 // clear the console on load. This is more than a convenience - too many logs crashes it. |
|
63 // If closed it throws an error |
|
64 try{ console.clear(); }catch(e){} |
|
65 } |
|
66 |
|
67 if( |
|
68 has("ff") || // Firefox has Firebug |
|
69 has("chrome") || // Chrome 3+ has a console |
|
70 has("safari") || // Safari 4 has a console |
|
71 isNewIE || // Has the new IE console |
|
72 window.firebug || // Testing for mozilla firebug lite |
|
73 (typeof console != "undefined" && console.firebug) || //The firebug console |
|
74 dojo.config.useCustomLogger || // Allow custom loggers |
|
75 has("air") // isDebug triggers AIRInsector, not Firebug |
|
76 ){ |
|
77 return; |
|
78 } |
|
79 |
|
80 // don't build firebug in iframes |
|
81 try{ |
|
82 if(window != window.parent){ |
|
83 // but if we've got a parent logger, connect to it |
|
84 if(window.parent["console"]){ |
|
85 window.console = window.parent.console; |
|
86 } |
|
87 return; |
|
88 } |
|
89 }catch(e){/*squelch*/} |
|
90 |
|
91 // *************************************************************************** |
|
92 // Placing these variables before the functions that use them to avoid a |
|
93 // shrinksafe bug where variable renaming does not happen correctly otherwise. |
|
94 |
|
95 // most of the objects in this script are run anonomously |
|
96 var _firebugDoc = document; |
|
97 var _firebugWin = window; |
|
98 var __consoleAnchorId__ = 0; |
|
99 |
|
100 var consoleFrame = null; |
|
101 var consoleBody = null; |
|
102 var consoleObjectInspector = null; |
|
103 var fireBugTabs = null; |
|
104 var commandLine = null; |
|
105 var consoleToolbar = null; |
|
106 |
|
107 var frameVisible = false; |
|
108 var messageQueue = []; |
|
109 var groupStack = []; |
|
110 var timeMap = {}; |
|
111 var countMap = {}; |
|
112 |
|
113 var consoleDomInspector = null; |
|
114 var _inspectionMoveConnection; |
|
115 var _inspectionClickConnection; |
|
116 var _inspectionEnabled = false; |
|
117 var _inspectionTimer = null; |
|
118 var _inspectTempNode = document.createElement("div"); |
|
119 |
|
120 |
|
121 var _inspectCurrentNode; |
|
122 var _restoreBorderStyle; |
|
123 |
|
124 // *************************************************************************** |
|
125 |
|
126 window.console = { |
|
127 _connects: [], |
|
128 log: function(){ |
|
129 // summary: |
|
130 // Sends arguments to console. |
|
131 logFormatted(arguments, ""); |
|
132 }, |
|
133 |
|
134 debug: function(){ |
|
135 // summary: |
|
136 // Sends arguments to console. Missing finctionality to show script line of trace. |
|
137 logFormatted(arguments, "debug"); |
|
138 }, |
|
139 |
|
140 info: function(){ |
|
141 // summary: |
|
142 // Sends arguments to console, highlighted with (I) icon. |
|
143 logFormatted(arguments, "info"); |
|
144 }, |
|
145 |
|
146 warn: function(){ |
|
147 // summary: |
|
148 // Sends warning arguments to console, highlighted with (!) icon and blue style. |
|
149 logFormatted(arguments, "warning"); |
|
150 }, |
|
151 |
|
152 error: function(){ |
|
153 // summary: |
|
154 // Sends error arguments (object) to console, highlighted with (X) icon and yellow style |
|
155 // NEW: error object now displays in object inspector |
|
156 logFormatted(arguments, "error"); |
|
157 }, |
|
158 |
|
159 assert: function(truth, message){ |
|
160 // summary: |
|
161 // Tests for true. Throws exception if false. |
|
162 if(!truth){ |
|
163 var args = []; |
|
164 for(var i = 1; i < arguments.length; ++i){ |
|
165 args.push(arguments[i]); |
|
166 } |
|
167 |
|
168 logFormatted(args.length ? args : ["Assertion Failure"], "error"); |
|
169 throw message ? message : "Assertion Failure"; |
|
170 } |
|
171 }, |
|
172 |
|
173 dir: function(obj){ |
|
174 var str = printObject( obj ); |
|
175 str = str.replace(/\n/g, "<br />"); |
|
176 str = str.replace(/\t/g, " "); |
|
177 logRow([str], "dir"); |
|
178 }, |
|
179 |
|
180 dirxml: function(node){ |
|
181 var html = []; |
|
182 appendNode(node, html); |
|
183 logRow(html, "dirxml"); |
|
184 }, |
|
185 |
|
186 group: function(){ |
|
187 // summary: |
|
188 // collects log messages into a group, starting with this call and ending with |
|
189 // groupEnd(). Missing collapse functionality |
|
190 logRow(arguments, "group", pushGroup); |
|
191 }, |
|
192 |
|
193 groupEnd: function(){ |
|
194 // summary: |
|
195 // Closes group. See above |
|
196 logRow(arguments, "", popGroup); |
|
197 }, |
|
198 |
|
199 time: function(name){ |
|
200 // summary: |
|
201 // Starts timers assigned to name given in argument. Timer stops and displays on timeEnd(title); |
|
202 // example: |
|
203 // | console.time("load"); |
|
204 // | console.time("myFunction"); |
|
205 // | console.timeEnd("load"); |
|
206 // | console.timeEnd("myFunction"); |
|
207 timeMap[name] = new Date().getTime(); |
|
208 }, |
|
209 |
|
210 timeEnd: function(name){ |
|
211 // summary: |
|
212 // See above. |
|
213 if(name in timeMap){ |
|
214 var delta = (new Date()).getTime() - timeMap[name]; |
|
215 logFormatted([name+ ":", delta+"ms"]); |
|
216 delete timeMap[name]; |
|
217 } |
|
218 }, |
|
219 |
|
220 count: function(name){ |
|
221 // summary: |
|
222 // Not supported |
|
223 if(!countMap[name]) countMap[name] = 0; |
|
224 countMap[name]++; |
|
225 logFormatted([name+": "+countMap[name]]); |
|
226 }, |
|
227 |
|
228 trace: function(_value){ |
|
229 var stackAmt = _value || 3; |
|
230 var f = console.trace.caller; //function that called trace |
|
231 console.log(">>> console.trace(stack)"); |
|
232 for(var i=0;i<stackAmt;i++){ |
|
233 var func = f.toString(); |
|
234 var args=[]; |
|
235 for (var a = 0; a < f.arguments.length; a++){ |
|
236 args.push(f.arguments[a]); |
|
237 } |
|
238 if(f.arguments.length){ |
|
239 console.dir({"function":func, "arguments":args}); |
|
240 }else{ |
|
241 console.dir({"function":func}); |
|
242 } |
|
243 |
|
244 f = f.caller; |
|
245 } |
|
246 }, |
|
247 |
|
248 profile: function(){ |
|
249 // summary: |
|
250 // Not supported |
|
251 this.warn(["profile() not supported."]); |
|
252 }, |
|
253 |
|
254 profileEnd: function(){ }, |
|
255 |
|
256 clear: function(){ |
|
257 // summary: |
|
258 // Clears message console. Do not call this directly |
|
259 if(consoleBody){ |
|
260 while(consoleBody.childNodes.length){ |
|
261 dojo.destroy(consoleBody.firstChild); |
|
262 } |
|
263 } |
|
264 dojo.forEach(this._connects,dojo.disconnect); |
|
265 }, |
|
266 |
|
267 open: function(){ |
|
268 // summary: |
|
269 // Opens message console. Do not call this directly |
|
270 toggleConsole(true); |
|
271 }, |
|
272 |
|
273 close: function(){ |
|
274 // summary: |
|
275 // Closes message console. Do not call this directly |
|
276 if(frameVisible){ |
|
277 toggleConsole(); |
|
278 } |
|
279 }, |
|
280 _restoreBorder: function(){ |
|
281 if(_inspectCurrentNode){ |
|
282 _inspectCurrentNode.style.border = _restoreBorderStyle; |
|
283 } |
|
284 }, |
|
285 openDomInspector: function(){ |
|
286 _inspectionEnabled = true; |
|
287 consoleBody.style.display = "none"; |
|
288 consoleDomInspector.style.display = "block"; |
|
289 consoleObjectInspector.style.display = "none"; |
|
290 document.body.style.cursor = "pointer"; |
|
291 _inspectionMoveConnection = dojo.connect(document, "mousemove", function(evt){ |
|
292 if(!_inspectionEnabled){ return; } |
|
293 if(!_inspectionTimer){ |
|
294 _inspectionTimer = setTimeout(function(){ _inspectionTimer = null; }, 50); |
|
295 }else{ |
|
296 return; |
|
297 } |
|
298 var node = evt.target; |
|
299 if(node && (_inspectCurrentNode !== node)){ |
|
300 var parent = true; |
|
301 |
|
302 console._restoreBorder(); |
|
303 var html = []; |
|
304 appendNode(node, html); |
|
305 consoleDomInspector.innerHTML = html.join(""); |
|
306 |
|
307 _inspectCurrentNode = node; |
|
308 _restoreBorderStyle = _inspectCurrentNode.style.border; |
|
309 _inspectCurrentNode.style.border = "#0000FF 1px solid"; |
|
310 } |
|
311 }); |
|
312 setTimeout(function(){ |
|
313 _inspectionClickConnection = dojo.connect(document, "click", function(evt){ |
|
314 document.body.style.cursor = ""; |
|
315 _inspectionEnabled = !_inspectionEnabled; |
|
316 dojo.disconnect(_inspectionClickConnection); |
|
317 // console._restoreBorder(); |
|
318 }); |
|
319 }, 30); |
|
320 }, |
|
321 _closeDomInspector: function(){ |
|
322 document.body.style.cursor = ""; |
|
323 dojo.disconnect(_inspectionMoveConnection); |
|
324 dojo.disconnect(_inspectionClickConnection); |
|
325 _inspectionEnabled = false; |
|
326 console._restoreBorder(); |
|
327 }, |
|
328 openConsole:function(){ |
|
329 // summary: |
|
330 // Closes object inspector and opens message console. Do not call this directly |
|
331 consoleBody.style.display = "block"; |
|
332 consoleDomInspector.style.display = "none"; |
|
333 consoleObjectInspector.style.display = "none"; |
|
334 console._closeDomInspector(); |
|
335 }, |
|
336 openObjectInspector:function(){ |
|
337 consoleBody.style.display = "none"; |
|
338 consoleDomInspector.style.display = "none"; |
|
339 consoleObjectInspector.style.display = "block"; |
|
340 console._closeDomInspector(); |
|
341 }, |
|
342 recss: function(){ |
|
343 // this is placed in dojo since the console is most likely |
|
344 // in another window and dojo is easilly accessible |
|
345 var i,a,s;a=document.getElementsByTagName('link'); |
|
346 for(i=0;i<a.length;i++){ |
|
347 s=a[i]; |
|
348 if(s.rel.toLowerCase().indexOf('stylesheet')>=0&&s.href){ |
|
349 var h=s.href.replace(/(&|%5C?)forceReload=\d+/,''); |
|
350 s.href=h+(h.indexOf('?')>=0?'&':'?')+'forceReload='+new Date().valueOf(); |
|
351 } |
|
352 } |
|
353 } |
|
354 }; |
|
355 |
|
356 // *************************************************************************** |
|
357 |
|
358 function toggleConsole(forceOpen){ |
|
359 frameVisible = forceOpen || !frameVisible; |
|
360 if(consoleFrame){ |
|
361 consoleFrame.style.display = frameVisible ? "block" : "none"; |
|
362 } |
|
363 } |
|
364 |
|
365 function focusCommandLine(){ |
|
366 toggleConsole(true); |
|
367 if(commandLine){ |
|
368 commandLine.focus(); |
|
369 } |
|
370 } |
|
371 |
|
372 function openWin(x,y,w,h){ |
|
373 var win = window.open("","_firebug","status=0,menubar=0,resizable=1,top="+y+",left="+x+",width="+w+",height="+h+",scrollbars=1,addressbar=0"); |
|
374 if(!win){ |
|
375 var msg = "Firebug Lite could not open a pop-up window, most likely because of a blocker.\n" + |
|
376 "Either enable pop-ups for this domain, or change the djConfig to popup=false."; |
|
377 alert(msg); |
|
378 } |
|
379 createResizeHandler(win); |
|
380 var newDoc=win.document; |
|
381 //Safari needs an HTML height |
|
382 var HTMLstring= '<html style="height:100%;"><head><title>Firebug Lite</title></head>\n' + |
|
383 '<body bgColor="#ccc" style="height:97%;" onresize="opener.onFirebugResize()">\n' + |
|
384 '<div id="fb"></div>' + |
|
385 '</body></html>'; |
|
386 |
|
387 newDoc.write(HTMLstring); |
|
388 newDoc.close(); |
|
389 return win; |
|
390 } |
|
391 |
|
392 function createResizeHandler(wn){ |
|
393 // summary: |
|
394 // Creates handle for onresize window. Called from script in popup's body tag (so that it will work with IE). |
|
395 // |
|
396 |
|
397 var d = new Date(); |
|
398 d.setTime(d.getTime()+(60*24*60*60*1000)); // 60 days |
|
399 d = d.toUTCString(); |
|
400 |
|
401 var dc = wn.document, |
|
402 getViewport; |
|
403 |
|
404 if (wn.innerWidth){ |
|
405 getViewport = function(){ |
|
406 return{w:wn.innerWidth, h:wn.innerHeight}; |
|
407 }; |
|
408 }else if (dc.documentElement && dc.documentElement.clientWidth){ |
|
409 getViewport = function(){ |
|
410 return{w:dc.documentElement.clientWidth, h:dc.documentElement.clientHeight}; |
|
411 }; |
|
412 }else if (dc.body){ |
|
413 getViewport = function(){ |
|
414 return{w:dc.body.clientWidth, h:dc.body.clientHeight}; |
|
415 }; |
|
416 } |
|
417 |
|
418 |
|
419 window.onFirebugResize = function(){ |
|
420 |
|
421 //resize the height of the console log body |
|
422 layout(getViewport().h); |
|
423 |
|
424 clearInterval(wn._firebugWin_resize); |
|
425 wn._firebugWin_resize = setTimeout(function(){ |
|
426 var x = wn.screenLeft, |
|
427 y = wn.screenTop, |
|
428 w = wn.outerWidth || wn.document.body.offsetWidth, |
|
429 h = wn.outerHeight || wn.document.body.offsetHeight; |
|
430 |
|
431 document.cookie = "_firebugPosition=" + [x,y,w,h].join(",") + "; expires="+d+"; path=/"; |
|
432 |
|
433 }, 5000); //can't capture window.onMove - long timeout gives better chance of capturing a resize, then the move |
|
434 |
|
435 }; |
|
436 } |
|
437 |
|
438 |
|
439 /*****************************************************************************/ |
|
440 |
|
441 |
|
442 function createFrame(){ |
|
443 if(consoleFrame){ |
|
444 return; |
|
445 } |
|
446 toggleConsole(true); |
|
447 if(dojo.config.popup){ |
|
448 var containerHeight = "100%"; |
|
449 var cookieMatch = document.cookie.match(/(?:^|; )_firebugPosition=([^;]*)/); |
|
450 var p = cookieMatch ? cookieMatch[1].split(",") : [2,2,320,480]; |
|
451 |
|
452 _firebugWin = openWin(p[0],p[1],p[2],p[3]); // global |
|
453 _firebugDoc = _firebugWin.document; // global |
|
454 |
|
455 dojo.config.debugContainerId = 'fb'; |
|
456 |
|
457 // connecting popup |
|
458 _firebugWin.console = window.console; |
|
459 _firebugWin.dojo = window.dojo; |
|
460 }else{ |
|
461 _firebugDoc = document; |
|
462 containerHeight = (dojo.config.debugHeight || 300) + "px"; |
|
463 } |
|
464 |
|
465 var styleElement = _firebugDoc.createElement("link"); |
|
466 styleElement.href = require.toUrl("./firebug.css"); |
|
467 styleElement.rel = "stylesheet"; |
|
468 styleElement.type = "text/css"; |
|
469 var styleParent = _firebugDoc.getElementsByTagName("head"); |
|
470 if(styleParent){ |
|
471 styleParent = styleParent[0]; |
|
472 } |
|
473 if(!styleParent){ |
|
474 styleParent = _firebugDoc.getElementsByTagName("html")[0]; |
|
475 } |
|
476 if(has("ie")){ |
|
477 window.setTimeout(function(){ styleParent.appendChild(styleElement); }, 0); |
|
478 }else{ |
|
479 styleParent.appendChild(styleElement); |
|
480 } |
|
481 |
|
482 if(dojo.config.debugContainerId){ |
|
483 consoleFrame = _firebugDoc.getElementById(dojo.config.debugContainerId); |
|
484 } |
|
485 if(!consoleFrame){ |
|
486 consoleFrame = _firebugDoc.createElement("div"); |
|
487 _firebugDoc.body.appendChild(consoleFrame); |
|
488 } |
|
489 consoleFrame.className += " firebug"; |
|
490 consoleFrame.id = "firebug"; |
|
491 consoleFrame.style.height = containerHeight; |
|
492 consoleFrame.style.display = (frameVisible ? "block" : "none"); |
|
493 |
|
494 var buildLink = function(label, title, method, _class){ |
|
495 return '<li class="'+_class+'"><a href="javascript:void(0);" onclick="console.'+ method +'(); return false;" title="'+title+'">'+label+'</a></li>'; |
|
496 }; |
|
497 consoleFrame.innerHTML = |
|
498 '<div id="firebugToolbar">' |
|
499 + ' <ul id="fireBugTabs" class="tabs">' |
|
500 |
|
501 + buildLink("Clear", "Remove All Console Logs", "clear", "") |
|
502 + buildLink("ReCSS", "Refresh CSS without reloading page", "recss", "") |
|
503 |
|
504 + buildLink("Console", "Show Console Logs", "openConsole", "gap") |
|
505 + buildLink("DOM", "Show DOM Inspector", "openDomInspector", "") |
|
506 + buildLink("Object", "Show Object Inspector", "openObjectInspector", "") |
|
507 + ((dojo.config.popup) ? "" : buildLink("Close", "Close the console", "close", "gap")) |
|
508 |
|
509 + ' </ul>' |
|
510 + '</div>' |
|
511 + '<input type="text" id="firebugCommandLine" />' |
|
512 + '<div id="firebugLog"></div>' |
|
513 + '<div id="objectLog" style="display:none;">Click on an object in the Log display</div>' |
|
514 + '<div id="domInspect" style="display:none;">Hover over HTML elements in the main page. Click to hold selection.</div>'; |
|
515 |
|
516 |
|
517 consoleToolbar = _firebugDoc.getElementById("firebugToolbar"); |
|
518 |
|
519 commandLine = _firebugDoc.getElementById("firebugCommandLine"); |
|
520 addEvent(commandLine, "keydown", onCommandLineKeyDown); |
|
521 |
|
522 addEvent(_firebugDoc, has("ie") || has("safari") ? "keydown" : "keypress", onKeyDown); |
|
523 |
|
524 consoleBody = _firebugDoc.getElementById("firebugLog"); |
|
525 consoleObjectInspector = _firebugDoc.getElementById("objectLog"); |
|
526 consoleDomInspector = _firebugDoc.getElementById("domInspect"); |
|
527 fireBugTabs = _firebugDoc.getElementById("fireBugTabs"); |
|
528 layout(); |
|
529 flush(); |
|
530 } |
|
531 |
|
532 dojo.addOnLoad(createFrame); |
|
533 |
|
534 function clearFrame(){ |
|
535 _firebugDoc = null; |
|
536 |
|
537 if(_firebugWin.console){ |
|
538 _firebugWin.console.clear(); |
|
539 } |
|
540 _firebugWin = null; |
|
541 consoleFrame = null; |
|
542 consoleBody = null; |
|
543 consoleObjectInspector = null; |
|
544 consoleDomInspector = null; |
|
545 commandLine = null; |
|
546 messageQueue = []; |
|
547 groupStack = []; |
|
548 timeMap = {}; |
|
549 } |
|
550 |
|
551 |
|
552 function evalCommandLine(){ |
|
553 var text = commandLine.value; |
|
554 commandLine.value = ""; |
|
555 |
|
556 logRow(["> ", text], "command"); |
|
557 |
|
558 var value; |
|
559 try{ |
|
560 value = eval(text); |
|
561 }catch(e){ |
|
562 console.debug(e); // put exception on the console |
|
563 } |
|
564 |
|
565 console.log(value); |
|
566 } |
|
567 |
|
568 function layout(h){ |
|
569 var tHeight = 25; //consoleToolbar.offsetHeight; // tab style not ready on load - throws off layout |
|
570 var height = h ? |
|
571 h - (tHeight + commandLine.offsetHeight +25 + (h*.01)) + "px" : |
|
572 (consoleFrame.offsetHeight - tHeight - commandLine.offsetHeight) + "px"; |
|
573 |
|
574 consoleBody.style.top = tHeight + "px"; |
|
575 consoleBody.style.height = height; |
|
576 consoleObjectInspector.style.height = height; |
|
577 consoleObjectInspector.style.top = tHeight + "px"; |
|
578 consoleDomInspector.style.height = height; |
|
579 consoleDomInspector.style.top = tHeight + "px"; |
|
580 commandLine.style.bottom = 0; |
|
581 |
|
582 dojo.addOnWindowUnload(clearFrame); |
|
583 } |
|
584 |
|
585 function logRow(message, className, handler){ |
|
586 if(consoleBody){ |
|
587 writeMessage(message, className, handler); |
|
588 }else{ |
|
589 messageQueue.push([message, className, handler]); |
|
590 } |
|
591 } |
|
592 |
|
593 function flush(){ |
|
594 var queue = messageQueue; |
|
595 messageQueue = []; |
|
596 |
|
597 for(var i = 0; i < queue.length; ++i){ |
|
598 writeMessage(queue[i][0], queue[i][1], queue[i][2]); |
|
599 } |
|
600 } |
|
601 |
|
602 function writeMessage(message, className, handler){ |
|
603 var isScrolledToBottom = |
|
604 consoleBody.scrollTop + consoleBody.offsetHeight >= consoleBody.scrollHeight; |
|
605 |
|
606 handler = handler||writeRow; |
|
607 |
|
608 handler(message, className); |
|
609 |
|
610 if(isScrolledToBottom){ |
|
611 consoleBody.scrollTop = consoleBody.scrollHeight - consoleBody.offsetHeight; |
|
612 } |
|
613 } |
|
614 |
|
615 function appendRow(row){ |
|
616 var container = groupStack.length ? groupStack[groupStack.length-1] : consoleBody; |
|
617 container.appendChild(row); |
|
618 } |
|
619 |
|
620 function writeRow(message, className){ |
|
621 var row = consoleBody.ownerDocument.createElement("div"); |
|
622 row.className = "logRow" + (className ? " logRow-"+className : ""); |
|
623 row.innerHTML = message.join(""); |
|
624 appendRow(row); |
|
625 } |
|
626 |
|
627 function pushGroup(message, className){ |
|
628 logFormatted(message, className); |
|
629 |
|
630 //var groupRow = consoleBody.ownerDocument.createElement("div"); |
|
631 //groupRow.className = "logGroup"; |
|
632 var groupRowBox = consoleBody.ownerDocument.createElement("div"); |
|
633 groupRowBox.className = "logGroupBox"; |
|
634 //groupRow.appendChild(groupRowBox); |
|
635 appendRow(groupRowBox); |
|
636 groupStack.push(groupRowBox); |
|
637 } |
|
638 |
|
639 function popGroup(){ |
|
640 groupStack.pop(); |
|
641 } |
|
642 |
|
643 // *************************************************************************** |
|
644 |
|
645 function logFormatted(objects, className){ |
|
646 var html = []; |
|
647 |
|
648 var format = objects[0]; |
|
649 var objIndex = 0; |
|
650 |
|
651 if(typeof(format) != "string"){ |
|
652 format = ""; |
|
653 objIndex = -1; |
|
654 } |
|
655 |
|
656 var parts = parseFormat(format); |
|
657 |
|
658 for(var i = 0; i < parts.length; ++i){ |
|
659 var part = parts[i]; |
|
660 if(part && typeof part == "object"){ |
|
661 part.appender(objects[++objIndex], html); |
|
662 }else{ |
|
663 appendText(part, html); |
|
664 } |
|
665 } |
|
666 |
|
667 |
|
668 var ids = []; |
|
669 var obs = []; |
|
670 for(i = objIndex+1; i < objects.length; ++i){ |
|
671 appendText(" ", html); |
|
672 |
|
673 var object = objects[i]; |
|
674 if(object === undefined || object === null ){ |
|
675 appendNull(object, html); |
|
676 |
|
677 }else if(typeof(object) == "string"){ |
|
678 appendText(object, html); |
|
679 |
|
680 }else if(object instanceof Date){ |
|
681 appendText(object.toString(), html); |
|
682 |
|
683 }else if(object.nodeType == 9){ |
|
684 appendText("[ XmlDoc ]", html); |
|
685 |
|
686 }else{ |
|
687 // Create link for object inspector |
|
688 // need to create an ID for this link, since it is currently text |
|
689 var id = "_a" + __consoleAnchorId__++; |
|
690 ids.push(id); |
|
691 // need to save the object, so the arrays line up |
|
692 obs.push(object); |
|
693 var str = '<a id="'+id+'" href="javascript:void(0);">'+getObjectAbbr(object)+'</a>'; |
|
694 |
|
695 appendLink( str , html); |
|
696 } |
|
697 } |
|
698 |
|
699 logRow(html, className); |
|
700 |
|
701 // Now that the row is inserted in the DOM, loop through all of the links that were just created |
|
702 for(i=0; i<ids.length; i++){ |
|
703 var btn = _firebugDoc.getElementById(ids[i]); |
|
704 if(!btn){ continue; } |
|
705 |
|
706 // store the object in the dom btn for reference later |
|
707 // avoid parsing these objects unless necessary |
|
708 btn.obj = obs[i]; |
|
709 |
|
710 _firebugWin.console._connects.push(dojo.connect(btn, "onclick", function(){ |
|
711 |
|
712 console.openObjectInspector(); |
|
713 |
|
714 try{ |
|
715 printObject(this.obj); |
|
716 }catch(e){ |
|
717 this.obj = e; |
|
718 } |
|
719 consoleObjectInspector.innerHTML = "<pre>" + printObject( this.obj ) + "</pre>"; |
|
720 })); |
|
721 } |
|
722 } |
|
723 |
|
724 function parseFormat(format){ |
|
725 var parts = []; |
|
726 |
|
727 var reg = /((^%|[^\\]%)(\d+)?(\.)([a-zA-Z]))|((^%|[^\\]%)([a-zA-Z]))/; |
|
728 var appenderMap = {s: appendText, d: appendInteger, i: appendInteger, f: appendFloat}; |
|
729 |
|
730 for(var m = reg.exec(format); m; m = reg.exec(format)){ |
|
731 var type = m[8] ? m[8] : m[5]; |
|
732 var appender = type in appenderMap ? appenderMap[type] : appendObject; |
|
733 var precision = m[3] ? parseInt(m[3]) : (m[4] == "." ? -1 : 0); |
|
734 |
|
735 parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index+1)); |
|
736 parts.push({appender: appender, precision: precision}); |
|
737 |
|
738 format = format.substr(m.index+m[0].length); |
|
739 } |
|
740 |
|
741 parts.push(format); |
|
742 |
|
743 return parts; |
|
744 } |
|
745 |
|
746 function escapeHTML(value){ |
|
747 function replaceChars(ch){ |
|
748 switch(ch){ |
|
749 case "<": |
|
750 return "<"; |
|
751 case ">": |
|
752 return ">"; |
|
753 case "&": |
|
754 return "&"; |
|
755 case "'": |
|
756 return "'"; |
|
757 case '"': |
|
758 return """; |
|
759 } |
|
760 return "?"; |
|
761 } |
|
762 return String(value).replace(/[<>&"']/g, replaceChars); |
|
763 } |
|
764 |
|
765 function objectToString(object){ |
|
766 try{ |
|
767 return object+""; |
|
768 }catch(e){ |
|
769 return null; |
|
770 } |
|
771 } |
|
772 |
|
773 // *************************************************************************** |
|
774 function appendLink(object, html){ |
|
775 // needed for object links - no HTML escaping |
|
776 html.push( objectToString(object) ); |
|
777 } |
|
778 |
|
779 function appendText(object, html){ |
|
780 html.push(escapeHTML(objectToString(object))); |
|
781 } |
|
782 |
|
783 function appendNull(object, html){ |
|
784 html.push('<span class="objectBox-null">', escapeHTML(objectToString(object)), '</span>'); |
|
785 } |
|
786 |
|
787 function appendString(object, html){ |
|
788 html.push('<span class="objectBox-string">"', escapeHTML(objectToString(object)), |
|
789 '"</span>'); |
|
790 } |
|
791 |
|
792 function appendInteger(object, html){ |
|
793 html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>'); |
|
794 } |
|
795 |
|
796 function appendFloat(object, html){ |
|
797 html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>'); |
|
798 } |
|
799 |
|
800 function appendFunction(object, html){ |
|
801 html.push('<span class="objectBox-function">', getObjectAbbr(object), '</span>'); |
|
802 } |
|
803 |
|
804 function appendObject(object, html){ |
|
805 try{ |
|
806 if(object === undefined){ |
|
807 appendNull("undefined", html); |
|
808 }else if(object === null){ |
|
809 appendNull("null", html); |
|
810 }else if(typeof object == "string"){ |
|
811 appendString(object, html); |
|
812 }else if(typeof object == "number"){ |
|
813 appendInteger(object, html); |
|
814 }else if(typeof object == "function"){ |
|
815 appendFunction(object, html); |
|
816 }else if(object.nodeType == 1){ |
|
817 appendSelector(object, html); |
|
818 }else if(typeof object == "object"){ |
|
819 appendObjectFormatted(object, html); |
|
820 }else{ |
|
821 appendText(object, html); |
|
822 } |
|
823 }catch(e){ |
|
824 /* squelch */ |
|
825 } |
|
826 } |
|
827 |
|
828 function appendObjectFormatted(object, html){ |
|
829 var text = objectToString(object); |
|
830 var reObject = /\[object (.*?)\]/; |
|
831 |
|
832 var m = reObject.exec(text); |
|
833 html.push('<span class="objectBox-object">', m ? m[1] : text, '</span>'); |
|
834 } |
|
835 |
|
836 function appendSelector(object, html){ |
|
837 html.push('<span class="objectBox-selector">'); |
|
838 |
|
839 html.push('<span class="selectorTag">', escapeHTML(object.nodeName.toLowerCase()), '</span>'); |
|
840 if(object.id){ |
|
841 html.push('<span class="selectorId">#', escapeHTML(object.id), '</span>'); |
|
842 } |
|
843 if(object.className){ |
|
844 html.push('<span class="selectorClass">.', escapeHTML(object.className), '</span>'); |
|
845 } |
|
846 |
|
847 html.push('</span>'); |
|
848 } |
|
849 |
|
850 function appendNode(node, html){ |
|
851 if(node.nodeType == 1){ |
|
852 html.push( |
|
853 '<div class="objectBox-element">', |
|
854 '<<span class="nodeTag">', node.nodeName.toLowerCase(), '</span>'); |
|
855 |
|
856 for(var i = 0; i < node.attributes.length; ++i){ |
|
857 var attr = node.attributes[i]; |
|
858 if(!attr.specified){ continue; } |
|
859 |
|
860 html.push(' <span class="nodeName">', attr.nodeName.toLowerCase(), |
|
861 '</span>="<span class="nodeValue">', escapeHTML(attr.nodeValue), |
|
862 '</span>"'); |
|
863 } |
|
864 |
|
865 if(node.firstChild){ |
|
866 html.push('></div><div class="nodeChildren">'); |
|
867 |
|
868 for(var child = node.firstChild; child; child = child.nextSibling){ |
|
869 appendNode(child, html); |
|
870 } |
|
871 |
|
872 html.push('</div><div class="objectBox-element"></<span class="nodeTag">', |
|
873 node.nodeName.toLowerCase(), '></span></div>'); |
|
874 }else{ |
|
875 html.push('/></div>'); |
|
876 } |
|
877 }else if (node.nodeType == 3){ |
|
878 html.push('<div class="nodeText">', escapeHTML(node.nodeValue), |
|
879 '</div>'); |
|
880 } |
|
881 } |
|
882 |
|
883 // *************************************************************************** |
|
884 |
|
885 function addEvent(object, name, handler){ |
|
886 if(document.all){ |
|
887 object.attachEvent("on"+name, handler); |
|
888 }else{ |
|
889 object.addEventListener(name, handler, false); |
|
890 } |
|
891 } |
|
892 |
|
893 function removeEvent(object, name, handler){ |
|
894 if(document.all){ |
|
895 object.detachEvent("on"+name, handler); |
|
896 }else{ |
|
897 object.removeEventListener(name, handler, false); |
|
898 } |
|
899 } |
|
900 |
|
901 function cancelEvent(event){ |
|
902 if(document.all){ |
|
903 event.cancelBubble = true; |
|
904 }else{ |
|
905 event.stopPropagation(); |
|
906 } |
|
907 } |
|
908 |
|
909 function onError(msg, href, lineNo){ |
|
910 var lastSlash = href.lastIndexOf("/"); |
|
911 var fileName = lastSlash == -1 ? href : href.substr(lastSlash+1); |
|
912 |
|
913 var html = [ |
|
914 '<span class="errorMessage">', msg, '</span>', |
|
915 '<div class="objectBox-sourceLink">', fileName, ' (line ', lineNo, ')</div>' |
|
916 ]; |
|
917 |
|
918 logRow(html, "error"); |
|
919 } |
|
920 |
|
921 |
|
922 //After converting to div instead of iframe, now getting two keydowns right away in IE 6. |
|
923 //Make sure there is a little bit of delay. |
|
924 var onKeyDownTime = new Date().getTime(); |
|
925 |
|
926 function onKeyDown(event){ |
|
927 var timestamp = (new Date()).getTime(); |
|
928 if(timestamp > onKeyDownTime + 200){ |
|
929 event = dojo.fixEvent(event); |
|
930 var keys = dojo.keys; |
|
931 var ekc = event.keyCode; |
|
932 onKeyDownTime = timestamp; |
|
933 if(ekc == keys.F12){ |
|
934 toggleConsole(); |
|
935 }else if( |
|
936 (ekc == keys.NUMPAD_ENTER || ekc == 76) && |
|
937 event.shiftKey && |
|
938 (event.metaKey || event.ctrlKey) |
|
939 ){ |
|
940 focusCommandLine(); |
|
941 }else{ |
|
942 return; |
|
943 } |
|
944 cancelEvent(event); |
|
945 } |
|
946 } |
|
947 |
|
948 function onCommandLineKeyDown(e){ |
|
949 var dk = dojo.keys; |
|
950 if(e.keyCode == 13 && commandLine.value){ |
|
951 addToHistory(commandLine.value); |
|
952 evalCommandLine(); |
|
953 }else if(e.keyCode == 27){ |
|
954 commandLine.value = ""; |
|
955 }else if(e.keyCode == dk.UP_ARROW || e.charCode == dk.UP_ARROW){ |
|
956 navigateHistory("older"); |
|
957 }else if(e.keyCode == dk.DOWN_ARROW || e.charCode == dk.DOWN_ARROW){ |
|
958 navigateHistory("newer"); |
|
959 }else if(e.keyCode == dk.HOME || e.charCode == dk.HOME){ |
|
960 historyPosition = 1; |
|
961 navigateHistory("older"); |
|
962 }else if(e.keyCode == dk.END || e.charCode == dk.END){ |
|
963 historyPosition = 999999; |
|
964 navigateHistory("newer"); |
|
965 } |
|
966 } |
|
967 |
|
968 var historyPosition = -1; |
|
969 var historyCommandLine = null; |
|
970 |
|
971 function addToHistory(value){ |
|
972 var history = cookie("firebug_history"); |
|
973 history = (history) ? dojo.fromJson(history) : []; |
|
974 var pos = dojo.indexOf(history, value); |
|
975 if (pos != -1){ |
|
976 history.splice(pos, 1); |
|
977 } |
|
978 history.push(value); |
|
979 cookie("firebug_history", dojo.toJson(history), 30); |
|
980 while(history.length && !cookie("firebug_history")){ |
|
981 history.shift(); |
|
982 cookie("firebug_history", dojo.toJson(history), 30); |
|
983 } |
|
984 historyCommandLine = null; |
|
985 historyPosition = -1; |
|
986 } |
|
987 |
|
988 function navigateHistory(direction){ |
|
989 var history = cookie("firebug_history"); |
|
990 history = (history) ? dojo.fromJson(history) : []; |
|
991 if(!history.length){ |
|
992 return; |
|
993 } |
|
994 |
|
995 if(historyCommandLine === null){ |
|
996 historyCommandLine = commandLine.value; |
|
997 } |
|
998 |
|
999 if(historyPosition == -1){ |
|
1000 historyPosition = history.length; |
|
1001 } |
|
1002 |
|
1003 if(direction == "older"){ |
|
1004 --historyPosition; |
|
1005 if(historyPosition < 0){ |
|
1006 historyPosition = 0; |
|
1007 } |
|
1008 }else if(direction == "newer"){ |
|
1009 ++historyPosition; |
|
1010 if(historyPosition > history.length){ |
|
1011 historyPosition = history.length; |
|
1012 } |
|
1013 } |
|
1014 |
|
1015 if(historyPosition == history.length){ |
|
1016 commandLine.value = historyCommandLine; |
|
1017 historyCommandLine = null; |
|
1018 }else{ |
|
1019 commandLine.value = history[historyPosition]; |
|
1020 } |
|
1021 } |
|
1022 |
|
1023 function cookie(name, value){ |
|
1024 var c = document.cookie; |
|
1025 if(arguments.length == 1){ |
|
1026 var matches = c.match(new RegExp("(?:^|; )" + name + "=([^;]*)")); |
|
1027 return matches ? decodeURIComponent(matches[1]) : undefined; // String or undefined |
|
1028 }else{ |
|
1029 var d = new Date(); |
|
1030 d.setMonth(d.getMonth()+1); |
|
1031 document.cookie = name + "=" + encodeURIComponent(value) + ((d.toUtcString) ? "; expires=" + d.toUTCString() : ""); |
|
1032 } |
|
1033 } |
|
1034 |
|
1035 function isArray(it){ |
|
1036 return it && it instanceof Array || typeof it == "array"; |
|
1037 } |
|
1038 |
|
1039 //*************************************************************************************************** |
|
1040 // Print Object Helpers |
|
1041 function objectLength(o){ |
|
1042 var cnt = 0; |
|
1043 for(var nm in o){ |
|
1044 cnt++; |
|
1045 } |
|
1046 return cnt; |
|
1047 } |
|
1048 |
|
1049 function printObject(o, i, txt, used){ |
|
1050 // Recursively trace object, indenting to represent depth for display in object inspector |
|
1051 var ind = " \t"; |
|
1052 txt = txt || ""; |
|
1053 i = i || ind; |
|
1054 used = used || []; |
|
1055 var opnCls; |
|
1056 |
|
1057 if(o && o.nodeType == 1){ |
|
1058 var html = []; |
|
1059 appendNode(o, html); |
|
1060 return html.join(""); |
|
1061 } |
|
1062 |
|
1063 var br=",\n", cnt = 0, length = objectLength(o); |
|
1064 |
|
1065 if(o instanceof Date){ |
|
1066 return i + o.toString() + br; |
|
1067 } |
|
1068 looking: |
|
1069 for(var nm in o){ |
|
1070 cnt++; |
|
1071 if(cnt==length){br = "\n";} |
|
1072 if(o[nm] === window || o[nm] === document){ |
|
1073 // do nothing |
|
1074 }else if(o[nm] === null){ |
|
1075 txt += i+nm + " : NULL" + br; |
|
1076 }else if(o[nm] && o[nm].nodeType){ |
|
1077 if(o[nm].nodeType == 1){ |
|
1078 //txt += i+nm + " : < "+o[nm].tagName+" id=\""+ o[nm].id+"\" />" + br; |
|
1079 }else if(o[nm].nodeType == 3){ |
|
1080 txt += i+nm + " : [ TextNode "+o[nm].data + " ]" + br; |
|
1081 } |
|
1082 |
|
1083 }else if(typeof o[nm] == "object" && (o[nm] instanceof String || o[nm] instanceof Number || o[nm] instanceof Boolean)){ |
|
1084 txt += i+nm + " : " + o[nm] + "," + br; |
|
1085 |
|
1086 }else if(o[nm] instanceof Date){ |
|
1087 txt += i+nm + " : " + o[nm].toString() + br; |
|
1088 |
|
1089 }else if(typeof(o[nm]) == "object" && o[nm]){ |
|
1090 for(var j = 0, seen; seen = used[j]; j++){ |
|
1091 if(o[nm] === seen){ |
|
1092 txt += i+nm + " : RECURSION" + br; |
|
1093 continue looking; |
|
1094 } |
|
1095 } |
|
1096 used.push(o[nm]); |
|
1097 |
|
1098 opnCls = (isArray(o[nm]))?["[","]"]:["{","}"]; |
|
1099 txt += i+nm +" : " + opnCls[0] + "\n";//non-standard break, (no comma) |
|
1100 txt += printObject(o[nm], i+ind, "", used); |
|
1101 txt += i + opnCls[1] + br; |
|
1102 |
|
1103 }else if(typeof o[nm] == "undefined"){ |
|
1104 txt += i+nm + " : undefined" + br; |
|
1105 }else if(nm == "toString" && typeof o[nm] == "function"){ |
|
1106 var toString = o[nm](); |
|
1107 if(typeof toString == "string" && toString.match(/function ?(.*?)\(/)){ |
|
1108 toString = escapeHTML(getObjectAbbr(o[nm])); |
|
1109 } |
|
1110 txt += i+nm +" : " + toString + br; |
|
1111 }else{ |
|
1112 txt += i+nm +" : "+ escapeHTML(getObjectAbbr(o[nm])) + br; |
|
1113 } |
|
1114 } |
|
1115 return txt; |
|
1116 } |
|
1117 |
|
1118 function getObjectAbbr(obj){ |
|
1119 // Gets an abbreviation of an object for display in log |
|
1120 // X items in object, including id |
|
1121 // X items in an array |
|
1122 // TODO: Firebug Sr. actually goes by char count |
|
1123 var isError = (obj instanceof Error); |
|
1124 if(obj.nodeType == 1){ |
|
1125 return escapeHTML('< '+obj.tagName.toLowerCase()+' id=\"'+ obj.id+ '\" />'); |
|
1126 } |
|
1127 if(obj.nodeType == 3){ |
|
1128 return escapeHTML('[TextNode: "'+obj.nodeValue+'"]'); |
|
1129 } |
|
1130 var nm = (obj && (obj.id || obj.name || obj.ObjectID || obj.widgetId)); |
|
1131 if(!isError && nm){ return "{"+nm+"}"; } |
|
1132 |
|
1133 var obCnt = 2; |
|
1134 var arCnt = 4; |
|
1135 var cnt = 0; |
|
1136 |
|
1137 if(isError){ |
|
1138 nm = "[ Error: "+(obj.message || obj.description || obj)+" ]"; |
|
1139 }else if(isArray(obj)){ |
|
1140 nm = "[" + obj.slice(0,arCnt).join(","); |
|
1141 if(obj.length > arCnt){ |
|
1142 nm += " ... ("+obj.length+" items)"; |
|
1143 } |
|
1144 nm += "]"; |
|
1145 }else if(typeof obj == "function"){ |
|
1146 nm = obj + ""; |
|
1147 var reg = /function\s*([^\(]*)(\([^\)]*\))[^\{]*\{/; |
|
1148 var m = reg.exec(nm); |
|
1149 if(m){ |
|
1150 if(!m[1]){ |
|
1151 m[1] = "function"; |
|
1152 } |
|
1153 nm = m[1] + m[2]; |
|
1154 }else{ |
|
1155 nm = "function()"; |
|
1156 } |
|
1157 }else if(typeof obj != "object" || typeof obj == "string"){ |
|
1158 nm = obj + ""; |
|
1159 }else{ |
|
1160 nm = "{"; |
|
1161 for(var i in obj){ |
|
1162 cnt++; |
|
1163 if(cnt > obCnt){ break; } |
|
1164 nm += i+":"+escapeHTML(obj[i])+" "; |
|
1165 } |
|
1166 nm+="}"; |
|
1167 } |
|
1168 |
|
1169 return nm; |
|
1170 } |
|
1171 |
|
1172 //************************************************************************************* |
|
1173 |
|
1174 //window.onerror = onError; |
|
1175 |
|
1176 addEvent(document, has("ie") || has("safari") ? "keydown" : "keypress", onKeyDown); |
|
1177 |
|
1178 if( (document.documentElement.getAttribute("debug") == "true")|| |
|
1179 (dojo.config.isDebug) |
|
1180 ){ |
|
1181 toggleConsole(true); |
|
1182 } |
|
1183 |
|
1184 dojo.addOnWindowUnload(function(){ |
|
1185 // Erase the globals and event handlers I created, to prevent spurious leak warnings |
|
1186 removeEvent(document, has("ie") || has("safari") ? "keydown" : "keypress", onKeyDown); |
|
1187 window.onFirebugResize = null; |
|
1188 window.console = null; |
|
1189 }); |
|
1190 |
|
1191 }); |
|