source: Dev/trunk/src/client/dojo/_firebug/firebug.js @ 531

Last change on this file since 531 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 32.5 KB
Line 
1define([
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, "&nbsp;&nbsp;&nbsp;&nbsp;");
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 "&lt;";
751                                case ">":
752                                        return "&gt;";
753                                case "&":
754                                        return "&amp;";
755                                case "'":
756                                        return "&#39;";
757                                case '"':
758                                        return "&quot;";
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">&quot;', escapeHTML(objectToString(object)),
789                        '&quot;</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                                        '&lt;<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('&nbsp;<span class="nodeName">', attr.nodeName.toLowerCase(),
861                                        '</span>=&quot;<span class="nodeValue">', escapeHTML(attr.nodeValue),
862                                        '</span>&quot;');
863                        }
864
865                        if(node.firstChild){
866                                html.push('&gt;</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">&lt;/<span class="nodeTag">',
873                                        node.nodeName.toLowerCase(), '&gt;</span></div>');
874                        }else{
875                                html.push('/&gt;</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});
Note: See TracBrowser for help on using the repository browser.