firebug.js 34 KB

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