firebug.js 33 KB

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