nexus site path corrected
[portal.git] / ecomp-portal-FE / client / bower_components / lodash / vendor / firebug-lite / src / firebug-lite-debug.js
1 (function(){
2
3 /*!*************************************************************
4  *
5  *    Firebug Lite 1.4.0
6  *
7  *      Copyright (c) 2007, Parakey Inc.
8  *      Released under BSD license.
9  *      More information: http://getfirebug.com/firebuglite
10  *
11  **************************************************************/
12
13 /*!
14  * CSS selectors powered by:
15  *
16  * Sizzle CSS Selector Engine - v1.0
17  *  Copyright 2009, The Dojo Foundation
18  *  Released under the MIT, BSD, and GPL Licenses.
19  *  More information: http://sizzlejs.com/
20  */
21
22 /** @namespace describe lib */
23
24 // FIXME: xxxpedro if we use "var FBL = {}" the FBL won't appear in the DOM Panel in IE
25 var FBL = {};
26
27 ( /** @scope s_lib @this FBL */ function() {
28 // ************************************************************************************************
29
30 // ************************************************************************************************
31 // Constants
32
33 var productionDir = "http://getfirebug.com/releases/lite/";
34 var bookmarkletVersion = 4;
35
36 // ************************************************************************************************
37
38 var reNotWhitespace = /[^\s]/;
39 var reSplitFile = /:\/{1,3}(.*?)\/([^\/]*?)\/?($|\?.*)/;
40
41 // Globals
42 this.reJavascript = /\s*javascript:\s*(.*)/;
43 this.reChrome = /chrome:\/\/([^\/]*)\//;
44 this.reFile = /file:\/\/([^\/]*)\//;
45
46
47 // ************************************************************************************************
48 // properties
49
50 var userAgent = navigator.userAgent.toLowerCase();
51 this.isFirefox = /firefox/.test(userAgent);
52 this.isOpera   = /opera/.test(userAgent);
53 this.isSafari  = /webkit/.test(userAgent);
54 this.isIE      = /msie/.test(userAgent) && !/opera/.test(userAgent);
55 this.isIE6     = /msie 6/i.test(navigator.appVersion);
56 this.browserVersion = (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1];
57 this.isIElt8   = this.isIE && (this.browserVersion-0 < 8);
58
59 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
60
61 this.NS = null;
62 this.pixelsPerInch = null;
63
64
65 // ************************************************************************************************
66 // Namespaces
67
68 var namespaces = [];
69
70 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
71
72 this.ns = function(fn)
73 {
74     var ns = {};
75     namespaces.push(fn, ns);
76     return ns;
77 };
78
79 var FBTrace = null;
80
81 this.initialize = function()
82 {
83     // Firebug Lite is already running in persistent mode so we just quit
84     if (window.firebug && firebug.firebuglite || window.console && console.firebuglite)
85         return;
86
87     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
88     // initialize environment
89
90     // point the FBTrace object to the local variable
91     if (FBL.FBTrace)
92         FBTrace = FBL.FBTrace;
93     else
94         FBTrace = FBL.FBTrace = {};
95
96     // check if the actual window is a persisted chrome context
97     var isChromeContext = window.Firebug && typeof window.Firebug.SharedEnv == "object";
98
99     // chrome context of the persistent application
100     if (isChromeContext)
101     {
102         // TODO: xxxpedro persist - make a better synchronization
103         sharedEnv = window.Firebug.SharedEnv;
104         delete window.Firebug.SharedEnv;
105
106         FBL.Env = sharedEnv;
107         FBL.Env.isChromeContext = true;
108         FBTrace.messageQueue = FBL.Env.traceMessageQueue;
109     }
110     // non-persistent application
111     else
112     {
113         FBL.NS = document.documentElement.namespaceURI;
114         FBL.Env.browser = window;
115         FBL.Env.destroy = destroyEnvironment;
116
117         if (document.documentElement.getAttribute("debug") == "true")
118             FBL.Env.Options.startOpened = true;
119
120         // find the URL location of the loaded application
121         findLocation();
122
123         // TODO: get preferences here...
124         // The problem is that we don't have the Firebug object yet, so we can't use
125         // Firebug.loadPrefs. We're using the Store module directly instead.
126         var prefs = FBL.Store.get("FirebugLite") || {};
127         FBL.Env.DefaultOptions = FBL.Env.Options;
128         FBL.Env.Options = FBL.extend(FBL.Env.Options, prefs.options || {});
129
130         if (FBL.isFirefox &&
131             typeof FBL.Env.browser.console == "object" &&
132             FBL.Env.browser.console.firebug &&
133             FBL.Env.Options.disableWhenFirebugActive)
134                 return;
135     }
136
137     // exposes the FBL to the global namespace when in debug mode
138     if (FBL.Env.isDebugMode)
139     {
140         FBL.Env.browser.FBL = FBL;
141     }
142
143     // check browser compatibilities
144     this.isQuiksMode = FBL.Env.browser.document.compatMode == "BackCompat";
145     this.isIEQuiksMode = this.isIE && this.isQuiksMode;
146     this.isIEStantandMode = this.isIE && !this.isQuiksMode;
147
148     this.noFixedPosition = this.isIE6 || this.isIEQuiksMode;
149
150     // after creating/synchronizing the environment, initialize the FBTrace module
151     if (FBL.Env.Options.enableTrace) FBTrace.initialize();
152
153     if (FBTrace.DBG_INITIALIZE && isChromeContext) FBTrace.sysout("FBL.initialize - persistent application", "initialize chrome context");
154
155     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
156     // initialize namespaces
157
158     if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("FBL.initialize", namespaces.length/2+" namespaces BEGIN");
159
160     for (var i = 0; i < namespaces.length; i += 2)
161     {
162         var fn = namespaces[i];
163         var ns = namespaces[i+1];
164         fn.apply(ns);
165     }
166
167     if (FBTrace.DBG_INITIALIZE) {
168         FBTrace.sysout("FBL.initialize", namespaces.length/2+" namespaces END");
169         FBTrace.sysout("FBL waitForDocument", "waiting document load");
170     }
171
172     FBL.Ajax.initialize();
173
174     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
175     // finish environment initialization
176     FBL.Firebug.loadPrefs();
177
178     if (FBL.Env.Options.enablePersistent)
179     {
180         // TODO: xxxpedro persist - make a better synchronization
181         if (isChromeContext)
182         {
183             FBL.FirebugChrome.clone(FBL.Env.FirebugChrome);
184         }
185         else
186         {
187             FBL.Env.FirebugChrome = FBL.FirebugChrome;
188             FBL.Env.traceMessageQueue = FBTrace.messageQueue;
189         }
190     }
191
192     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
193     // wait document load
194
195     waitForDocument();
196 };
197
198 var waitForDocument = function waitForDocument()
199 {
200     // document.body not available in XML+XSL documents in Firefox
201     var doc = FBL.Env.browser.document;
202     var body = doc.getElementsByTagName("body")[0];
203
204     if (body)
205     {
206         calculatePixelsPerInch(doc, body);
207         onDocumentLoad();
208     }
209     else
210         setTimeout(waitForDocument, 50);
211 };
212
213 var onDocumentLoad = function onDocumentLoad()
214 {
215     if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("FBL onDocumentLoad", "document loaded");
216
217     // fix IE6 problem with cache of background images, causing a lot of flickering
218     if (FBL.isIE6)
219         fixIE6BackgroundImageCache();
220
221     // chrome context of the persistent application
222     if (FBL.Env.Options.enablePersistent && FBL.Env.isChromeContext)
223     {
224         // finally, start the application in the chrome context
225         FBL.Firebug.initialize();
226
227         // if is not development mode, remove the shared environment cache object
228         // used to synchronize the both persistent contexts
229         if (!FBL.Env.isDevelopmentMode)
230         {
231             sharedEnv.destroy();
232             sharedEnv = null;
233         }
234     }
235     // non-persistent application
236     else
237     {
238         FBL.FirebugChrome.create();
239     }
240 };
241
242 // ************************************************************************************************
243 // Env
244
245 var sharedEnv;
246
247 this.Env =
248 {
249     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
250     // Env Options (will be transported to Firebug options)
251     Options:
252     {
253         saveCookies: true,
254
255         saveWindowPosition: false,
256         saveCommandLineHistory: false,
257
258         startOpened: false,
259         startInNewWindow: false,
260         showIconWhenHidden: true,
261
262         overrideConsole: true,
263         ignoreFirebugElements: true,
264         disableWhenFirebugActive: true,
265
266         disableXHRListener: false,
267         disableResourceFetching: false,
268
269         enableTrace: false,
270         enablePersistent: false
271
272     },
273
274     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
275     // Library location
276     Location:
277     {
278         sourceDir: null,
279         baseDir: null,
280         skinDir: null,
281         skin: null,
282         app: null
283     },
284
285     skin: "xp",
286     useLocalSkin: false,
287
288     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
289     // Env states
290     isDevelopmentMode: false,
291     isDebugMode: false,
292     isChromeContext: false,
293
294     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
295     // Env references
296     browser: null,
297     chrome: null
298 };
299
300 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
301
302 var destroyEnvironment = function destroyEnvironment()
303 {
304     setTimeout(function()
305     {
306         FBL = null;
307     }, 100);
308 };
309
310 // ************************************************************************************************
311 // Library location
312
313 var findLocation =  function findLocation()
314 {
315     var reFirebugFile = /(firebug-lite(?:-\w+)?(?:\.js|\.jgz))(?:#(.+))?$/;
316     var reGetFirebugSite = /(?:http|https):\/\/getfirebug.com\//;
317     var isGetFirebugSite;
318
319     var rePath = /^(.*\/)/;
320     var reProtocol = /^\w+:\/\//;
321     var path = null;
322     var doc = document;
323
324     // Firebug Lite 1.3.0 bookmarklet identification
325     var script = doc.getElementById("FirebugLite");
326
327     var scriptSrc;
328     var hasSrcAttribute = true;
329
330     // If the script was loaded via bookmarklet, we already have the script tag
331     if (script)
332     {
333         scriptSrc = script.src;
334         file = reFirebugFile.exec(scriptSrc);
335
336         var version = script.getAttribute("FirebugLite");
337         var number = version ? parseInt(version) : 0;
338
339         if (!version || !number || number < bookmarkletVersion)
340         {
341             FBL.Env.bookmarkletOutdated = true;
342         }
343     }
344     // otherwise we must search for the correct script tag
345     else
346     {
347         for(var i=0, s=doc.getElementsByTagName("script"), si; si=s[i]; i++)
348         {
349             var file = null;
350             if ( si.nodeName.toLowerCase() == "script" )
351             {
352                 if (file = reFirebugFile.exec(si.getAttribute("firebugSrc")))
353                 {
354                     scriptSrc = si.getAttribute("firebugSrc");
355                     hasSrcAttribute = false;
356                 }
357                 else if (file = reFirebugFile.exec(si.src))
358                 {
359                     scriptSrc = si.src;
360                 }
361                 else
362                     continue;
363
364                 script = si;
365                 break;
366             }
367         }
368     }
369
370     // mark the script tag to be ignored by Firebug Lite
371     if (script)
372         script.firebugIgnore = true;
373
374     if (file)
375     {
376         var fileName = file[1];
377         var fileOptions = file[2];
378
379         // absolute path
380         if (reProtocol.test(scriptSrc)) {
381             path = rePath.exec(scriptSrc)[1];
382
383         }
384         // relative path
385         else
386         {
387             var r = rePath.exec(scriptSrc);
388             var src = r ? r[1] : scriptSrc;
389             var backDir = /^((?:\.\.\/)+)(.*)/.exec(src);
390             var reLastDir = /^(.*\/)[^\/]+\/$/;
391             path = rePath.exec(location.href)[1];
392
393             // "../some/path"
394             if (backDir)
395             {
396                 var j = backDir[1].length/3;
397                 var p;
398                 while (j-- > 0)
399                     path = reLastDir.exec(path)[1];
400
401                 path += backDir[2];
402             }
403
404             else if(src.indexOf("/") != -1)
405             {
406                 // "./some/path"
407                 if(/^\.\/./.test(src))
408                 {
409                     path += src.substring(2);
410                 }
411                 // "/some/path"
412                 else if(/^\/./.test(src))
413                 {
414                     var domain = /^(\w+:\/\/[^\/]+)/.exec(path);
415                     path = domain[1] + src;
416                 }
417                 // "some/path"
418                 else
419                 {
420                     path += src;
421                 }
422             }
423         }
424     }
425
426     FBL.Env.isChromeExtension = script && script.getAttribute("extension") == "Chrome";
427     if (FBL.Env.isChromeExtension)
428     {
429         path = productionDir;
430         FBL.Env.bookmarkletOutdated = false;
431         script = {innerHTML: "{showIconWhenHidden:false}"};
432     }
433
434     isGetFirebugSite = reGetFirebugSite.test(path);
435
436     if (isGetFirebugSite && path.indexOf("/releases/lite/") == -1)
437     {
438         // See Issue 4587 - If we are loading the script from getfirebug.com shortcut, like
439         // https://getfirebug.com/firebug-lite.js, then we must manually add the full path,
440         // otherwise the Env.Location will hold the wrong path, which will in turn lead to
441         // undesirable effects like the problem in Issue 4587
442         path += "releases/lite/" + (fileName == "firebug-lite-beta.js" ? "beta/" : "latest/");
443     }
444
445     var m = path && path.match(/([^\/]+)\/$/) || null;
446
447     if (path && m)
448     {
449         var Env = FBL.Env;
450
451         // Always use the local skin when running in the same domain
452         // See Issue 3554: Firebug Lite should use local images when loaded locally
453         Env.useLocalSkin = path.indexOf(location.protocol + "//" + location.host + "/") == 0 &&
454                 // but we cannot use the locan skin when loaded from getfirebug.com, otherwise
455                 // the bookmarklet won't work when visiting getfirebug.com
456                 !isGetFirebugSite;
457
458         // detecting development and debug modes via file name
459         if (fileName == "firebug-lite-dev.js")
460         {
461             Env.isDevelopmentMode = true;
462             Env.isDebugMode = true;
463         }
464         else if (fileName == "firebug-lite-debug.js")
465         {
466             Env.isDebugMode = true;
467         }
468
469         // process the <html debug="true">
470         if (Env.browser.document.documentElement.getAttribute("debug") == "true")
471         {
472             Env.Options.startOpened = true;
473         }
474
475         // process the Script URL Options
476         if (fileOptions)
477         {
478             var options = fileOptions.split(",");
479
480             for (var i = 0, length = options.length; i < length; i++)
481             {
482                 var option = options[i];
483                 var name, value;
484
485                 if (option.indexOf("=") != -1)
486                 {
487                     var parts = option.split("=");
488                     name = parts[0];
489                     value = eval(unescape(parts[1]));
490                 }
491                 else
492                 {
493                     name = option;
494                     value = true;
495                 }
496
497                 if (name == "debug")
498                 {
499                     Env.isDebugMode = !!value;
500                 }
501                 else if (name in Env.Options)
502                 {
503                     Env.Options[name] = value;
504                 }
505                 else
506                 {
507                     Env[name] = value;
508                 }
509             }
510         }
511
512         // process the Script JSON Options
513         if (hasSrcAttribute)
514         {
515             var innerOptions = FBL.trim(script.innerHTML);
516             if (innerOptions)
517             {
518                 var innerOptionsObject = eval("(" + innerOptions + ")");
519
520                 for (var name in innerOptionsObject)
521                 {
522                     var value = innerOptionsObject[name];
523
524                     if (name == "debug")
525                     {
526                         Env.isDebugMode = !!value;
527                     }
528                     else if (name in Env.Options)
529                     {
530                         Env.Options[name] = value;
531                     }
532                     else
533                     {
534                         Env[name] = value;
535                     }
536                 }
537             }
538         }
539
540         if (!Env.Options.saveCookies)
541             FBL.Store.remove("FirebugLite");
542
543         // process the Debug Mode
544         if (Env.isDebugMode)
545         {
546             Env.Options.startOpened = true;
547             Env.Options.enableTrace = true;
548             Env.Options.disableWhenFirebugActive = false;
549         }
550
551         var loc = Env.Location;
552         var isProductionRelease = path.indexOf(productionDir) != -1;
553
554         loc.sourceDir = path;
555         loc.baseDir = path.substr(0, path.length - m[1].length - 1);
556         loc.skinDir = (isProductionRelease ? path : loc.baseDir) + "skin/" + Env.skin + "/";
557         loc.skin = loc.skinDir + "firebug.html";
558         loc.app = path + fileName;
559     }
560     else
561     {
562         throw new Error("Firebug Error: Library path not found");
563     }
564 };
565
566 // ************************************************************************************************
567 // Basics
568
569 this.bind = function()  // fn, thisObject, args => thisObject.fn(args, arguments);
570 {
571    var args = cloneArray(arguments), fn = args.shift(), object = args.shift();
572    return function() { return fn.apply(object, arrayInsert(cloneArray(args), 0, arguments)); };
573 };
574
575 this.bindFixed = function() // fn, thisObject, args => thisObject.fn(args);
576 {
577     var args = cloneArray(arguments), fn = args.shift(), object = args.shift();
578     return function() { return fn.apply(object, args); };
579 };
580
581 this.extend = function(l, r)
582 {
583     var newOb = {};
584     for (var n in l)
585         newOb[n] = l[n];
586     for (var n in r)
587         newOb[n] = r[n];
588     return newOb;
589 };
590
591 this.descend = function(prototypeParent, childProperties)
592 {
593     function protoSetter() {};
594     protoSetter.prototype = prototypeParent;
595     var newOb = new protoSetter();
596     for (var n in childProperties)
597         newOb[n] = childProperties[n];
598     return newOb;
599 };
600
601 this.append = function(l, r)
602 {
603     for (var n in r)
604         l[n] = r[n];
605
606     return l;
607 };
608
609 this.keys = function(map)  // At least sometimes the keys will be on user-level window objects
610 {
611     var keys = [];
612     try
613     {
614         for (var name in map)  // enumeration is safe
615             keys.push(name);   // name is string, safe
616     }
617     catch (exc)
618     {
619         // Sometimes we get exceptions trying to iterate properties
620     }
621
622     return keys;  // return is safe
623 };
624
625 this.values = function(map)
626 {
627     var values = [];
628     try
629     {
630         for (var name in map)
631         {
632             try
633             {
634                 values.push(map[name]);
635             }
636             catch (exc)
637             {
638                 // Sometimes we get exceptions trying to access properties
639                 if (FBTrace.DBG_ERRORS)
640                     FBTrace.sysout("lib.values FAILED ", exc);
641             }
642
643         }
644     }
645     catch (exc)
646     {
647         // Sometimes we get exceptions trying to iterate properties
648         if (FBTrace.DBG_ERRORS)
649             FBTrace.sysout("lib.values FAILED ", exc);
650     }
651
652     return values;
653 };
654
655 this.remove = function(list, item)
656 {
657     for (var i = 0; i < list.length; ++i)
658     {
659         if (list[i] == item)
660         {
661             list.splice(i, 1);
662             break;
663         }
664     }
665 };
666
667 this.sliceArray = function(array, index)
668 {
669     var slice = [];
670     for (var i = index; i < array.length; ++i)
671         slice.push(array[i]);
672
673     return slice;
674 };
675
676 function cloneArray(array, fn)
677 {
678    var newArray = [];
679
680    if (fn)
681        for (var i = 0; i < array.length; ++i)
682            newArray.push(fn(array[i]));
683    else
684        for (var i = 0; i < array.length; ++i)
685            newArray.push(array[i]);
686
687    return newArray;
688 }
689
690 function extendArray(array, array2)
691 {
692    var newArray = [];
693    newArray.push.apply(newArray, array);
694    newArray.push.apply(newArray, array2);
695    return newArray;
696 }
697
698 this.extendArray = extendArray;
699 this.cloneArray = cloneArray;
700
701 function arrayInsert(array, index, other)
702 {
703    for (var i = 0; i < other.length; ++i)
704        array.splice(i+index, 0, other[i]);
705
706    return array;
707 }
708
709 // ************************************************************************************************
710
711 this.createStyleSheet = function(doc, url)
712 {
713     //TODO: xxxpedro
714     //var style = doc.createElementNS("http://www.w3.org/1999/xhtml", "style");
715     var style = this.createElement("link");
716     style.setAttribute("charset","utf-8");
717     style.firebugIgnore = true;
718     style.setAttribute("rel", "stylesheet");
719     style.setAttribute("type", "text/css");
720     style.setAttribute("href", url);
721
722     //TODO: xxxpedro
723     //style.innerHTML = this.getResource(url);
724     return style;
725 };
726
727 this.addStyleSheet = function(doc, style)
728 {
729     var heads = doc.getElementsByTagName("head");
730     if (heads.length)
731         heads[0].appendChild(style);
732     else
733         doc.documentElement.appendChild(style);
734 };
735
736 this.appendStylesheet = function(doc, uri)
737 {
738     // Make sure the stylesheet is not appended twice.
739     if (this.$(uri, doc))
740         return;
741
742     var styleSheet = this.createStyleSheet(doc, uri);
743     styleSheet.setAttribute("id", uri);
744     this.addStyleSheet(doc, styleSheet);
745 };
746
747 this.addScript = function(doc, id, src)
748 {
749     var element = doc.createElementNS("http://www.w3.org/1999/xhtml", "html:script");
750     element.setAttribute("type", "text/javascript");
751     element.setAttribute("id", id);
752     if (!FBTrace.DBG_CONSOLE)
753         FBL.unwrapObject(element).firebugIgnore = true;
754
755     element.innerHTML = src;
756     if (doc.documentElement)
757         doc.documentElement.appendChild(element);
758     else
759     {
760         // See issue 1079, the svg test case gives this error
761         if (FBTrace.DBG_ERRORS)
762             FBTrace.sysout("lib.addScript doc has no documentElement:", doc);
763     }
764     return element;
765 };
766
767
768 // ************************************************************************************************
769
770 this.getStyle = this.isIE ?
771     function(el, name)
772     {
773         return el.currentStyle[name] || el.style[name] || undefined;
774     }
775     :
776     function(el, name)
777     {
778         return el.ownerDocument.defaultView.getComputedStyle(el,null)[name]
779             || el.style[name] || undefined;
780     };
781
782
783 // ************************************************************************************************
784 // Whitespace and Entity conversions
785
786 var entityConversionLists = this.entityConversionLists = {
787     normal : {
788         whitespace : {
789             '\t' : '\u200c\u2192',
790             '\n' : '\u200c\u00b6',
791             '\r' : '\u200c\u00ac',
792             ' '  : '\u200c\u00b7'
793         }
794     },
795     reverse : {
796         whitespace : {
797             '&Tab;' : '\t',
798             '&NewLine;' : '\n',
799             '\u200c\u2192' : '\t',
800             '\u200c\u00b6' : '\n',
801             '\u200c\u00ac' : '\r',
802             '\u200c\u00b7' : ' '
803         }
804     }
805 };
806
807 var normal = entityConversionLists.normal,
808     reverse = entityConversionLists.reverse;
809
810 function addEntityMapToList(ccode, entity)
811 {
812     var lists = Array.prototype.slice.call(arguments, 2),
813         len = lists.length,
814         ch = String.fromCharCode(ccode);
815     for (var i = 0; i < len; i++)
816     {
817         var list = lists[i];
818         normal[list]=normal[list] || {};
819         normal[list][ch] = '&' + entity + ';';
820         reverse[list]=reverse[list] || {};
821         reverse[list]['&' + entity + ';'] = ch;
822     }
823 };
824
825 var e = addEntityMapToList,
826     white = 'whitespace',
827     text = 'text',
828     attr = 'attributes',
829     css = 'css',
830     editor = 'editor';
831
832 e(0x0022, 'quot', attr, css);
833 e(0x0026, 'amp', attr, text, css);
834 e(0x0027, 'apos', css);
835 e(0x003c, 'lt', attr, text, css);
836 e(0x003e, 'gt', attr, text, css);
837 e(0xa9, 'copy', text, editor);
838 e(0xae, 'reg', text, editor);
839 e(0x2122, 'trade', text, editor);
840
841 // See http://en.wikipedia.org/wiki/Dash
842 e(0x2012, '#8210', attr, text, editor); // figure dash
843 e(0x2013, 'ndash', attr, text, editor); // en dash
844 e(0x2014, 'mdash', attr, text, editor); // em dash
845 e(0x2015, '#8213', attr, text, editor); // horizontal bar
846
847 e(0x00a0, 'nbsp', attr, text, white, editor);
848 e(0x2002, 'ensp', attr, text, white, editor);
849 e(0x2003, 'emsp', attr, text, white, editor);
850 e(0x2009, 'thinsp', attr, text, white, editor);
851 e(0x200c, 'zwnj', attr, text, white, editor);
852 e(0x200d, 'zwj', attr, text, white, editor);
853 e(0x200e, 'lrm', attr, text, white, editor);
854 e(0x200f, 'rlm', attr, text, white, editor);
855 e(0x200b, '#8203', attr, text, white, editor); // zero-width space (ZWSP)
856
857 //************************************************************************************************
858 // Entity escaping
859
860 var entityConversionRegexes = {
861         normal : {},
862         reverse : {}
863     };
864
865 var escapeEntitiesRegEx = {
866     normal : function(list)
867     {
868         var chars = [];
869         for ( var ch in list)
870         {
871             chars.push(ch);
872         }
873         return new RegExp('([' + chars.join('') + '])', 'gm');
874     },
875     reverse : function(list)
876     {
877         var chars = [];
878         for ( var ch in list)
879         {
880             chars.push(ch);
881         }
882         return new RegExp('(' + chars.join('|') + ')', 'gm');
883     }
884 };
885
886 function getEscapeRegexp(direction, lists)
887 {
888     var name = '', re;
889     var groups = [].concat(lists);
890     for (i = 0; i < groups.length; i++)
891     {
892         name += groups[i].group;
893     }
894     re = entityConversionRegexes[direction][name];
895     if (!re)
896     {
897         var list = {};
898         if (groups.length > 1)
899         {
900             for ( var i = 0; i < groups.length; i++)
901             {
902                 var aList = entityConversionLists[direction][groups[i].group];
903                 for ( var item in aList)
904                     list[item] = aList[item];
905             }
906         } else if (groups.length==1)
907         {
908             list = entityConversionLists[direction][groups[0].group]; // faster for special case
909         } else {
910             list = {}; // perhaps should print out an error here?
911         }
912         re = entityConversionRegexes[direction][name] = escapeEntitiesRegEx[direction](list);
913     }
914     return re;
915 };
916
917 function createSimpleEscape(name, direction)
918 {
919     return function(value)
920     {
921         var list = entityConversionLists[direction][name];
922         return String(value).replace(
923                 getEscapeRegexp(direction, {
924                     group : name,
925                     list : list
926                 }),
927                 function(ch)
928                 {
929                     return list[ch];
930                 }
931                );
932     };
933 };
934
935 function escapeGroupsForEntities(str, lists)
936 {
937     lists = [].concat(lists);
938     var re = getEscapeRegexp('normal', lists),
939         split = String(str).split(re),
940         len = split.length,
941         results = [],
942         cur, r, i, ri = 0, l, list, last = '';
943     if (!len)
944         return [ {
945             str : String(str),
946             group : '',
947             name : ''
948         } ];
949     for (i = 0; i < len; i++)
950     {
951         cur = split[i];
952         if (cur == '')
953             continue;
954         for (l = 0; l < lists.length; l++)
955         {
956             list = lists[l];
957             r = entityConversionLists.normal[list.group][cur];
958             // if (cur == ' ' && list.group == 'whitespace' && last == ' ') // only show for runs of more than one space
959             //     r = ' ';
960             if (r)
961             {
962                 results[ri] = {
963                     'str' : r,
964                     'class' : list['class'],
965                     'extra' : list.extra[cur] ? list['class']
966                             + list.extra[cur] : ''
967                 };
968                 break;
969             }
970         }
971         // last=cur;
972         if (!r)
973             results[ri] = {
974                 'str' : cur,
975                 'class' : '',
976                 'extra' : ''
977             };
978         ri++;
979     }
980     return results;
981 };
982
983 this.escapeGroupsForEntities = escapeGroupsForEntities;
984
985
986 function unescapeEntities(str, lists)
987 {
988     var re = getEscapeRegexp('reverse', lists),
989         split = String(str).split(re),
990         len = split.length,
991         results = [],
992         cur, r, i, ri = 0, l, list;
993     if (!len)
994         return str;
995     lists = [].concat(lists);
996     for (i = 0; i < len; i++)
997     {
998         cur = split[i];
999         if (cur == '')
1000             continue;
1001         for (l = 0; l < lists.length; l++)
1002         {
1003             list = lists[l];
1004             r = entityConversionLists.reverse[list.group][cur];
1005             if (r)
1006             {
1007                 results[ri] = r;
1008                 break;
1009             }
1010         }
1011         if (!r)
1012             results[ri] = cur;
1013         ri++;
1014     }
1015     return results.join('') || '';
1016 };
1017
1018
1019 // ************************************************************************************************
1020 // String escaping
1021
1022 var escapeForTextNode = this.escapeForTextNode = createSimpleEscape('text', 'normal');
1023 var escapeForHtmlEditor = this.escapeForHtmlEditor = createSimpleEscape('editor', 'normal');
1024 var escapeForElementAttribute = this.escapeForElementAttribute = createSimpleEscape('attributes', 'normal');
1025 var escapeForCss = this.escapeForCss = createSimpleEscape('css', 'normal');
1026
1027 // deprecated compatibility functions
1028 //this.deprecateEscapeHTML = createSimpleEscape('text', 'normal');
1029 //this.deprecatedUnescapeHTML = createSimpleEscape('text', 'reverse');
1030 //this.escapeHTML = deprecated("use appropriate escapeFor... function", this.deprecateEscapeHTML);
1031 //this.unescapeHTML = deprecated("use appropriate unescapeFor... function", this.deprecatedUnescapeHTML);
1032
1033 var escapeForSourceLine = this.escapeForSourceLine = createSimpleEscape('text', 'normal');
1034
1035 var unescapeWhitespace = createSimpleEscape('whitespace', 'reverse');
1036
1037 this.unescapeForTextNode = function(str)
1038 {
1039     if (Firebug.showTextNodesWithWhitespace)
1040         str = unescapeWhitespace(str);
1041     if (!Firebug.showTextNodesWithEntities)
1042         str = escapeForElementAttribute(str);
1043     return str;
1044 };
1045
1046 this.escapeNewLines = function(value)
1047 {
1048     return value.replace(/\r/g, "\\r").replace(/\n/g, "\\n");
1049 };
1050
1051 this.stripNewLines = function(value)
1052 {
1053     return typeof(value) == "string" ? value.replace(/[\r\n]/g, " ") : value;
1054 };
1055
1056 this.escapeJS = function(value)
1057 {
1058     return value.replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace('"', '\\"', "g");
1059 };
1060
1061 function escapeHTMLAttribute(value)
1062 {
1063     function replaceChars(ch)
1064     {
1065         switch (ch)
1066         {
1067             case "&":
1068                 return "&amp;";
1069             case "'":
1070                 return apos;
1071             case '"':
1072                 return quot;
1073         }
1074         return "?";
1075     };
1076     var apos = "&#39;", quot = "&quot;", around = '"';
1077     if( value.indexOf('"') == -1 ) {
1078         quot = '"';
1079         apos = "'";
1080     } else if( value.indexOf("'") == -1 ) {
1081         quot = '"';
1082         around = "'";
1083     }
1084     return around + (String(value).replace(/[&'"]/g, replaceChars)) + around;
1085 }
1086
1087
1088 function escapeHTML(value)
1089 {
1090     function replaceChars(ch)
1091     {
1092         switch (ch)
1093         {
1094             case "<":
1095                 return "&lt;";
1096             case ">":
1097                 return "&gt;";
1098             case "&":
1099                 return "&amp;";
1100             case "'":
1101                 return "&#39;";
1102             case '"':
1103                 return "&quot;";
1104         }
1105         return "?";
1106     };
1107     return String(value).replace(/[<>&"']/g, replaceChars);
1108 }
1109
1110 this.escapeHTML = escapeHTML;
1111
1112 this.cropString = function(text, limit)
1113 {
1114     text = text + "";
1115
1116     if (!limit)
1117         var halfLimit = 50;
1118     else
1119         var halfLimit = limit / 2;
1120
1121     if (text.length > limit)
1122         return this.escapeNewLines(text.substr(0, halfLimit) + "..." + text.substr(text.length-halfLimit));
1123     else
1124         return this.escapeNewLines(text);
1125 };
1126
1127 this.isWhitespace = function(text)
1128 {
1129     return !reNotWhitespace.exec(text);
1130 };
1131
1132 this.splitLines = function(text)
1133 {
1134     var reSplitLines2 = /.*(:?\r\n|\n|\r)?/mg;
1135     var lines;
1136     if (text.match)
1137     {
1138         lines = text.match(reSplitLines2);
1139     }
1140     else
1141     {
1142         var str = text+"";
1143         lines = str.match(reSplitLines2);
1144     }
1145     lines.pop();
1146     return lines;
1147 };
1148
1149
1150 // ************************************************************************************************
1151
1152 this.safeToString = function(ob)
1153 {
1154     if (this.isIE)
1155     {
1156         try
1157         {
1158             // FIXME: xxxpedro this is failing in IE for the global "external" object
1159             return ob + "";
1160         }
1161         catch(E)
1162         {
1163             FBTrace.sysout("Lib.safeToString() failed for ", ob);
1164             return "";
1165         }
1166     }
1167
1168     try
1169     {
1170         if (ob && "toString" in ob && typeof ob.toString == "function")
1171             return ob.toString();
1172     }
1173     catch (exc)
1174     {
1175         // xxxpedro it is not safe to use ob+""?
1176         return ob + "";
1177         ///return "[an object with no toString() function]";
1178     }
1179 };
1180
1181 // ************************************************************************************************
1182
1183 this.hasProperties = function(ob)
1184 {
1185     try
1186     {
1187         for (var name in ob)
1188             return true;
1189     } catch (exc) {}
1190     return false;
1191 };
1192
1193 // ************************************************************************************************
1194 // String Util
1195
1196 var reTrim = /^\s+|\s+$/g;
1197 this.trim = function(s)
1198 {
1199     return s.replace(reTrim, "");
1200 };
1201
1202
1203 // ************************************************************************************************
1204 // Empty
1205
1206 this.emptyFn = function(){};
1207
1208
1209
1210 // ************************************************************************************************
1211 // Visibility
1212
1213 this.isVisible = function(elt)
1214 {
1215     /*
1216     if (elt instanceof XULElement)
1217     {
1218         //FBTrace.sysout("isVisible elt.offsetWidth: "+elt.offsetWidth+" offsetHeight:"+ elt.offsetHeight+" localName:"+ elt.localName+" nameSpace:"+elt.nameSpaceURI+"\n");
1219         return (!elt.hidden && !elt.collapsed);
1220     }
1221     /**/
1222
1223     return this.getStyle(elt, "visibility") != "hidden" &&
1224         ( elt.offsetWidth > 0 || elt.offsetHeight > 0
1225         || elt.tagName in invisibleTags
1226         || elt.namespaceURI == "http://www.w3.org/2000/svg"
1227         || elt.namespaceURI == "http://www.w3.org/1998/Math/MathML" );
1228 };
1229
1230 this.collapse = function(elt, collapsed)
1231 {
1232     // IE6 doesn't support the [collapsed] CSS selector. IE7 does support the selector,
1233     // but it is causing a bug (the element disappears when you set the "collapsed"
1234     // attribute, but it doesn't appear when you remove the attribute. So, for those
1235     // cases, we need to use the class attribute.
1236     if (this.isIElt8)
1237     {
1238         if (collapsed)
1239             this.setClass(elt, "collapsed");
1240         else
1241             this.removeClass(elt, "collapsed");
1242     }
1243     else
1244         elt.setAttribute("collapsed", collapsed ? "true" : "false");
1245 };
1246
1247 this.obscure = function(elt, obscured)
1248 {
1249     if (obscured)
1250         this.setClass(elt, "obscured");
1251     else
1252         this.removeClass(elt, "obscured");
1253 };
1254
1255 this.hide = function(elt, hidden)
1256 {
1257     elt.style.visibility = hidden ? "hidden" : "visible";
1258 };
1259
1260 this.clearNode = function(node)
1261 {
1262     var nodeName = " " + node.nodeName.toLowerCase() + " ";
1263     var ignoreTags = " table tbody thead tfoot th tr td ";
1264
1265     // IE can't use innerHTML of table elements
1266     if (this.isIE && ignoreTags.indexOf(nodeName) != -1)
1267         this.eraseNode(node);
1268     else
1269         node.innerHTML = "";
1270 };
1271
1272 this.eraseNode = function(node)
1273 {
1274     while (node.lastChild)
1275         node.removeChild(node.lastChild);
1276 };
1277
1278 // ************************************************************************************************
1279 // Window iteration
1280
1281 this.iterateWindows = function(win, handler)
1282 {
1283     if (!win || !win.document)
1284         return;
1285
1286     handler(win);
1287
1288     if (win == top || !win.frames) return; // XXXjjb hack for chromeBug
1289
1290     for (var i = 0; i < win.frames.length; ++i)
1291     {
1292         var subWin = win.frames[i];
1293         if (subWin != win)
1294             this.iterateWindows(subWin, handler);
1295     }
1296 };
1297
1298 this.getRootWindow = function(win)
1299 {
1300     for (; win; win = win.parent)
1301     {
1302         if (!win.parent || win == win.parent || !this.instanceOf(win.parent, "Window"))
1303             return win;
1304     }
1305     return null;
1306 };
1307
1308 // ************************************************************************************************
1309 // Graphics
1310
1311 this.getClientOffset = function(elt)
1312 {
1313     var addOffset = function addOffset(elt, coords, view)
1314     {
1315         var p = elt.offsetParent;
1316
1317         ///var style = isIE ? elt.currentStyle : view.getComputedStyle(elt, "");
1318         var chrome = Firebug.chrome;
1319
1320         if (elt.offsetLeft)
1321             ///coords.x += elt.offsetLeft + parseInt(style.borderLeftWidth);
1322             coords.x += elt.offsetLeft + chrome.getMeasurementInPixels(elt, "borderLeft");
1323         if (elt.offsetTop)
1324             ///coords.y += elt.offsetTop + parseInt(style.borderTopWidth);
1325             coords.y += elt.offsetTop + chrome.getMeasurementInPixels(elt, "borderTop");
1326
1327         if (p)
1328         {
1329             if (p.nodeType == 1)
1330                 addOffset(p, coords, view);
1331         }
1332         else
1333         {
1334             var otherView = isIE ? elt.ownerDocument.parentWindow : elt.ownerDocument.defaultView;
1335             // IE will fail when reading the frameElement property of a popup window.
1336             // We don't need it anyway once it is outside the (popup) viewport, so we're
1337             // ignoring the frameElement check when the window is a popup
1338             if (!otherView.opener && otherView.frameElement)
1339                 addOffset(otherView.frameElement, coords, otherView);
1340         }
1341     };
1342
1343     var isIE = this.isIE;
1344     var coords = {x: 0, y: 0};
1345     if (elt)
1346     {
1347         var view = isIE ? elt.ownerDocument.parentWindow : elt.ownerDocument.defaultView;
1348         addOffset(elt, coords, view);
1349     }
1350
1351     return coords;
1352 };
1353
1354 this.getViewOffset = function(elt, singleFrame)
1355 {
1356     function addOffset(elt, coords, view)
1357     {
1358         var p = elt.offsetParent;
1359         coords.x += elt.offsetLeft - (p ? p.scrollLeft : 0);
1360         coords.y += elt.offsetTop - (p ? p.scrollTop : 0);
1361
1362         if (p)
1363         {
1364             if (p.nodeType == 1)
1365             {
1366                 var parentStyle = view.getComputedStyle(p, "");
1367                 if (parentStyle.position != "static")
1368                 {
1369                     coords.x += parseInt(parentStyle.borderLeftWidth);
1370                     coords.y += parseInt(parentStyle.borderTopWidth);
1371
1372                     if (p.localName == "TABLE")
1373                     {
1374                         coords.x += parseInt(parentStyle.paddingLeft);
1375                         coords.y += parseInt(parentStyle.paddingTop);
1376                     }
1377                     else if (p.localName == "BODY")
1378                     {
1379                         var style = view.getComputedStyle(elt, "");
1380                         coords.x += parseInt(style.marginLeft);
1381                         coords.y += parseInt(style.marginTop);
1382                     }
1383                 }
1384                 else if (p.localName == "BODY")
1385                 {
1386                     coords.x += parseInt(parentStyle.borderLeftWidth);
1387                     coords.y += parseInt(parentStyle.borderTopWidth);
1388                 }
1389
1390                 var parent = elt.parentNode;
1391                 while (p != parent)
1392                 {
1393                     coords.x -= parent.scrollLeft;
1394                     coords.y -= parent.scrollTop;
1395                     parent = parent.parentNode;
1396                 }
1397                 addOffset(p, coords, view);
1398             }
1399         }
1400         else
1401         {
1402             if (elt.localName == "BODY")
1403             {
1404                 var style = view.getComputedStyle(elt, "");
1405                 coords.x += parseInt(style.borderLeftWidth);
1406                 coords.y += parseInt(style.borderTopWidth);
1407
1408                 var htmlStyle = view.getComputedStyle(elt.parentNode, "");
1409                 coords.x -= parseInt(htmlStyle.paddingLeft);
1410                 coords.y -= parseInt(htmlStyle.paddingTop);
1411             }
1412
1413             if (elt.scrollLeft)
1414                 coords.x += elt.scrollLeft;
1415             if (elt.scrollTop)
1416                 coords.y += elt.scrollTop;
1417
1418             var win = elt.ownerDocument.defaultView;
1419             if (win && (!singleFrame && win.frameElement))
1420                 addOffset(win.frameElement, coords, win);
1421         }
1422
1423     }
1424
1425     var coords = {x: 0, y: 0};
1426     if (elt)
1427         addOffset(elt, coords, elt.ownerDocument.defaultView);
1428
1429     return coords;
1430 };
1431
1432 this.getLTRBWH = function(elt)
1433 {
1434     var bcrect,
1435         dims = {"left": 0, "top": 0, "right": 0, "bottom": 0, "width": 0, "height": 0};
1436
1437     if (elt)
1438     {
1439         bcrect = elt.getBoundingClientRect();
1440         dims.left = bcrect.left;
1441         dims.top = bcrect.top;
1442         dims.right = bcrect.right;
1443         dims.bottom = bcrect.bottom;
1444
1445         if(bcrect.width)
1446         {
1447             dims.width = bcrect.width;
1448             dims.height = bcrect.height;
1449         }
1450         else
1451         {
1452             dims.width = dims.right - dims.left;
1453             dims.height = dims.bottom - dims.top;
1454         }
1455     }
1456     return dims;
1457 };
1458
1459 this.applyBodyOffsets = function(elt, clientRect)
1460 {
1461     var od = elt.ownerDocument;
1462     if (!od.body)
1463         return clientRect;
1464
1465     var style = od.defaultView.getComputedStyle(od.body, null);
1466
1467     var pos = style.getPropertyValue('position');
1468     if(pos === 'absolute' || pos === 'relative')
1469     {
1470         var borderLeft = parseInt(style.getPropertyValue('border-left-width').replace('px', ''),10) || 0;
1471         var borderTop = parseInt(style.getPropertyValue('border-top-width').replace('px', ''),10) || 0;
1472         var paddingLeft = parseInt(style.getPropertyValue('padding-left').replace('px', ''),10) || 0;
1473         var paddingTop = parseInt(style.getPropertyValue('padding-top').replace('px', ''),10) || 0;
1474         var marginLeft = parseInt(style.getPropertyValue('margin-left').replace('px', ''),10) || 0;
1475         var marginTop = parseInt(style.getPropertyValue('margin-top').replace('px', ''),10) || 0;
1476
1477         var offsetX = borderLeft + paddingLeft + marginLeft;
1478         var offsetY = borderTop + paddingTop + marginTop;
1479
1480         clientRect.left -= offsetX;
1481         clientRect.top -= offsetY;
1482         clientRect.right -= offsetX;
1483         clientRect.bottom -= offsetY;
1484     }
1485
1486     return clientRect;
1487 };
1488
1489 this.getOffsetSize = function(elt)
1490 {
1491     return {width: elt.offsetWidth, height: elt.offsetHeight};
1492 };
1493
1494 this.getOverflowParent = function(element)
1495 {
1496     for (var scrollParent = element.parentNode; scrollParent; scrollParent = scrollParent.offsetParent)
1497     {
1498         if (scrollParent.scrollHeight > scrollParent.offsetHeight)
1499             return scrollParent;
1500     }
1501 };
1502
1503 this.isScrolledToBottom = function(element)
1504 {
1505     var onBottom = (element.scrollTop + element.offsetHeight) == element.scrollHeight;
1506     if (FBTrace.DBG_CONSOLE)
1507         FBTrace.sysout("isScrolledToBottom offsetHeight: "+element.offsetHeight +" onBottom:"+onBottom);
1508     return onBottom;
1509 };
1510
1511 this.scrollToBottom = function(element)
1512 {
1513         element.scrollTop = element.scrollHeight;
1514
1515         if (FBTrace.DBG_CONSOLE)
1516         {
1517             FBTrace.sysout("scrollToBottom reset scrollTop "+element.scrollTop+" = "+element.scrollHeight);
1518             if (element.scrollHeight == element.offsetHeight)
1519                 FBTrace.sysout("scrollToBottom attempt to scroll non-scrollable element "+element, element);
1520         }
1521
1522         return (element.scrollTop == element.scrollHeight);
1523 };
1524
1525 this.move = function(element, x, y)
1526 {
1527     element.style.left = x + "px";
1528     element.style.top = y + "px";
1529 };
1530
1531 this.resize = function(element, w, h)
1532 {
1533     element.style.width = w + "px";
1534     element.style.height = h + "px";
1535 };
1536
1537 this.linesIntoCenterView = function(element, scrollBox)  // {before: int, after: int}
1538 {
1539     if (!scrollBox)
1540         scrollBox = this.getOverflowParent(element);
1541
1542     if (!scrollBox)
1543         return;
1544
1545     var offset = this.getClientOffset(element);
1546
1547     var topSpace = offset.y - scrollBox.scrollTop;
1548     var bottomSpace = (scrollBox.scrollTop + scrollBox.clientHeight)
1549             - (offset.y + element.offsetHeight);
1550
1551     if (topSpace < 0 || bottomSpace < 0)
1552     {
1553         var split = (scrollBox.clientHeight/2);
1554         var centerY = offset.y - split;
1555         scrollBox.scrollTop = centerY;
1556         topSpace = split;
1557         bottomSpace = split -  element.offsetHeight;
1558     }
1559
1560     return {before: Math.round((topSpace/element.offsetHeight) + 0.5),
1561             after: Math.round((bottomSpace/element.offsetHeight) + 0.5) };
1562 };
1563
1564 this.scrollIntoCenterView = function(element, scrollBox, notX, notY)
1565 {
1566     if (!element)
1567         return;
1568
1569     if (!scrollBox)
1570         scrollBox = this.getOverflowParent(element);
1571
1572     if (!scrollBox)
1573         return;
1574
1575     var offset = this.getClientOffset(element);
1576
1577     if (!notY)
1578     {
1579         var topSpace = offset.y - scrollBox.scrollTop;
1580         var bottomSpace = (scrollBox.scrollTop + scrollBox.clientHeight)
1581             - (offset.y + element.offsetHeight);
1582
1583         if (topSpace < 0 || bottomSpace < 0)
1584         {
1585             var centerY = offset.y - (scrollBox.clientHeight/2);
1586             scrollBox.scrollTop = centerY;
1587         }
1588     }
1589
1590     if (!notX)
1591     {
1592         var leftSpace = offset.x - scrollBox.scrollLeft;
1593         var rightSpace = (scrollBox.scrollLeft + scrollBox.clientWidth)
1594             - (offset.x + element.clientWidth);
1595
1596         if (leftSpace < 0 || rightSpace < 0)
1597         {
1598             var centerX = offset.x - (scrollBox.clientWidth/2);
1599             scrollBox.scrollLeft = centerX;
1600         }
1601     }
1602     if (FBTrace.DBG_SOURCEFILES)
1603         FBTrace.sysout("lib.scrollIntoCenterView ","Element:"+element.innerHTML);
1604 };
1605
1606
1607 // ************************************************************************************************
1608 // CSS
1609
1610 var cssKeywordMap = null;
1611 var cssPropNames = null;
1612 var cssColorNames = null;
1613 var imageRules = null;
1614
1615 this.getCSSKeywordsByProperty = function(propName)
1616 {
1617     if (!cssKeywordMap)
1618     {
1619         cssKeywordMap = {};
1620
1621         for (var name in this.cssInfo)
1622         {
1623             var list = [];
1624
1625             var types = this.cssInfo[name];
1626             for (var i = 0; i < types.length; ++i)
1627             {
1628                 var keywords = this.cssKeywords[types[i]];
1629                 if (keywords)
1630                     list.push.apply(list, keywords);
1631             }
1632
1633             cssKeywordMap[name] = list;
1634         }
1635     }
1636
1637     return propName in cssKeywordMap ? cssKeywordMap[propName] : [];
1638 };
1639
1640 this.getCSSPropertyNames = function()
1641 {
1642     if (!cssPropNames)
1643     {
1644         cssPropNames = [];
1645
1646         for (var name in this.cssInfo)
1647             cssPropNames.push(name);
1648     }
1649
1650     return cssPropNames;
1651 };
1652
1653 this.isColorKeyword = function(keyword)
1654 {
1655     if (keyword == "transparent")
1656         return false;
1657
1658     if (!cssColorNames)
1659     {
1660         cssColorNames = [];
1661
1662         var colors = this.cssKeywords["color"];
1663         for (var i = 0; i < colors.length; ++i)
1664             cssColorNames.push(colors[i].toLowerCase());
1665
1666         var systemColors = this.cssKeywords["systemColor"];
1667         for (var i = 0; i < systemColors.length; ++i)
1668             cssColorNames.push(systemColors[i].toLowerCase());
1669     }
1670
1671     return cssColorNames.indexOf ? // Array.indexOf is not available in IE
1672             cssColorNames.indexOf(keyword.toLowerCase()) != -1 :
1673             (" " + cssColorNames.join(" ") + " ").indexOf(" " + keyword.toLowerCase() + " ") != -1;
1674 };
1675
1676 this.isImageRule = function(rule)
1677 {
1678     if (!imageRules)
1679     {
1680         imageRules = [];
1681
1682         for (var i in this.cssInfo)
1683         {
1684             var r = i.toLowerCase();
1685             var suffix = "image";
1686             if (r.match(suffix + "$") == suffix || r == "background")
1687                 imageRules.push(r);
1688         }
1689     }
1690
1691     return imageRules.indexOf ? // Array.indexOf is not available in IE
1692             imageRules.indexOf(rule.toLowerCase()) != -1 :
1693             (" " + imageRules.join(" ") + " ").indexOf(" " + rule.toLowerCase() + " ") != -1;
1694 };
1695
1696 this.copyTextStyles = function(fromNode, toNode, style)
1697 {
1698     var view = this.isIE ?
1699             fromNode.ownerDocument.parentWindow :
1700             fromNode.ownerDocument.defaultView;
1701
1702     if (view)
1703     {
1704         if (!style)
1705             style = this.isIE ? fromNode.currentStyle : view.getComputedStyle(fromNode, "");
1706
1707         toNode.style.fontFamily = style.fontFamily;
1708
1709         // TODO: xxxpedro need to create a FBL.getComputedStyle() because IE
1710         // returns wrong computed styles for inherited properties (like font-*)
1711         //
1712         // Also would be good to create a FBL.getStyle()
1713         toNode.style.fontSize = style.fontSize;
1714         toNode.style.fontWeight = style.fontWeight;
1715         toNode.style.fontStyle = style.fontStyle;
1716
1717         return style;
1718     }
1719 };
1720
1721 this.copyBoxStyles = function(fromNode, toNode, style)
1722 {
1723     var view = this.isIE ?
1724             fromNode.ownerDocument.parentWindow :
1725             fromNode.ownerDocument.defaultView;
1726
1727     if (view)
1728     {
1729         if (!style)
1730             style = this.isIE ? fromNode.currentStyle : view.getComputedStyle(fromNode, "");
1731
1732         toNode.style.marginTop = style.marginTop;
1733         toNode.style.marginRight = style.marginRight;
1734         toNode.style.marginBottom = style.marginBottom;
1735         toNode.style.marginLeft = style.marginLeft;
1736         toNode.style.borderTopWidth = style.borderTopWidth;
1737         toNode.style.borderRightWidth = style.borderRightWidth;
1738         toNode.style.borderBottomWidth = style.borderBottomWidth;
1739         toNode.style.borderLeftWidth = style.borderLeftWidth;
1740
1741         return style;
1742     }
1743 };
1744
1745 this.readBoxStyles = function(style)
1746 {
1747     var styleNames = {
1748         "margin-top": "marginTop", "margin-right": "marginRight",
1749         "margin-left": "marginLeft", "margin-bottom": "marginBottom",
1750         "border-top-width": "borderTop", "border-right-width": "borderRight",
1751         "border-left-width": "borderLeft", "border-bottom-width": "borderBottom",
1752         "padding-top": "paddingTop", "padding-right": "paddingRight",
1753         "padding-left": "paddingLeft", "padding-bottom": "paddingBottom",
1754         "z-index": "zIndex"
1755     };
1756
1757     var styles = {};
1758     for (var styleName in styleNames)
1759         styles[styleNames[styleName]] = parseInt(style.getPropertyCSSValue(styleName).cssText) || 0;
1760     if (FBTrace.DBG_INSPECT)
1761         FBTrace.sysout("readBoxStyles ", styles);
1762     return styles;
1763 };
1764
1765 this.getBoxFromStyles = function(style, element)
1766 {
1767     var args = this.readBoxStyles(style);
1768     args.width = element.offsetWidth
1769         - (args.paddingLeft+args.paddingRight+args.borderLeft+args.borderRight);
1770     args.height = element.offsetHeight
1771         - (args.paddingTop+args.paddingBottom+args.borderTop+args.borderBottom);
1772     return args;
1773 };
1774
1775 this.getElementCSSSelector = function(element)
1776 {
1777     var label = element.localName.toLowerCase();
1778     if (element.id)
1779         label += "#" + element.id;
1780     if (element.hasAttribute("class"))
1781         label += "." + element.getAttribute("class").split(" ")[0];
1782
1783     return label;
1784 };
1785
1786 this.getURLForStyleSheet= function(styleSheet)
1787 {
1788     //http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet. For inline style sheets, the value of this attribute is null.
1789     return (styleSheet.href ? styleSheet.href : styleSheet.ownerNode.ownerDocument.URL);
1790 };
1791
1792 this.getDocumentForStyleSheet = function(styleSheet)
1793 {
1794     while (styleSheet.parentStyleSheet && !styleSheet.ownerNode)
1795     {
1796         styleSheet = styleSheet.parentStyleSheet;
1797     }
1798     if (styleSheet.ownerNode)
1799       return styleSheet.ownerNode.ownerDocument;
1800 };
1801
1802 /**
1803  * Retrieves the instance number for a given style sheet. The instance number
1804  * is sheet's index within the set of all other sheets whose URL is the same.
1805  */
1806 this.getInstanceForStyleSheet = function(styleSheet, ownerDocument)
1807 {
1808     // System URLs are always unique (or at least we are making this assumption)
1809     if (FBL.isSystemStyleSheet(styleSheet))
1810         return 0;
1811
1812     // ownerDocument is an optional hint for performance
1813     if (FBTrace.DBG_CSS) FBTrace.sysout("getInstanceForStyleSheet: " + styleSheet.href + " " + styleSheet.media.mediaText + " " + (styleSheet.ownerNode && FBL.getElementXPath(styleSheet.ownerNode)), ownerDocument);
1814     ownerDocument = ownerDocument || FBL.getDocumentForStyleSheet(styleSheet);
1815
1816     var ret = 0,
1817         styleSheets = ownerDocument.styleSheets,
1818         href = styleSheet.href;
1819     for (var i = 0; i < styleSheets.length; i++)
1820     {
1821         var curSheet = styleSheets[i];
1822         if (FBTrace.DBG_CSS) FBTrace.sysout("getInstanceForStyleSheet: compare href " + i + " " + curSheet.href + " " + curSheet.media.mediaText + " " + (curSheet.ownerNode && FBL.getElementXPath(curSheet.ownerNode)));
1823         if (curSheet == styleSheet)
1824             break;
1825         if (curSheet.href == href)
1826             ret++;
1827     }
1828     return ret;
1829 };
1830
1831 // ************************************************************************************************
1832 // HTML and XML Serialization
1833
1834
1835 var getElementType = this.getElementType = function(node)
1836 {
1837     if (isElementXUL(node))
1838         return 'xul';
1839     else if (isElementSVG(node))
1840         return 'svg';
1841     else if (isElementMathML(node))
1842         return 'mathml';
1843     else if (isElementXHTML(node))
1844         return 'xhtml';
1845     else if (isElementHTML(node))
1846         return 'html';
1847 };
1848
1849 var getElementSimpleType = this.getElementSimpleType = function(node)
1850 {
1851     if (isElementSVG(node))
1852         return 'svg';
1853     else if (isElementMathML(node))
1854         return 'mathml';
1855     else
1856         return 'html';
1857 };
1858
1859 var isElementHTML = this.isElementHTML = function(node)
1860 {
1861     return node.nodeName == node.nodeName.toUpperCase();
1862 };
1863
1864 var isElementXHTML = this.isElementXHTML = function(node)
1865 {
1866     return node.nodeName == node.nodeName.toLowerCase();
1867 };
1868
1869 var isElementMathML = this.isElementMathML = function(node)
1870 {
1871     return node.namespaceURI == 'http://www.w3.org/1998/Math/MathML';
1872 };
1873
1874 var isElementSVG = this.isElementSVG = function(node)
1875 {
1876     return node.namespaceURI == 'http://www.w3.org/2000/svg';
1877 };
1878
1879 var isElementXUL = this.isElementXUL = function(node)
1880 {
1881     return node instanceof XULElement;
1882 };
1883
1884 this.isSelfClosing = function(element)
1885 {
1886     if (isElementSVG(element) || isElementMathML(element))
1887         return true;
1888     var tag = element.localName.toLowerCase();
1889     return (this.selfClosingTags.hasOwnProperty(tag));
1890 };
1891
1892 this.getElementHTML = function(element)
1893 {
1894     var self=this;
1895     function toHTML(elt)
1896     {
1897         if (elt.nodeType == Node.ELEMENT_NODE)
1898         {
1899             if (unwrapObject(elt).firebugIgnore)
1900                 return;
1901
1902             html.push('<', elt.nodeName.toLowerCase());
1903
1904             for (var i = 0; i < elt.attributes.length; ++i)
1905             {
1906                 var attr = elt.attributes[i];
1907
1908                 // Hide attributes set by Firebug
1909                 if (attr.localName.indexOf("firebug-") == 0)
1910                     continue;
1911
1912                 // MathML
1913                 if (attr.localName.indexOf("-moz-math") == 0)
1914                 {
1915                     // just hide for now
1916                     continue;
1917                 }
1918
1919                 html.push(' ', attr.nodeName, '="', escapeForElementAttribute(attr.nodeValue),'"');
1920             }
1921
1922             if (elt.firstChild)
1923             {
1924                 html.push('>');
1925
1926                 var pureText=true;
1927                 for (var child = element.firstChild; child; child = child.nextSibling)
1928                     pureText=pureText && (child.nodeType == Node.TEXT_NODE);
1929
1930                 if (pureText)
1931                     html.push(escapeForHtmlEditor(elt.textContent));
1932                 else {
1933                     for (var child = elt.firstChild; child; child = child.nextSibling)
1934                         toHTML(child);
1935                 }
1936
1937                 html.push('</', elt.nodeName.toLowerCase(), '>');
1938             }
1939             else if (isElementSVG(elt) || isElementMathML(elt))
1940             {
1941                 html.push('/>');
1942             }
1943             else if (self.isSelfClosing(elt))
1944             {
1945                 html.push((isElementXHTML(elt))?'/>':'>');
1946             }
1947             else
1948             {
1949                 html.push('></', elt.nodeName.toLowerCase(), '>');
1950             }
1951         }
1952         else if (elt.nodeType == Node.TEXT_NODE)
1953             html.push(escapeForTextNode(elt.textContent));
1954         else if (elt.nodeType == Node.CDATA_SECTION_NODE)
1955             html.push('<![CDATA[', elt.nodeValue, ']]>');
1956         else if (elt.nodeType == Node.COMMENT_NODE)
1957             html.push('<!--', elt.nodeValue, '-->');
1958     }
1959
1960     var html = [];
1961     toHTML(element);
1962     return html.join("");
1963 };
1964
1965 this.getElementXML = function(element)
1966 {
1967     function toXML(elt)
1968     {
1969         if (elt.nodeType == Node.ELEMENT_NODE)
1970         {
1971             if (unwrapObject(elt).firebugIgnore)
1972                 return;
1973
1974             xml.push('<', elt.nodeName.toLowerCase());
1975
1976             for (var i = 0; i < elt.attributes.length; ++i)
1977             {
1978                 var attr = elt.attributes[i];
1979
1980                 // Hide attributes set by Firebug
1981                 if (attr.localName.indexOf("firebug-") == 0)
1982                     continue;
1983
1984                 // MathML
1985                 if (attr.localName.indexOf("-moz-math") == 0)
1986                 {
1987                     // just hide for now
1988                     continue;
1989                 }
1990
1991                 xml.push(' ', attr.nodeName, '="', escapeForElementAttribute(attr.nodeValue),'"');
1992             }
1993
1994             if (elt.firstChild)
1995             {
1996                 xml.push('>');
1997
1998                 for (var child = elt.firstChild; child; child = child.nextSibling)
1999                     toXML(child);
2000
2001                 xml.push('</', elt.nodeName.toLowerCase(), '>');
2002             }
2003             else
2004                 xml.push('/>');
2005         }
2006         else if (elt.nodeType == Node.TEXT_NODE)
2007             xml.push(elt.nodeValue);
2008         else if (elt.nodeType == Node.CDATA_SECTION_NODE)
2009             xml.push('<![CDATA[', elt.nodeValue, ']]>');
2010         else if (elt.nodeType == Node.COMMENT_NODE)
2011             xml.push('<!--', elt.nodeValue, '-->');
2012     }
2013
2014     var xml = [];
2015     toXML(element);
2016     return xml.join("");
2017 };
2018
2019
2020 // ************************************************************************************************
2021 // CSS classes
2022
2023 this.hasClass = function(node, name) // className, className, ...
2024 {
2025     // TODO: xxxpedro when lib.hasClass is called with more than 2 arguments?
2026     // this function can be optimized a lot if assumed 2 arguments only,
2027     // which seems to be what happens 99% of the time
2028     if (arguments.length == 2)
2029         return (' '+node.className+' ').indexOf(' '+name+' ') != -1;
2030
2031     if (!node || node.nodeType != 1)
2032         return false;
2033     else
2034     {
2035         for (var i=1; i<arguments.length; ++i)
2036         {
2037             var name = arguments[i];
2038             var re = new RegExp("(^|\\s)"+name+"($|\\s)");
2039             if (!re.exec(node.className))
2040                 return false;
2041         }
2042
2043         return true;
2044     }
2045 };
2046
2047 this.old_hasClass = function(node, name) // className, className, ...
2048 {
2049     if (!node || node.nodeType != 1)
2050         return false;
2051     else
2052     {
2053         for (var i=1; i<arguments.length; ++i)
2054         {
2055             var name = arguments[i];
2056             var re = new RegExp("(^|\\s)"+name+"($|\\s)");
2057             if (!re.exec(node.className))
2058                 return false;
2059         }
2060
2061         return true;
2062     }
2063 };
2064
2065 this.setClass = function(node, name)
2066 {
2067     if (node && (' '+node.className+' ').indexOf(' '+name+' ') == -1)
2068     ///if (node && !this.hasClass(node, name))
2069         node.className += " " + name;
2070 };
2071
2072 this.getClassValue = function(node, name)
2073 {
2074     var re = new RegExp(name+"-([^ ]+)");
2075     var m = re.exec(node.className);
2076     return m ? m[1] : "";
2077 };
2078
2079 this.removeClass = function(node, name)
2080 {
2081     if (node && node.className)
2082     {
2083         var index = node.className.indexOf(name);
2084         if (index >= 0)
2085         {
2086             var size = name.length;
2087             node.className = node.className.substr(0,index-1) + node.className.substr(index+size);
2088         }
2089     }
2090 };
2091
2092 this.toggleClass = function(elt, name)
2093 {
2094     if ((' '+elt.className+' ').indexOf(' '+name+' ') != -1)
2095     ///if (this.hasClass(elt, name))
2096         this.removeClass(elt, name);
2097     else
2098         this.setClass(elt, name);
2099 };
2100
2101 this.setClassTimed = function(elt, name, context, timeout)
2102 {
2103     if (!timeout)
2104         timeout = 1300;
2105
2106     if (elt.__setClassTimeout)
2107         context.clearTimeout(elt.__setClassTimeout);
2108     else
2109         this.setClass(elt, name);
2110
2111     elt.__setClassTimeout = context.setTimeout(function()
2112     {
2113         delete elt.__setClassTimeout;
2114
2115         FBL.removeClass(elt, name);
2116     }, timeout);
2117 };
2118
2119 this.cancelClassTimed = function(elt, name, context)
2120 {
2121     if (elt.__setClassTimeout)
2122     {
2123         FBL.removeClass(elt, name);
2124         context.clearTimeout(elt.__setClassTimeout);
2125         delete elt.__setClassTimeout;
2126     }
2127 };
2128
2129
2130 // ************************************************************************************************
2131 // DOM queries
2132
2133 this.$ = function(id, doc)
2134 {
2135     if (doc)
2136         return doc.getElementById(id);
2137     else
2138     {
2139         return FBL.Firebug.chrome.document.getElementById(id);
2140     }
2141 };
2142
2143 this.$$ = function(selector, doc)
2144 {
2145     if (doc || !FBL.Firebug.chrome)
2146         return FBL.Firebug.Selector(selector, doc);
2147     else
2148     {
2149         return FBL.Firebug.Selector(selector, FBL.Firebug.chrome.document);
2150     }
2151 };
2152
2153 this.getChildByClass = function(node) // ,classname, classname, classname...
2154 {
2155     for (var i = 1; i < arguments.length; ++i)
2156     {
2157         var className = arguments[i];
2158         var child = node.firstChild;
2159         node = null;
2160         for (; child; child = child.nextSibling)
2161         {
2162             if (this.hasClass(child, className))
2163             {
2164                 node = child;
2165                 break;
2166             }
2167         }
2168     }
2169
2170     return node;
2171 };
2172
2173 this.getAncestorByClass = function(node, className)
2174 {
2175     for (var parent = node; parent; parent = parent.parentNode)
2176     {
2177         if (this.hasClass(parent, className))
2178             return parent;
2179     }
2180
2181     return null;
2182 };
2183
2184
2185 this.getElementsByClass = function(node, className)
2186 {
2187     var result = [];
2188
2189     for (var child = node.firstChild; child; child = child.nextSibling)
2190     {
2191         if (this.hasClass(child, className))
2192             result.push(child);
2193     }
2194
2195     return result;
2196 };
2197
2198 this.getElementByClass = function(node, className)  // className, className, ...
2199 {
2200     var args = cloneArray(arguments); args.splice(0, 1);
2201     for (var child = node.firstChild; child; child = child.nextSibling)
2202     {
2203         var args1 = cloneArray(args); args1.unshift(child);
2204         if (FBL.hasClass.apply(null, args1))
2205             return child;
2206         else
2207         {
2208             var found = FBL.getElementByClass.apply(null, args1);
2209             if (found)
2210                 return found;
2211         }
2212     }
2213
2214     return null;
2215 };
2216
2217 this.isAncestor = function(node, potentialAncestor)
2218 {
2219     for (var parent = node; parent; parent = parent.parentNode)
2220     {
2221         if (parent == potentialAncestor)
2222             return true;
2223     }
2224
2225     return false;
2226 };
2227
2228 this.getNextElement = function(node)
2229 {
2230     while (node && node.nodeType != 1)
2231         node = node.nextSibling;
2232
2233     return node;
2234 };
2235
2236 this.getPreviousElement = function(node)
2237 {
2238     while (node && node.nodeType != 1)
2239         node = node.previousSibling;
2240
2241     return node;
2242 };
2243
2244 this.getBody = function(doc)
2245 {
2246     if (doc.body)
2247         return doc.body;
2248
2249     var body = doc.getElementsByTagName("body")[0];
2250     if (body)
2251         return body;
2252
2253     return doc.firstChild;  // For non-HTML docs
2254 };
2255
2256 this.findNextDown = function(node, criteria)
2257 {
2258     if (!node)
2259         return null;
2260
2261     for (var child = node.firstChild; child; child = child.nextSibling)
2262     {
2263         if (criteria(child))
2264             return child;
2265
2266         var next = this.findNextDown(child, criteria);
2267         if (next)
2268             return next;
2269     }
2270 };
2271
2272 this.findPreviousUp = function(node, criteria)
2273 {
2274     if (!node)
2275         return null;
2276
2277     for (var child = node.lastChild; child; child = child.previousSibling)
2278     {
2279         var next = this.findPreviousUp(child, criteria);
2280         if (next)
2281             return next;
2282
2283         if (criteria(child))
2284             return child;
2285     }
2286 };
2287
2288 this.findNext = function(node, criteria, upOnly, maxRoot)
2289 {
2290     if (!node)
2291         return null;
2292
2293     if (!upOnly)
2294     {
2295         var next = this.findNextDown(node, criteria);
2296         if (next)
2297             return next;
2298     }
2299
2300     for (var sib = node.nextSibling; sib; sib = sib.nextSibling)
2301     {
2302         if (criteria(sib))
2303             return sib;
2304
2305         var next = this.findNextDown(sib, criteria);
2306         if (next)
2307             return next;
2308     }
2309
2310     if (node.parentNode && node.parentNode != maxRoot)
2311         return this.findNext(node.parentNode, criteria, true);
2312 };
2313
2314 this.findPrevious = function(node, criteria, downOnly, maxRoot)
2315 {
2316     if (!node)
2317         return null;
2318
2319     for (var sib = node.previousSibling; sib; sib = sib.previousSibling)
2320     {
2321         var prev = this.findPreviousUp(sib, criteria);
2322         if (prev)
2323             return prev;
2324
2325         if (criteria(sib))
2326             return sib;
2327     }
2328
2329     if (!downOnly)
2330     {
2331         var next = this.findPreviousUp(node, criteria);
2332         if (next)
2333             return next;
2334     }
2335
2336     if (node.parentNode && node.parentNode != maxRoot)
2337     {
2338         if (criteria(node.parentNode))
2339             return node.parentNode;
2340
2341         return this.findPrevious(node.parentNode, criteria, true);
2342     }
2343 };
2344
2345 this.getNextByClass = function(root, state)
2346 {
2347     var iter = function iter(node) { return node.nodeType == 1 && FBL.hasClass(node, state); };
2348     return this.findNext(root, iter);
2349 };
2350
2351 this.getPreviousByClass = function(root, state)
2352 {
2353     var iter = function iter(node) { return node.nodeType == 1 && FBL.hasClass(node, state); };
2354     return this.findPrevious(root, iter);
2355 };
2356
2357 this.isElement = function(o)
2358 {
2359     try {
2360         return o && this.instanceOf(o, "Element");
2361     }
2362     catch (ex) {
2363         return false;
2364     }
2365 };
2366
2367
2368 // ************************************************************************************************
2369 // DOM Modification
2370
2371 // TODO: xxxpedro use doc fragments in Context API
2372 var appendFragment = null;
2373
2374 this.appendInnerHTML = function(element, html, referenceElement)
2375 {
2376     // if undefined, we must convert it to null otherwise it will throw an error in IE
2377     // when executing element.insertBefore(firstChild, referenceElement)
2378     referenceElement = referenceElement || null;
2379
2380     var doc = element.ownerDocument;
2381
2382     // doc.createRange not available in IE
2383     if (doc.createRange)
2384     {
2385         var range = doc.createRange();  // a helper object
2386         range.selectNodeContents(element); // the environment to interpret the html
2387
2388         var fragment = range.createContextualFragment(html);  // parse
2389         var firstChild = fragment.firstChild;
2390         element.insertBefore(fragment, referenceElement);
2391     }
2392     else
2393     {
2394         if (!appendFragment || appendFragment.ownerDocument != doc)
2395             appendFragment = doc.createDocumentFragment();
2396
2397         var div = doc.createElement("div");
2398         div.innerHTML = html;
2399
2400         var firstChild = div.firstChild;
2401         while (div.firstChild)
2402             appendFragment.appendChild(div.firstChild);
2403
2404         element.insertBefore(appendFragment, referenceElement);
2405
2406         div = null;
2407     }
2408
2409     return firstChild;
2410 };
2411
2412
2413 // ************************************************************************************************
2414 // DOM creation
2415
2416 this.createElement = function(tagName, properties)
2417 {
2418     properties = properties || {};
2419     var doc = properties.document || FBL.Firebug.chrome.document;
2420
2421     var element = doc.createElement(tagName);
2422
2423     for(var name in properties)
2424     {
2425         if (name != "document")
2426         {
2427             element[name] = properties[name];
2428         }
2429     }
2430
2431     return element;
2432 };
2433
2434 this.createGlobalElement = function(tagName, properties)
2435 {
2436     properties = properties || {};
2437     var doc = FBL.Env.browser.document;
2438
2439     var element = this.NS && doc.createElementNS ?
2440             doc.createElementNS(FBL.NS, tagName) :
2441             doc.createElement(tagName);
2442
2443     for(var name in properties)
2444     {
2445         var propname = name;
2446         if (FBL.isIE && name == "class") propname = "className";
2447
2448         if (name != "document")
2449         {
2450             element.setAttribute(propname, properties[name]);
2451         }
2452     }
2453
2454     return element;
2455 };
2456
2457 //************************************************************************************************
2458
2459 this.safeGetWindowLocation = function(window)
2460 {
2461     try
2462     {
2463         if (window)
2464         {
2465             if (window.closed)
2466                 return "(window.closed)";
2467             if ("location" in window)
2468                 return window.location+"";
2469             else
2470                 return "(no window.location)";
2471         }
2472         else
2473             return "(no context.window)";
2474     }
2475     catch(exc)
2476     {
2477         if (FBTrace.DBG_WINDOWS || FBTrace.DBG_ERRORS)
2478             FBTrace.sysout("TabContext.getWindowLocation failed "+exc, exc);
2479             FBTrace.sysout("TabContext.getWindowLocation failed window:", window);
2480         return "(getWindowLocation: "+exc+")";
2481     }
2482 };
2483
2484 // ************************************************************************************************
2485 // Events
2486
2487 this.isLeftClick = function(event)
2488 {
2489     return (this.isIE && event.type != "click" && event.type != "dblclick" ?
2490             event.button == 1 : // IE "click" and "dblclick" button model
2491             event.button == 0) && // others
2492         this.noKeyModifiers(event);
2493 };
2494
2495 this.isMiddleClick = function(event)
2496 {
2497     return (this.isIE && event.type != "click" && event.type != "dblclick" ?
2498             event.button == 4 : // IE "click" and "dblclick" button model
2499             event.button == 1) &&
2500         this.noKeyModifiers(event);
2501 };
2502
2503 this.isRightClick = function(event)
2504 {
2505     return (this.isIE && event.type != "click" && event.type != "dblclick" ?
2506             event.button == 2 : // IE "click" and "dblclick" button model
2507             event.button == 2) &&
2508         this.noKeyModifiers(event);
2509 };
2510
2511 this.noKeyModifiers = function(event)
2512 {
2513     return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
2514 };
2515
2516 this.isControlClick = function(event)
2517 {
2518     return (this.isIE && event.type != "click" && event.type != "dblclick" ?
2519             event.button == 1 : // IE "click" and "dblclick" button model
2520             event.button == 0) &&
2521         this.isControl(event);
2522 };
2523
2524 this.isShiftClick = function(event)
2525 {
2526     return (this.isIE && event.type != "click" && event.type != "dblclick" ?
2527             event.button == 1 : // IE "click" and "dblclick" button model
2528             event.button == 0) &&
2529         this.isShift(event);
2530 };
2531
2532 this.isControl = function(event)
2533 {
2534     return (event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey;
2535 };
2536
2537 this.isAlt = function(event)
2538 {
2539     return event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey;
2540 };
2541
2542 this.isAltClick = function(event)
2543 {
2544     return (this.isIE && event.type != "click" && event.type != "dblclick" ?
2545             event.button == 1 : // IE "click" and "dblclick" button model
2546             event.button == 0) &&
2547         this.isAlt(event);
2548 };
2549
2550 this.isControlShift = function(event)
2551 {
2552     return (event.metaKey || event.ctrlKey) && event.shiftKey && !event.altKey;
2553 };
2554
2555 this.isShift = function(event)
2556 {
2557     return event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey;
2558 };
2559
2560 this.addEvent = function(object, name, handler, useCapture)
2561 {
2562     if (object.addEventListener)
2563         object.addEventListener(name, handler, useCapture);
2564     else
2565         object.attachEvent("on"+name, handler);
2566 };
2567
2568 this.removeEvent = function(object, name, handler, useCapture)
2569 {
2570     try
2571     {
2572         if (object.removeEventListener)
2573             object.removeEventListener(name, handler, useCapture);
2574         else
2575             object.detachEvent("on"+name, handler);
2576     }
2577     catch(e)
2578     {
2579         if (FBTrace.DBG_ERRORS)
2580             FBTrace.sysout("FBL.removeEvent error: ", object, name);
2581     }
2582 };
2583
2584 this.cancelEvent = function(e, preventDefault)
2585 {
2586     if (!e) return;
2587
2588     if (preventDefault)
2589     {
2590                 if (e.preventDefault)
2591                     e.preventDefault();
2592                 else
2593                     e.returnValue = false;
2594     }
2595
2596     if (e.stopPropagation)
2597         e.stopPropagation();
2598     else
2599         e.cancelBubble = true;
2600 };
2601
2602 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2603
2604 this.addGlobalEvent = function(name, handler)
2605 {
2606     var doc = this.Firebug.browser.document;
2607     var frames = this.Firebug.browser.window.frames;
2608
2609     this.addEvent(doc, name, handler);
2610
2611     if (this.Firebug.chrome.type == "popup")
2612         this.addEvent(this.Firebug.chrome.document, name, handler);
2613
2614     for (var i = 0, frame; frame = frames[i]; i++)
2615     {
2616         try
2617         {
2618             this.addEvent(frame.document, name, handler);
2619         }
2620         catch(E)
2621         {
2622             // Avoid acess denied
2623         }
2624     }
2625 };
2626
2627 this.removeGlobalEvent = function(name, handler)
2628 {
2629     var doc = this.Firebug.browser.document;
2630     var frames = this.Firebug.browser.window.frames;
2631
2632     this.removeEvent(doc, name, handler);
2633
2634     if (this.Firebug.chrome.type == "popup")
2635         this.removeEvent(this.Firebug.chrome.document, name, handler);
2636
2637     for (var i = 0, frame; frame = frames[i]; i++)
2638     {
2639         try
2640         {
2641             this.removeEvent(frame.document, name, handler);
2642         }
2643         catch(E)
2644         {
2645             // Avoid acess denied
2646         }
2647     }
2648 };
2649
2650 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2651
2652 this.dispatch = function(listeners, name, args)
2653 {
2654     if (!listeners) return;
2655
2656     try
2657     {/**/
2658         if (typeof listeners.length != "undefined")
2659         {
2660             if (FBTrace.DBG_DISPATCH) FBTrace.sysout("FBL.dispatch", name+" to "+listeners.length+" listeners");
2661
2662             for (var i = 0; i < listeners.length; ++i)
2663             {
2664                 var listener = listeners[i];
2665                 if ( listener[name] )
2666                     listener[name].apply(listener, args);
2667             }
2668         }
2669         else
2670         {
2671             if (FBTrace.DBG_DISPATCH) FBTrace.sysout("FBL.dispatch", name+" to listeners of an object");
2672
2673             for (var prop in listeners)
2674             {
2675                 var listener = listeners[prop];
2676                 if ( listener[name] )
2677                     listener[name].apply(listener, args);
2678             }
2679         }
2680     }
2681     catch (exc)
2682     {
2683         if (FBTrace.DBG_ERRORS)
2684         {
2685             FBTrace.sysout(" Exception in lib.dispatch "+ name, exc);
2686             //FBTrace.dumpProperties(" Exception in lib.dispatch listener", listener);
2687         }
2688     }
2689     /**/
2690 };
2691
2692 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2693
2694 var disableTextSelectionHandler = function(event)
2695 {
2696     FBL.cancelEvent(event, true);
2697
2698     return false;
2699 };
2700
2701 this.disableTextSelection = function(e)
2702 {
2703     if (typeof e.onselectstart != "undefined") // IE
2704         this.addEvent(e, "selectstart", disableTextSelectionHandler);
2705
2706     else // others
2707     {
2708         e.style.cssText = "user-select: none; -khtml-user-select: none; -moz-user-select: none;";
2709
2710         // canceling the event in FF will prevent the menu popups to close when clicking over
2711         // text-disabled elements
2712         if (!this.isFirefox)
2713             this.addEvent(e, "mousedown", disableTextSelectionHandler);
2714     }
2715
2716     e.style.cursor = "default";
2717 };
2718
2719 this.restoreTextSelection = function(e)
2720 {
2721     if (typeof e.onselectstart != "undefined") // IE
2722         this.removeEvent(e, "selectstart", disableTextSelectionHandler);
2723
2724     else // others
2725     {
2726         e.style.cssText = "cursor: default;";
2727
2728         // canceling the event in FF will prevent the menu popups to close when clicking over
2729         // text-disabled elements
2730         if (!this.isFirefox)
2731             this.removeEvent(e, "mousedown", disableTextSelectionHandler);
2732     }
2733 };
2734
2735 // ************************************************************************************************
2736 // DOM Events
2737
2738 var eventTypes =
2739 {
2740     composition: [
2741         "composition",
2742         "compositionstart",
2743         "compositionend" ],
2744     contextmenu: [
2745         "contextmenu" ],
2746     drag: [
2747         "dragenter",
2748         "dragover",
2749         "dragexit",
2750         "dragdrop",
2751         "draggesture" ],
2752     focus: [
2753         "focus",
2754         "blur" ],
2755     form: [
2756         "submit",
2757         "reset",
2758         "change",
2759         "select",
2760         "input" ],
2761     key: [
2762         "keydown",
2763         "keyup",
2764         "keypress" ],
2765     load: [
2766         "load",
2767         "beforeunload",
2768         "unload",
2769         "abort",
2770         "error" ],
2771     mouse: [
2772         "mousedown",
2773         "mouseup",
2774         "click",
2775         "dblclick",
2776         "mouseover",
2777         "mouseout",
2778         "mousemove" ],
2779     mutation: [
2780         "DOMSubtreeModified",
2781         "DOMNodeInserted",
2782         "DOMNodeRemoved",
2783         "DOMNodeRemovedFromDocument",
2784         "DOMNodeInsertedIntoDocument",
2785         "DOMAttrModified",
2786         "DOMCharacterDataModified" ],
2787     paint: [
2788         "paint",
2789         "resize",
2790         "scroll" ],
2791     scroll: [
2792         "overflow",
2793         "underflow",
2794         "overflowchanged" ],
2795     text: [
2796         "text" ],
2797     ui: [
2798         "DOMActivate",
2799         "DOMFocusIn",
2800         "DOMFocusOut" ],
2801     xul: [
2802         "popupshowing",
2803         "popupshown",
2804         "popuphiding",
2805         "popuphidden",
2806         "close",
2807         "command",
2808         "broadcast",
2809         "commandupdate" ]
2810 };
2811
2812 this.getEventFamily = function(eventType)
2813 {
2814     if (!this.families)
2815     {
2816         this.families = {};
2817
2818         for (var family in eventTypes)
2819         {
2820             var types = eventTypes[family];
2821             for (var i = 0; i < types.length; ++i)
2822                 this.families[types[i]] = family;
2823         }
2824     }
2825
2826     return this.families[eventType];
2827 };
2828
2829
2830 // ************************************************************************************************
2831 // URLs
2832
2833 this.getFileName = function(url)
2834 {
2835     var split = this.splitURLBase(url);
2836     return split.name;
2837 };
2838
2839 this.splitURLBase = function(url)
2840 {
2841     if (this.isDataURL(url))
2842         return this.splitDataURL(url);
2843     return this.splitURLTrue(url);
2844 };
2845
2846 this.splitDataURL = function(url)
2847 {
2848     var mark = url.indexOf(':', 3);
2849     if (mark != 4)
2850         return false;   //  the first 5 chars must be 'data:'
2851
2852     var point = url.indexOf(',', mark+1);
2853     if (point < mark)
2854         return false; // syntax error
2855
2856     var props = { encodedContent: url.substr(point+1) };
2857
2858     var metadataBuffer = url.substr(mark+1, point);
2859     var metadata = metadataBuffer.split(';');
2860     for (var i = 0; i < metadata.length; i++)
2861     {
2862         var nv = metadata[i].split('=');
2863         if (nv.length == 2)
2864             props[nv[0]] = nv[1];
2865     }
2866
2867     // Additional Firebug-specific properties
2868     if (props.hasOwnProperty('fileName'))
2869     {
2870          var caller_URL = decodeURIComponent(props['fileName']);
2871          var caller_split = this.splitURLTrue(caller_URL);
2872
2873         if (props.hasOwnProperty('baseLineNumber'))  // this means it's probably an eval()
2874         {
2875             props['path'] = caller_split.path;
2876             props['line'] = props['baseLineNumber'];
2877             var hint = decodeURIComponent(props['encodedContent'].substr(0,200)).replace(/\s*$/, "");
2878             props['name'] =  'eval->'+hint;
2879         }
2880         else
2881         {
2882             props['name'] = caller_split.name;
2883             props['path'] = caller_split.path;
2884         }
2885     }
2886     else
2887     {
2888         if (!props.hasOwnProperty('path'))
2889             props['path'] = "data:";
2890         if (!props.hasOwnProperty('name'))
2891             props['name'] =  decodeURIComponent(props['encodedContent'].substr(0,200)).replace(/\s*$/, "");
2892     }
2893
2894     return props;
2895 };
2896
2897 this.splitURLTrue = function(url)
2898 {
2899     var m = reSplitFile.exec(url);
2900     if (!m)
2901         return {name: url, path: url};
2902     else if (!m[2])
2903         return {path: m[1], name: m[1]};
2904     else
2905         return {path: m[1], name: m[2]+m[3]};
2906 };
2907
2908 this.getFileExtension = function(url)
2909 {
2910     if (!url)
2911         return null;
2912
2913     // Remove query string from the URL if any.
2914     var queryString = url.indexOf("?");
2915     if (queryString != -1)
2916         url = url.substr(0, queryString);
2917
2918     // Now get the file extension.
2919     var lastDot = url.lastIndexOf(".");
2920     return url.substr(lastDot+1);
2921 };
2922
2923 this.isSystemURL = function(url)
2924 {
2925     if (!url) return true;
2926     if (url.length == 0) return true;
2927     if (url[0] == 'h') return false;
2928     if (url.substr(0, 9) == "resource:")
2929         return true;
2930     else if (url.substr(0, 16) == "chrome://firebug")
2931         return true;
2932     else if (url  == "XPCSafeJSObjectWrapper.cpp")
2933         return true;
2934     else if (url.substr(0, 6) == "about:")
2935         return true;
2936     else if (url.indexOf("firebug-service.js") != -1)
2937         return true;
2938     else
2939         return false;
2940 };
2941
2942 this.isSystemPage = function(win)
2943 {
2944     try
2945     {
2946         var doc = win.document;
2947         if (!doc)
2948             return false;
2949
2950         // Detect pages for pretty printed XML
2951         if ((doc.styleSheets.length && doc.styleSheets[0].href
2952                 == "chrome://global/content/xml/XMLPrettyPrint.css")
2953             || (doc.styleSheets.length > 1 && doc.styleSheets[1].href
2954                 == "chrome://browser/skin/feeds/subscribe.css"))
2955             return true;
2956
2957         return FBL.isSystemURL(win.location.href);
2958     }
2959     catch (exc)
2960     {
2961         // Sometimes documents just aren't ready to be manipulated here, but don't let that
2962         // gum up the works
2963         ERROR("tabWatcher.isSystemPage document not ready:"+ exc);
2964         return false;
2965     }
2966 };
2967
2968 this.isSystemStyleSheet = function(sheet)
2969 {
2970     var href = sheet && sheet.href;
2971     return href && FBL.isSystemURL(href);
2972 };
2973
2974 this.getURIHost = function(uri)
2975 {
2976     try
2977     {
2978         if (uri)
2979             return uri.host;
2980         else
2981             return "";
2982     }
2983     catch (exc)
2984     {
2985         return "";
2986     }
2987 };
2988
2989 this.isLocalURL = function(url)
2990 {
2991     if (url.substr(0, 5) == "file:")
2992         return true;
2993     else if (url.substr(0, 8) == "wyciwyg:")
2994         return true;
2995     else
2996         return false;
2997 };
2998
2999 this.isDataURL = function(url)
3000 {
3001     return (url && url.substr(0,5) == "data:");
3002 };
3003
3004 this.getLocalPath = function(url)
3005 {
3006     if (this.isLocalURL(url))
3007     {
3008         var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
3009         var file = fileHandler.getFileFromURLSpec(url);
3010         return file.path;
3011     }
3012 };
3013
3014 this.getURLFromLocalFile = function(file)
3015 {
3016     var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
3017     var URL = fileHandler.getURLSpecFromFile(file);
3018     return URL;
3019 };
3020
3021 this.getDataURLForContent = function(content, url)
3022 {
3023     // data:text/javascript;fileName=x%2Cy.js;baseLineNumber=10,<the-url-encoded-data>
3024     var uri = "data:text/html;";
3025     uri += "fileName="+encodeURIComponent(url)+ ",";
3026     uri += encodeURIComponent(content);
3027     return uri;
3028 },
3029
3030 this.getDomain = function(url)
3031 {
3032     var m = /[^:]+:\/{1,3}([^\/]+)/.exec(url);
3033     return m ? m[1] : "";
3034 };
3035
3036 this.getURLPath = function(url)
3037 {
3038     var m = /[^:]+:\/{1,3}[^\/]+(\/.*?)$/.exec(url);
3039     return m ? m[1] : "";
3040 };
3041
3042 this.getPrettyDomain = function(url)
3043 {
3044     var m = /[^:]+:\/{1,3}(www\.)?([^\/]+)/.exec(url);
3045     return m ? m[2] : "";
3046 };
3047
3048 this.absoluteURL = function(url, baseURL)
3049 {
3050     return this.absoluteURLWithDots(url, baseURL).replace("/./", "/", "g");
3051 };
3052
3053 this.absoluteURLWithDots = function(url, baseURL)
3054 {
3055     if (url[0] == "?")
3056         return baseURL + url;
3057
3058     var reURL = /(([^:]+:)\/{1,2}[^\/]*)(.*?)$/;
3059     var m = reURL.exec(url);
3060     if (m)
3061         return url;
3062
3063     var m = reURL.exec(baseURL);
3064     if (!m)
3065         return "";
3066
3067     var head = m[1];
3068     var tail = m[3];
3069     if (url.substr(0, 2) == "//")
3070         return m[2] + url;
3071     else if (url[0] == "/")
3072     {
3073         return head + url;
3074     }
3075     else if (tail[tail.length-1] == "/")
3076         return baseURL + url;
3077     else
3078     {
3079         var parts = tail.split("/");
3080         return head + parts.slice(0, parts.length-1).join("/") + "/" + url;
3081     }
3082 };
3083
3084 this.normalizeURL = function(url)  // this gets called a lot, any performance improvement welcome
3085 {
3086     if (!url)
3087         return "";
3088     // Replace one or more characters that are not forward-slash followed by /.., by space.
3089     if (url.length < 255) // guard against monsters.
3090     {
3091         // Replace one or more characters that are not forward-slash followed by /.., by space.
3092         url = url.replace(/[^\/]+\/\.\.\//, "", "g");
3093         // Issue 1496, avoid #
3094         url = url.replace(/#.*/,"");
3095         // For some reason, JSDS reports file URLs like "file:/" instead of "file:///", so they
3096         // don't match up with the URLs we get back from the DOM
3097         url = url.replace(/file:\/([^\/])/g, "file:///$1");
3098         if (url.indexOf('chrome:')==0)
3099         {
3100             var m = reChromeCase.exec(url);  // 1 is package name, 2 is path
3101             if (m)
3102             {
3103                 url = "chrome://"+m[1].toLowerCase()+"/"+m[2];
3104             }
3105         }
3106     }
3107     return url;
3108 };
3109
3110 this.denormalizeURL = function(url)
3111 {
3112     return url.replace(/file:\/\/\//g, "file:/");
3113 };
3114
3115 this.parseURLParams = function(url)
3116 {
3117     var q = url ? url.indexOf("?") : -1;
3118     if (q == -1)
3119         return [];
3120
3121     var search = url.substr(q+1);
3122     var h = search.lastIndexOf("#");
3123     if (h != -1)
3124         search = search.substr(0, h);
3125
3126     if (!search)
3127         return [];
3128
3129     return this.parseURLEncodedText(search);
3130 };
3131
3132 this.parseURLEncodedText = function(text)
3133 {
3134     var maxValueLength = 25000;
3135
3136     var params = [];
3137
3138     // Unescape '+' characters that are used to encode a space.
3139     // See section 2.2.in RFC 3986: http://www.ietf.org/rfc/rfc3986.txt
3140     text = text.replace(/\+/g, " ");
3141
3142     var args = text.split("&");
3143     for (var i = 0; i < args.length; ++i)
3144     {
3145         try {
3146             var parts = args[i].split("=");
3147             if (parts.length == 2)
3148             {
3149                 if (parts[1].length > maxValueLength)
3150                     parts[1] = this.$STR("LargeData");
3151
3152                 params.push({name: decodeURIComponent(parts[0]), value: decodeURIComponent(parts[1])});
3153             }
3154             else
3155                 params.push({name: decodeURIComponent(parts[0]), value: ""});
3156         }
3157         catch (e)
3158         {
3159             if (FBTrace.DBG_ERRORS)
3160             {
3161                 FBTrace.sysout("parseURLEncodedText EXCEPTION ", e);
3162                 FBTrace.sysout("parseURLEncodedText EXCEPTION URI", args[i]);
3163             }
3164         }
3165     }
3166
3167     params.sort(function(a, b) { return a.name <= b.name ? -1 : 1; });
3168
3169     return params;
3170 };
3171
3172 // TODO: xxxpedro lib. why loops in domplate are requiring array in parameters
3173 // as in response/request headers and get/post parameters in Net module?
3174 this.parseURLParamsArray = function(url)
3175 {
3176     var q = url ? url.indexOf("?") : -1;
3177     if (q == -1)
3178         return [];
3179
3180     var search = url.substr(q+1);
3181     var h = search.lastIndexOf("#");
3182     if (h != -1)
3183         search = search.substr(0, h);
3184
3185     if (!search)
3186         return [];
3187
3188     return this.parseURLEncodedTextArray(search);
3189 };
3190
3191 this.parseURLEncodedTextArray = function(text)
3192 {
3193     var maxValueLength = 25000;
3194
3195     var params = [];
3196
3197     // Unescape '+' characters that are used to encode a space.
3198     // See section 2.2.in RFC 3986: http://www.ietf.org/rfc/rfc3986.txt
3199     text = text.replace(/\+/g, " ");
3200
3201     var args = text.split("&");
3202     for (var i = 0; i < args.length; ++i)
3203     {
3204         try {
3205             var parts = args[i].split("=");
3206             if (parts.length == 2)
3207             {
3208                 if (parts[1].length > maxValueLength)
3209                     parts[1] = this.$STR("LargeData");
3210
3211                 params.push({name: decodeURIComponent(parts[0]), value: [decodeURIComponent(parts[1])]});
3212             }
3213             else
3214                 params.push({name: decodeURIComponent(parts[0]), value: [""]});
3215         }
3216         catch (e)
3217         {
3218             if (FBTrace.DBG_ERRORS)
3219             {
3220                 FBTrace.sysout("parseURLEncodedText EXCEPTION ", e);
3221                 FBTrace.sysout("parseURLEncodedText EXCEPTION URI", args[i]);
3222             }
3223         }
3224     }
3225
3226     params.sort(function(a, b) { return a.name <= b.name ? -1 : 1; });
3227
3228     return params;
3229 };
3230
3231 this.reEncodeURL = function(file, text)
3232 {
3233     var lines = text.split("\n");
3234     var params = this.parseURLEncodedText(lines[lines.length-1]);
3235
3236     var args = [];
3237     for (var i = 0; i < params.length; ++i)
3238         args.push(encodeURIComponent(params[i].name)+"="+encodeURIComponent(params[i].value));
3239
3240     var url = file.href;
3241     url += (url.indexOf("?") == -1 ? "?" : "&") + args.join("&");
3242
3243     return url;
3244 };
3245
3246 this.getResource = function(aURL)
3247 {
3248     try
3249     {
3250         var channel=ioService.newChannel(aURL,null,null);
3251         var input=channel.open();
3252         return FBL.readFromStream(input);
3253     }
3254     catch (e)
3255     {
3256         if (FBTrace.DBG_ERRORS)
3257             FBTrace.sysout("lib.getResource FAILS for "+aURL, e);
3258     }
3259 };
3260
3261 this.parseJSONString = function(jsonString, originURL)
3262 {
3263     // See if this is a Prototype style *-secure request.
3264     var regex = new RegExp(/^\/\*-secure-([\s\S]*)\*\/\s*$/);
3265     var matches = regex.exec(jsonString);
3266
3267     if (matches)
3268     {
3269         jsonString = matches[1];
3270
3271         if (jsonString[0] == "\\" && jsonString[1] == "n")
3272             jsonString = jsonString.substr(2);
3273
3274         if (jsonString[jsonString.length-2] == "\\" && jsonString[jsonString.length-1] == "n")
3275             jsonString = jsonString.substr(0, jsonString.length-2);
3276     }
3277
3278     if (jsonString.indexOf("&&&START&&&"))
3279     {
3280         regex = new RegExp(/&&&START&&& (.+) &&&END&&&/);
3281         matches = regex.exec(jsonString);
3282         if (matches)
3283             jsonString = matches[1];
3284     }
3285
3286     // throw on the extra parentheses
3287     jsonString = "(" + jsonString + ")";
3288
3289     ///var s = Components.utils.Sandbox(originURL);
3290     var jsonObject = null;
3291
3292     try
3293     {
3294         ///jsonObject = Components.utils.evalInSandbox(jsonString, s);
3295
3296         //jsonObject = Firebug.context.eval(jsonString);
3297         jsonObject = Firebug.context.evaluate(jsonString, null, null, function(){return null;});
3298     }
3299     catch(e)
3300     {
3301         /***
3302         if (e.message.indexOf("is not defined"))
3303         {
3304             var parts = e.message.split(" ");
3305             s[parts[0]] = function(str){ return str; };
3306             try {
3307                 jsonObject = Components.utils.evalInSandbox(jsonString, s);
3308             } catch(ex) {
3309                 if (FBTrace.DBG_ERRORS || FBTrace.DBG_JSONVIEWER)
3310                     FBTrace.sysout("jsonviewer.parseJSON EXCEPTION", e);
3311                 return null;
3312             }
3313         }
3314         else
3315         {/**/
3316             if (FBTrace.DBG_ERRORS || FBTrace.DBG_JSONVIEWER)
3317                 FBTrace.sysout("jsonviewer.parseJSON EXCEPTION", e);
3318             return null;
3319         ///}
3320     }
3321
3322     return jsonObject;
3323 };
3324
3325 // ************************************************************************************************
3326
3327 this.objectToString = function(object)
3328 {
3329     try
3330     {
3331         return object+"";
3332     }
3333     catch (exc)
3334     {
3335         return null;
3336     }
3337 };
3338
3339 // ************************************************************************************************
3340 // Input Caret Position
3341
3342 this.setSelectionRange = function(input, start, length)
3343 {
3344     if (input.createTextRange)
3345     {
3346         var range = input.createTextRange();
3347         range.moveStart("character", start);
3348         range.moveEnd("character", length - input.value.length);
3349         range.select();
3350     }
3351     else if (input.setSelectionRange)
3352     {
3353         input.setSelectionRange(start, length);
3354         input.focus();
3355     }
3356 };
3357
3358 // ************************************************************************************************
3359 // Input Selection Start / Caret Position
3360
3361 this.getInputSelectionStart = function(input)
3362 {
3363     if (document.selection)
3364     {
3365         var range = input.ownerDocument.selection.createRange();
3366         var text = range.text;
3367
3368         //console.log("range", range.text);
3369
3370         // if there is a selection, find the start position
3371         if (text)
3372         {
3373             return input.value.indexOf(text);
3374         }
3375         // if there is no selection, find the caret position
3376         else
3377         {
3378             range.moveStart("character", -input.value.length);
3379
3380             return range.text.length;
3381         }
3382     }
3383     else if (typeof input.selectionStart != "undefined")
3384         return input.selectionStart;
3385
3386     return 0;
3387 };
3388
3389 // ************************************************************************************************
3390 // Opera Tab Fix
3391
3392 function onOperaTabBlur(e)
3393 {
3394     if (this.lastKey == 9)
3395       this.focus();
3396 };
3397
3398 function onOperaTabKeyDown(e)
3399 {
3400     this.lastKey = e.keyCode;
3401 };
3402
3403 function onOperaTabFocus(e)
3404 {
3405     this.lastKey = null;
3406 };
3407
3408 this.fixOperaTabKey = function(el)
3409 {
3410     el.onfocus = onOperaTabFocus;
3411     el.onblur = onOperaTabBlur;
3412     el.onkeydown = onOperaTabKeyDown;
3413 };
3414
3415 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3416
3417 this.Property = function(object, name)
3418 {
3419     this.object = object;
3420     this.name = name;
3421
3422     this.getObject = function()
3423     {
3424         return object[name];
3425     };
3426 };
3427
3428 this.ErrorCopy = function(message)
3429 {
3430     this.message = message;
3431 };
3432
3433 function EventCopy(event)
3434 {
3435     // Because event objects are destroyed arbitrarily by Gecko, we must make a copy of them to
3436     // represent them long term in the inspector.
3437     for (var name in event)
3438     {
3439         try {
3440             this[name] = event[name];
3441         } catch (exc) { }
3442     }
3443 }
3444
3445 this.EventCopy = EventCopy;
3446
3447
3448 // ************************************************************************************************
3449 // Type Checking
3450
3451 var toString = Object.prototype.toString;
3452 var reFunction = /^\s*function(\s+[\w_$][\w\d_$]*)?\s*\(/;
3453
3454 this.isArray = function(object) {
3455     return toString.call(object) === '[object Array]';
3456 };
3457
3458 this.isFunction = function(object) {
3459     if (!object) return false;
3460
3461     try
3462     {
3463         // FIXME: xxxpedro this is failing in IE for the global "external" object
3464         return toString.call(object) === "[object Function]" ||
3465                 this.isIE && typeof object != "string" && reFunction.test(""+object);
3466     }
3467     catch (E)
3468     {
3469         FBTrace.sysout("Lib.isFunction() failed for ", object);
3470         return false;
3471     }
3472 };
3473
3474
3475 // ************************************************************************************************
3476 // Instance Checking
3477
3478 this.instanceOf = function(object, className)
3479 {
3480     if (!object || typeof object != "object")
3481         return false;
3482
3483     // Try to use the native instanceof operator. We can only use it when we know
3484     // exactly the window where the object is located at
3485     if (object.ownerDocument)
3486     {
3487         // find the correct window of the object
3488         var win = object.ownerDocument.defaultView || object.ownerDocument.parentWindow;
3489
3490         // if the class is accessible in the window, uses the native instanceof operator
3491         // if the instanceof evaluates to "true" we can assume it is a instance, but if it
3492         // evaluates to "false" we must continue with the duck type detection below because
3493         // the native object may be extended, thus breaking the instanceof result
3494         // See Issue 3524: Firebug Lite Style Panel doesn't work if the native Element is extended
3495         if (className in win && object instanceof win[className])
3496             return true;
3497     }
3498     // If the object doesn't have the ownerDocument property, we'll try to look at
3499     // the current context's window
3500     else
3501     {
3502         // TODO: xxxpedro context
3503         // Since we're not using yet a Firebug.context, we'll just use the top window
3504         // (browser) as a reference
3505         var win = Firebug.browser.window;
3506         if (className in win)
3507             return object instanceof win[className];
3508     }
3509
3510     // get the duck type model from the cache
3511     var cache = instanceCheckMap[className];
3512     if (!cache)
3513         return false;
3514
3515     // starts the hacky duck type detection
3516     for(var n in cache)
3517     {
3518         var obj = cache[n];
3519         var type = typeof obj;
3520         obj = type == "object" ? obj : [obj];
3521
3522         for(var name in obj)
3523         {
3524             // avoid problems with extended native objects
3525             // See Issue 3524: Firebug Lite Style Panel doesn't work if the native Element is extended
3526             if (!obj.hasOwnProperty(name))
3527                 continue;
3528
3529             var value = obj[name];
3530
3531             if( n == "property" && !(value in object) ||
3532                 n == "method" && !this.isFunction(object[value]) ||
3533                 n == "value" && (""+object[name]).toLowerCase() != (""+value).toLowerCase() )
3534                     return false;
3535         }
3536     }
3537
3538     return true;
3539 };
3540
3541 var instanceCheckMap =
3542 {
3543     // DuckTypeCheck:
3544     // {
3545     //     property: ["window", "document"],
3546     //     method: "setTimeout",
3547     //     value: {nodeType: 1}
3548     // },
3549
3550     Window:
3551     {
3552         property: ["window", "document"],
3553         method: "setTimeout"
3554     },
3555
3556     Document:
3557     {
3558         property: ["body", "cookie"],
3559         method: "getElementById"
3560     },
3561
3562     Node:
3563     {
3564         property: "ownerDocument",
3565         method: "appendChild"
3566     },
3567
3568     Element:
3569     {
3570         property: "tagName",
3571         value: {nodeType: 1}
3572     },
3573
3574     Location:
3575     {
3576         property: ["hostname", "protocol"],
3577         method: "assign"
3578     },
3579
3580     HTMLImageElement:
3581     {
3582         property: "useMap",
3583         value:
3584         {
3585             nodeType: 1,
3586             tagName: "img"
3587         }
3588     },
3589
3590     HTMLAnchorElement:
3591     {
3592         property: "hreflang",
3593         value:
3594         {
3595             nodeType: 1,
3596             tagName: "a"
3597         }
3598     },
3599
3600     HTMLInputElement:
3601     {
3602         property: "form",
3603         value:
3604         {
3605             nodeType: 1,
3606             tagName: "input"
3607         }
3608     },
3609
3610     HTMLButtonElement:
3611     {
3612         // ?
3613     },
3614
3615     HTMLFormElement:
3616     {
3617         method: "submit",
3618         value:
3619         {
3620             nodeType: 1,
3621             tagName: "form"
3622         }
3623     },
3624
3625     HTMLBodyElement:
3626     {
3627
3628     },
3629
3630     HTMLHtmlElement:
3631     {
3632
3633     },
3634
3635     CSSStyleRule:
3636     {
3637         property: ["selectorText", "style"]
3638     }
3639
3640 };
3641
3642
3643 // ************************************************************************************************
3644 // DOM Constants
3645
3646 /*
3647
3648 Problems:
3649
3650   - IE does not have window.Node, window.Element, etc
3651   - for (var name in Node.prototype) return nothing on FF
3652
3653 */
3654
3655
3656 var domMemberMap2 = {};
3657
3658 var domMemberMap2Sandbox = null;
3659
3660 var getDomMemberMap2 = function(name)
3661 {
3662     if (!domMemberMap2Sandbox)
3663     {
3664         var doc = Firebug.chrome.document;
3665         var frame = doc.createElement("iframe");
3666
3667         frame.id = "FirebugSandbox";
3668         frame.style.display = "none";
3669         frame.src = "about:blank";
3670
3671         doc.body.appendChild(frame);
3672
3673         domMemberMap2Sandbox = frame.window || frame.contentWindow;
3674     }
3675
3676     var props = [];
3677
3678     //var object = domMemberMap2Sandbox[name];
3679     //object = object.prototype || object;
3680
3681     var object = null;
3682
3683     if (name == "Window")
3684         object = domMemberMap2Sandbox.window;
3685
3686     else if (name == "Document")
3687         object = domMemberMap2Sandbox.document;
3688
3689     else if (name == "HTMLScriptElement")
3690         object = domMemberMap2Sandbox.document.createElement("script");
3691
3692     else if (name == "HTMLAnchorElement")
3693         object = domMemberMap2Sandbox.document.createElement("a");
3694
3695     else if (name.indexOf("Element") != -1)
3696     {
3697         object = domMemberMap2Sandbox.document.createElement("div");
3698     }
3699
3700     if (object)
3701     {
3702         //object = object.prototype || object;
3703
3704         //props  = 'addEventListener,document,location,navigator,window'.split(',');
3705
3706         for (var n in object)
3707           props.push(n);
3708     }
3709     /**/
3710
3711     return props;
3712     return extendArray(props, domMemberMap[name]);
3713 };
3714
3715 // xxxpedro experimental get DOM members
3716 this.getDOMMembers = function(object)
3717 {
3718     if (!domMemberCache)
3719     {
3720         FBL.domMemberCache = domMemberCache = {};
3721
3722         for (var name in domMemberMap)
3723         {
3724             var builtins = getDomMemberMap2(name);
3725             var cache = domMemberCache[name] = {};
3726
3727             /*
3728             if (name.indexOf("Element") != -1)
3729             {
3730                 this.append(cache, this.getDOMMembers("Node"));
3731                 this.append(cache, this.getDOMMembers("Element"));
3732             }
3733             /**/
3734
3735             for (var i = 0; i < builtins.length; ++i)
3736                 cache[builtins[i]] = i;
3737         }
3738     }
3739
3740     try
3741     {
3742         if (this.instanceOf(object, "Window"))
3743             { return domMemberCache.Window; }
3744         else if (this.instanceOf(object, "Document") || this.instanceOf(object, "XMLDocument"))
3745             { return domMemberCache.Document; }
3746         else if (this.instanceOf(object, "Location"))
3747             { return domMemberCache.Location; }
3748         else if (this.instanceOf(object, "HTMLImageElement"))
3749             { return domMemberCache.HTMLImageElement; }
3750         else if (this.instanceOf(object, "HTMLAnchorElement"))
3751             { return domMemberCache.HTMLAnchorElement; }
3752         else if (this.instanceOf(object, "HTMLInputElement"))
3753             { return domMemberCache.HTMLInputElement; }
3754         else if (this.instanceOf(object, "HTMLButtonElement"))
3755             { return domMemberCache.HTMLButtonElement; }
3756         else if (this.instanceOf(object, "HTMLFormElement"))
3757             { return domMemberCache.HTMLFormElement; }
3758         else if (this.instanceOf(object, "HTMLBodyElement"))
3759             { return domMemberCache.HTMLBodyElement; }
3760         else if (this.instanceOf(object, "HTMLHtmlElement"))
3761             { return domMemberCache.HTMLHtmlElement; }
3762         else if (this.instanceOf(object, "HTMLScriptElement"))
3763             { return domMemberCache.HTMLScriptElement; }
3764         else if (this.instanceOf(object, "HTMLTableElement"))
3765             { return domMemberCache.HTMLTableElement; }
3766         else if (this.instanceOf(object, "HTMLTableRowElement"))
3767             { return domMemberCache.HTMLTableRowElement; }
3768         else if (this.instanceOf(object, "HTMLTableCellElement"))
3769             { return domMemberCache.HTMLTableCellElement; }
3770         else if (this.instanceOf(object, "HTMLIFrameElement"))
3771             { return domMemberCache.HTMLIFrameElement; }
3772         else if (this.instanceOf(object, "SVGSVGElement"))
3773             { return domMemberCache.SVGSVGElement; }
3774         else if (this.instanceOf(object, "SVGElement"))
3775             { return domMemberCache.SVGElement; }
3776         else if (this.instanceOf(object, "Element"))
3777             { return domMemberCache.Element; }
3778         else if (this.instanceOf(object, "Text") || this.instanceOf(object, "CDATASection"))
3779             { return domMemberCache.Text; }
3780         else if (this.instanceOf(object, "Attr"))
3781             { return domMemberCache.Attr; }
3782         else if (this.instanceOf(object, "Node"))
3783             { return domMemberCache.Node; }
3784         else if (this.instanceOf(object, "Event") || this.instanceOf(object, "EventCopy"))
3785             { return domMemberCache.Event; }
3786         else
3787             return {};
3788     }
3789     catch(E)
3790     {
3791         if (FBTrace.DBG_ERRORS)
3792             FBTrace.sysout("lib.getDOMMembers FAILED ", E);
3793
3794         return {};
3795     }
3796 };
3797
3798
3799 /*
3800 this.getDOMMembers = function(object)
3801 {
3802     if (!domMemberCache)
3803     {
3804         domMemberCache = {};
3805
3806         for (var name in domMemberMap)
3807         {
3808             var builtins = domMemberMap[name];
3809             var cache = domMemberCache[name] = {};
3810
3811             for (var i = 0; i < builtins.length; ++i)
3812                 cache[builtins[i]] = i;
3813         }
3814     }
3815
3816     try
3817     {
3818         if (this.instanceOf(object, "Window"))
3819             { return domMemberCache.Window; }
3820         else if (object instanceof Document || object instanceof XMLDocument)
3821             { return domMemberCache.Document; }
3822         else if (object instanceof Location)
3823             { return domMemberCache.Location; }
3824         else if (object instanceof HTMLImageElement)
3825             { return domMemberCache.HTMLImageElement; }
3826         else if (object instanceof HTMLAnchorElement)
3827             { return domMemberCache.HTMLAnchorElement; }
3828         else if (object instanceof HTMLInputElement)
3829             { return domMemberCache.HTMLInputElement; }
3830         else if (object instanceof HTMLButtonElement)
3831             { return domMemberCache.HTMLButtonElement; }
3832         else if (object instanceof HTMLFormElement)
3833             { return domMemberCache.HTMLFormElement; }
3834         else if (object instanceof HTMLBodyElement)
3835             { return domMemberCache.HTMLBodyElement; }
3836         else if (object instanceof HTMLHtmlElement)
3837             { return domMemberCache.HTMLHtmlElement; }
3838         else if (object instanceof HTMLScriptElement)
3839             { return domMemberCache.HTMLScriptElement; }
3840         else if (object instanceof HTMLTableElement)
3841             { return domMemberCache.HTMLTableElement; }
3842         else if (object instanceof HTMLTableRowElement)
3843             { return domMemberCache.HTMLTableRowElement; }
3844         else if (object instanceof HTMLTableCellElement)
3845             { return domMemberCache.HTMLTableCellElement; }
3846         else if (object instanceof HTMLIFrameElement)
3847             { return domMemberCache.HTMLIFrameElement; }
3848         else if (object instanceof SVGSVGElement)
3849             { return domMemberCache.SVGSVGElement; }
3850         else if (object instanceof SVGElement)
3851             { return domMemberCache.SVGElement; }
3852         else if (object instanceof Element)
3853             { return domMemberCache.Element; }
3854         else if (object instanceof Text || object instanceof CDATASection)
3855             { return domMemberCache.Text; }
3856         else if (object instanceof Attr)
3857             { return domMemberCache.Attr; }
3858         else if (object instanceof Node)
3859             { return domMemberCache.Node; }
3860         else if (object instanceof Event || object instanceof EventCopy)
3861             { return domMemberCache.Event; }
3862         else
3863             return {};
3864     }
3865     catch(E)
3866     {
3867         return {};
3868     }
3869 };
3870 /**/
3871
3872 this.isDOMMember = function(object, propName)
3873 {
3874     var members = this.getDOMMembers(object);
3875     return members && propName in members;
3876 };
3877
3878 var domMemberCache = null;
3879 var domMemberMap = {};
3880
3881 domMemberMap.Window =
3882 [
3883     "document",
3884     "frameElement",
3885
3886     "innerWidth",
3887     "innerHeight",
3888     "outerWidth",
3889     "outerHeight",
3890     "screenX",
3891     "screenY",
3892     "pageXOffset",
3893     "pageYOffset",
3894     "scrollX",
3895     "scrollY",
3896     "scrollMaxX",
3897     "scrollMaxY",
3898
3899     "status",
3900     "defaultStatus",
3901
3902     "parent",
3903     "opener",
3904     "top",
3905     "window",
3906     "content",
3907     "self",
3908
3909     "location",
3910     "history",
3911     "frames",
3912     "navigator",
3913     "screen",
3914     "menubar",
3915     "toolbar",
3916     "locationbar",
3917     "personalbar",
3918     "statusbar",
3919     "directories",
3920     "scrollbars",
3921     "fullScreen",
3922     "netscape",
3923     "java",
3924     "console",
3925     "Components",
3926     "controllers",
3927     "closed",
3928     "crypto",
3929     "pkcs11",
3930
3931     "name",
3932     "property",
3933     "length",
3934
3935     "sessionStorage",
3936     "globalStorage",
3937
3938     "setTimeout",
3939     "setInterval",
3940     "clearTimeout",
3941     "clearInterval",
3942     "addEventListener",
3943     "removeEventListener",
3944     "dispatchEvent",
3945     "getComputedStyle",
3946     "captureEvents",
3947     "releaseEvents",
3948     "routeEvent",
3949     "enableExternalCapture",
3950     "disableExternalCapture",
3951     "moveTo",
3952     "moveBy",
3953     "resizeTo",
3954     "resizeBy",
3955     "scroll",
3956     "scrollTo",
3957     "scrollBy",
3958     "scrollByLines",
3959     "scrollByPages",
3960     "sizeToContent",
3961     "setResizable",
3962     "getSelection",
3963     "open",
3964     "openDialog",
3965     "close",
3966     "alert",
3967     "confirm",
3968     "prompt",
3969     "dump",
3970     "focus",
3971     "blur",
3972     "find",
3973     "back",
3974     "forward",
3975     "home",
3976     "stop",
3977     "print",
3978     "atob",
3979     "btoa",
3980     "updateCommands",
3981     "XPCNativeWrapper",
3982     "GeckoActiveXObject",
3983     "applicationCache"      // FF3
3984 ];
3985
3986 domMemberMap.Location =
3987 [
3988     "href",
3989     "protocol",
3990     "host",
3991     "hostname",
3992     "port",
3993     "pathname",
3994     "search",
3995     "hash",
3996
3997     "assign",
3998     "reload",
3999     "replace"
4000 ];
4001
4002 domMemberMap.Node =
4003 [
4004     "id",
4005     "className",
4006
4007     "nodeType",
4008     "tagName",
4009     "nodeName",
4010     "localName",
4011     "prefix",
4012     "namespaceURI",
4013     "nodeValue",
4014
4015     "ownerDocument",
4016     "parentNode",
4017     "offsetParent",
4018     "nextSibling",
4019     "previousSibling",
4020     "firstChild",
4021     "lastChild",
4022     "childNodes",
4023     "attributes",
4024
4025     "dir",
4026     "baseURI",
4027     "textContent",
4028     "innerHTML",
4029
4030     "addEventListener",
4031     "removeEventListener",
4032     "dispatchEvent",
4033     "cloneNode",
4034     "appendChild",
4035     "insertBefore",
4036     "replaceChild",
4037     "removeChild",
4038     "compareDocumentPosition",
4039     "hasAttributes",
4040     "hasChildNodes",
4041     "lookupNamespaceURI",
4042     "lookupPrefix",
4043     "normalize",
4044     "isDefaultNamespace",
4045     "isEqualNode",
4046     "isSameNode",
4047     "isSupported",
4048     "getFeature",
4049     "getUserData",
4050     "setUserData"
4051 ];
4052
4053 domMemberMap.Document = extendArray(domMemberMap.Node,
4054 [
4055     "documentElement",
4056     "body",
4057     "title",
4058     "location",
4059     "referrer",
4060     "cookie",
4061     "contentType",
4062     "lastModified",
4063     "characterSet",
4064     "inputEncoding",
4065     "xmlEncoding",
4066     "xmlStandalone",
4067     "xmlVersion",
4068     "strictErrorChecking",
4069     "documentURI",
4070     "URL",
4071
4072     "defaultView",
4073     "doctype",
4074     "implementation",
4075     "styleSheets",
4076     "images",
4077     "links",
4078     "forms",
4079     "anchors",
4080     "embeds",
4081     "plugins",
4082     "applets",
4083
4084     "width",
4085     "height",
4086
4087     "designMode",
4088     "compatMode",
4089     "async",
4090     "preferredStylesheetSet",
4091
4092     "alinkColor",
4093     "linkColor",
4094     "vlinkColor",
4095     "bgColor",
4096     "fgColor",
4097     "domain",
4098
4099     "addEventListener",
4100     "removeEventListener",
4101     "dispatchEvent",
4102     "captureEvents",
4103     "releaseEvents",
4104     "routeEvent",
4105     "clear",
4106     "open",
4107     "close",
4108     "execCommand",
4109     "execCommandShowHelp",
4110     "getElementsByName",
4111     "getSelection",
4112     "queryCommandEnabled",
4113     "queryCommandIndeterm",
4114     "queryCommandState",
4115     "queryCommandSupported",
4116     "queryCommandText",
4117     "queryCommandValue",
4118     "write",
4119     "writeln",
4120     "adoptNode",
4121     "appendChild",
4122     "removeChild",
4123     "renameNode",
4124     "cloneNode",
4125     "compareDocumentPosition",
4126     "createAttribute",
4127     "createAttributeNS",
4128     "createCDATASection",
4129     "createComment",
4130     "createDocumentFragment",
4131     "createElement",
4132     "createElementNS",
4133     "createEntityReference",
4134     "createEvent",
4135     "createExpression",
4136     "createNSResolver",
4137     "createNodeIterator",
4138     "createProcessingInstruction",
4139     "createRange",
4140     "createTextNode",
4141     "createTreeWalker",
4142     "domConfig",
4143     "evaluate",
4144     "evaluateFIXptr",
4145     "evaluateXPointer",
4146     "getAnonymousElementByAttribute",
4147     "getAnonymousNodes",
4148     "addBinding",
4149     "removeBinding",
4150     "getBindingParent",
4151     "getBoxObjectFor",
4152     "setBoxObjectFor",
4153     "getElementById",
4154     "getElementsByTagName",
4155     "getElementsByTagNameNS",
4156     "hasAttributes",
4157     "hasChildNodes",
4158     "importNode",
4159     "insertBefore",
4160     "isDefaultNamespace",
4161     "isEqualNode",
4162     "isSameNode",
4163     "isSupported",
4164     "load",
4165     "loadBindingDocument",
4166     "lookupNamespaceURI",
4167     "lookupPrefix",
4168     "normalize",
4169     "normalizeDocument",
4170     "getFeature",
4171     "getUserData",
4172     "setUserData"
4173 ]);
4174
4175 domMemberMap.Element = extendArray(domMemberMap.Node,
4176 [
4177     "clientWidth",
4178     "clientHeight",
4179     "offsetLeft",
4180     "offsetTop",
4181     "offsetWidth",
4182     "offsetHeight",
4183     "scrollLeft",
4184     "scrollTop",
4185     "scrollWidth",
4186     "scrollHeight",
4187
4188     "style",
4189
4190     "tabIndex",
4191     "title",
4192     "lang",
4193     "align",
4194     "spellcheck",
4195
4196     "addEventListener",
4197     "removeEventListener",
4198     "dispatchEvent",
4199     "focus",
4200     "blur",
4201     "cloneNode",
4202     "appendChild",
4203     "insertBefore",
4204     "replaceChild",
4205     "removeChild",
4206     "compareDocumentPosition",
4207     "getElementsByTagName",
4208     "getElementsByTagNameNS",
4209     "getAttribute",
4210     "getAttributeNS",
4211     "getAttributeNode",
4212     "getAttributeNodeNS",
4213     "setAttribute",
4214     "setAttributeNS",
4215     "setAttributeNode",
4216     "setAttributeNodeNS",
4217     "removeAttribute",
4218     "removeAttributeNS",
4219     "removeAttributeNode",
4220     "hasAttribute",
4221     "hasAttributeNS",
4222     "hasAttributes",
4223     "hasChildNodes",
4224     "lookupNamespaceURI",
4225     "lookupPrefix",
4226     "normalize",
4227     "isDefaultNamespace",
4228     "isEqualNode",
4229     "isSameNode",
4230     "isSupported",
4231     "getFeature",
4232     "getUserData",
4233     "setUserData"
4234 ]);
4235
4236 domMemberMap.SVGElement = extendArray(domMemberMap.Element,
4237 [
4238     "x",
4239     "y",
4240     "width",
4241     "height",
4242     "rx",
4243     "ry",
4244     "transform",
4245     "href",
4246
4247     "ownerSVGElement",
4248     "viewportElement",
4249     "farthestViewportElement",
4250     "nearestViewportElement",
4251
4252     "getBBox",
4253     "getCTM",
4254     "getScreenCTM",
4255     "getTransformToElement",
4256     "getPresentationAttribute",
4257     "preserveAspectRatio"
4258 ]);
4259
4260 domMemberMap.SVGSVGElement = extendArray(domMemberMap.Element,
4261 [
4262     "x",
4263     "y",
4264     "width",
4265     "height",
4266     "rx",
4267     "ry",
4268     "transform",
4269
4270     "viewBox",
4271     "viewport",
4272     "currentView",
4273     "useCurrentView",
4274     "pixelUnitToMillimeterX",
4275     "pixelUnitToMillimeterY",
4276     "screenPixelToMillimeterX",
4277     "screenPixelToMillimeterY",
4278     "currentScale",
4279     "currentTranslate",
4280     "zoomAndPan",
4281
4282     "ownerSVGElement",
4283     "viewportElement",
4284     "farthestViewportElement",
4285     "nearestViewportElement",
4286     "contentScriptType",
4287     "contentStyleType",
4288
4289     "getBBox",
4290     "getCTM",
4291     "getScreenCTM",
4292     "getTransformToElement",
4293     "getEnclosureList",
4294     "getIntersectionList",
4295     "getViewboxToViewportTransform",
4296     "getPresentationAttribute",
4297     "getElementById",
4298     "checkEnclosure",
4299     "checkIntersection",
4300     "createSVGAngle",
4301     "createSVGLength",
4302     "createSVGMatrix",
4303     "createSVGNumber",
4304     "createSVGPoint",
4305     "createSVGRect",
4306     "createSVGString",
4307     "createSVGTransform",
4308     "createSVGTransformFromMatrix",
4309     "deSelectAll",
4310     "preserveAspectRatio",
4311     "forceRedraw",
4312     "suspendRedraw",
4313     "unsuspendRedraw",
4314     "unsuspendRedrawAll",
4315     "getCurrentTime",
4316     "setCurrentTime",
4317     "animationsPaused",
4318     "pauseAnimations",
4319     "unpauseAnimations"
4320 ]);
4321
4322 domMemberMap.HTMLImageElement = extendArray(domMemberMap.Element,
4323 [
4324     "src",
4325     "naturalWidth",
4326     "naturalHeight",
4327     "width",
4328     "height",
4329     "x",
4330     "y",
4331     "name",
4332     "alt",
4333     "longDesc",
4334     "lowsrc",
4335     "border",
4336     "complete",
4337     "hspace",
4338     "vspace",
4339     "isMap",
4340     "useMap"
4341 ]);
4342
4343 domMemberMap.HTMLAnchorElement = extendArray(domMemberMap.Element,
4344 [
4345     "name",
4346     "target",
4347     "accessKey",
4348     "href",
4349     "protocol",
4350     "host",
4351     "hostname",
4352     "port",
4353     "pathname",
4354     "search",
4355     "hash",
4356     "hreflang",
4357     "coords",
4358     "shape",
4359     "text",
4360     "type",
4361     "rel",
4362     "rev",
4363     "charset"
4364 ]);
4365
4366 domMemberMap.HTMLIFrameElement = extendArray(domMemberMap.Element,
4367 [
4368     "contentDocument",
4369     "contentWindow",
4370     "frameBorder",
4371     "height",
4372     "longDesc",
4373     "marginHeight",
4374     "marginWidth",
4375     "name",
4376     "scrolling",
4377     "src",
4378     "width"
4379 ]);
4380
4381 domMemberMap.HTMLTableElement = extendArray(domMemberMap.Element,
4382 [
4383     "bgColor",
4384     "border",
4385     "caption",
4386     "cellPadding",
4387     "cellSpacing",
4388     "frame",
4389     "rows",
4390     "rules",
4391     "summary",
4392     "tBodies",
4393     "tFoot",
4394     "tHead",
4395     "width",
4396
4397     "createCaption",
4398     "createTFoot",
4399     "createTHead",
4400     "deleteCaption",
4401     "deleteRow",
4402     "deleteTFoot",
4403     "deleteTHead",
4404     "insertRow"
4405 ]);
4406
4407 domMemberMap.HTMLTableRowElement = extendArray(domMemberMap.Element,
4408 [
4409     "bgColor",
4410     "cells",
4411     "ch",
4412     "chOff",
4413     "rowIndex",
4414     "sectionRowIndex",
4415     "vAlign",
4416
4417     "deleteCell",
4418     "insertCell"
4419 ]);
4420
4421 domMemberMap.HTMLTableCellElement = extendArray(domMemberMap.Element,
4422 [
4423     "abbr",
4424     "axis",
4425     "bgColor",
4426     "cellIndex",
4427     "ch",
4428     "chOff",
4429     "colSpan",
4430     "headers",
4431     "height",
4432     "noWrap",
4433     "rowSpan",
4434     "scope",
4435     "vAlign",
4436     "width"
4437
4438 ]);
4439
4440 domMemberMap.HTMLScriptElement = extendArray(domMemberMap.Element,
4441 [
4442     "src"
4443 ]);
4444
4445 domMemberMap.HTMLButtonElement = extendArray(domMemberMap.Element,
4446 [
4447     "accessKey",
4448     "disabled",
4449     "form",
4450     "name",
4451     "type",
4452     "value",
4453
4454     "click"
4455 ]);
4456
4457 domMemberMap.HTMLInputElement = extendArray(domMemberMap.Element,
4458 [
4459     "type",
4460     "value",
4461     "checked",
4462     "accept",
4463     "accessKey",
4464     "alt",
4465     "controllers",
4466     "defaultChecked",
4467     "defaultValue",
4468     "disabled",
4469     "form",
4470     "maxLength",
4471     "name",
4472     "readOnly",
4473     "selectionEnd",
4474     "selectionStart",
4475     "size",
4476     "src",
4477     "textLength",
4478     "useMap",
4479
4480     "click",
4481     "select",
4482     "setSelectionRange"
4483 ]);
4484
4485 domMemberMap.HTMLFormElement = extendArray(domMemberMap.Element,
4486 [
4487     "acceptCharset",
4488     "action",
4489     "author",
4490     "elements",
4491     "encoding",
4492     "enctype",
4493     "entry_id",
4494     "length",
4495     "method",
4496     "name",
4497     "post",
4498     "target",
4499     "text",
4500     "url",
4501
4502     "reset",
4503     "submit"
4504 ]);
4505
4506 domMemberMap.HTMLBodyElement = extendArray(domMemberMap.Element,
4507 [
4508     "aLink",
4509     "background",
4510     "bgColor",
4511     "link",
4512     "text",
4513     "vLink"
4514 ]);
4515
4516 domMemberMap.HTMLHtmlElement = extendArray(domMemberMap.Element,
4517 [
4518     "version"
4519 ]);
4520
4521 domMemberMap.Text = extendArray(domMemberMap.Node,
4522 [
4523     "data",
4524     "length",
4525
4526     "appendData",
4527     "deleteData",
4528     "insertData",
4529     "replaceData",
4530     "splitText",
4531     "substringData"
4532 ]);
4533
4534 domMemberMap.Attr = extendArray(domMemberMap.Node,
4535 [
4536     "name",
4537     "value",
4538     "specified",
4539     "ownerElement"
4540 ]);
4541
4542 domMemberMap.Event =
4543 [
4544     "type",
4545     "target",
4546     "currentTarget",
4547     "originalTarget",
4548     "explicitOriginalTarget",
4549     "relatedTarget",
4550     "rangeParent",
4551     "rangeOffset",
4552     "view",
4553
4554     "keyCode",
4555     "charCode",
4556     "screenX",
4557     "screenY",
4558     "clientX",
4559     "clientY",
4560     "layerX",
4561     "layerY",
4562     "pageX",
4563     "pageY",
4564
4565     "detail",
4566     "button",
4567     "which",
4568     "ctrlKey",
4569     "shiftKey",
4570     "altKey",
4571     "metaKey",
4572
4573     "eventPhase",
4574     "timeStamp",
4575     "bubbles",
4576     "cancelable",
4577     "cancelBubble",
4578
4579     "isTrusted",
4580     "isChar",
4581
4582     "getPreventDefault",
4583     "initEvent",
4584     "initMouseEvent",
4585     "initKeyEvent",
4586     "initUIEvent",
4587     "preventBubble",
4588     "preventCapture",
4589     "preventDefault",
4590     "stopPropagation"
4591 ];
4592
4593 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
4594
4595 this.domConstantMap =
4596 {
4597     "ELEMENT_NODE": 1,
4598     "ATTRIBUTE_NODE": 1,
4599     "TEXT_NODE": 1,
4600     "CDATA_SECTION_NODE": 1,
4601     "ENTITY_REFERENCE_NODE": 1,
4602     "ENTITY_NODE": 1,
4603     "PROCESSING_INSTRUCTION_NODE": 1,
4604     "COMMENT_NODE": 1,
4605     "DOCUMENT_NODE": 1,
4606     "DOCUMENT_TYPE_NODE": 1,
4607     "DOCUMENT_FRAGMENT_NODE": 1,
4608     "NOTATION_NODE": 1,
4609
4610     "DOCUMENT_POSITION_DISCONNECTED": 1,
4611     "DOCUMENT_POSITION_PRECEDING": 1,
4612     "DOCUMENT_POSITION_FOLLOWING": 1,
4613     "DOCUMENT_POSITION_CONTAINS": 1,
4614     "DOCUMENT_POSITION_CONTAINED_BY": 1,
4615     "DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC": 1,
4616
4617     "UNKNOWN_RULE": 1,
4618     "STYLE_RULE": 1,
4619     "CHARSET_RULE": 1,
4620     "IMPORT_RULE": 1,
4621     "MEDIA_RULE": 1,
4622     "FONT_FACE_RULE": 1,
4623     "PAGE_RULE": 1,
4624
4625     "CAPTURING_PHASE": 1,
4626     "AT_TARGET": 1,
4627     "BUBBLING_PHASE": 1,
4628
4629     "SCROLL_PAGE_UP": 1,
4630     "SCROLL_PAGE_DOWN": 1,
4631
4632     "MOUSEUP": 1,
4633     "MOUSEDOWN": 1,
4634     "MOUSEOVER": 1,
4635     "MOUSEOUT": 1,
4636     "MOUSEMOVE": 1,
4637     "MOUSEDRAG": 1,
4638     "CLICK": 1,
4639     "DBLCLICK": 1,
4640     "KEYDOWN": 1,
4641     "KEYUP": 1,
4642     "KEYPRESS": 1,
4643     "DRAGDROP": 1,
4644     "FOCUS": 1,
4645     "BLUR": 1,
4646     "SELECT": 1,
4647     "CHANGE": 1,
4648     "RESET": 1,
4649     "SUBMIT": 1,
4650     "SCROLL": 1,
4651     "LOAD": 1,
4652     "UNLOAD": 1,
4653     "XFER_DONE": 1,
4654     "ABORT": 1,
4655     "ERROR": 1,
4656     "LOCATE": 1,
4657     "MOVE": 1,
4658     "RESIZE": 1,
4659     "FORWARD": 1,
4660     "HELP": 1,
4661     "BACK": 1,
4662     "TEXT": 1,
4663
4664     "ALT_MASK": 1,
4665     "CONTROL_MASK": 1,
4666     "SHIFT_MASK": 1,
4667     "META_MASK": 1,
4668
4669     "DOM_VK_TAB": 1,
4670     "DOM_VK_PAGE_UP": 1,
4671     "DOM_VK_PAGE_DOWN": 1,
4672     "DOM_VK_UP": 1,
4673     "DOM_VK_DOWN": 1,
4674     "DOM_VK_LEFT": 1,
4675     "DOM_VK_RIGHT": 1,
4676     "DOM_VK_CANCEL": 1,
4677     "DOM_VK_HELP": 1,
4678     "DOM_VK_BACK_SPACE": 1,
4679     "DOM_VK_CLEAR": 1,
4680     "DOM_VK_RETURN": 1,
4681     "DOM_VK_ENTER": 1,
4682     "DOM_VK_SHIFT": 1,
4683     "DOM_VK_CONTROL": 1,
4684     "DOM_VK_ALT": 1,
4685     "DOM_VK_PAUSE": 1,
4686     "DOM_VK_CAPS_LOCK": 1,
4687     "DOM_VK_ESCAPE": 1,
4688     "DOM_VK_SPACE": 1,
4689     "DOM_VK_END": 1,
4690     "DOM_VK_HOME": 1,
4691     "DOM_VK_PRINTSCREEN": 1,
4692     "DOM_VK_INSERT": 1,
4693     "DOM_VK_DELETE": 1,
4694     "DOM_VK_0": 1,
4695     "DOM_VK_1": 1,
4696     "DOM_VK_2": 1,
4697     "DOM_VK_3": 1,
4698     "DOM_VK_4": 1,
4699     "DOM_VK_5": 1,
4700     "DOM_VK_6": 1,
4701     "DOM_VK_7": 1,
4702     "DOM_VK_8": 1,
4703     "DOM_VK_9": 1,
4704     "DOM_VK_SEMICOLON": 1,
4705     "DOM_VK_EQUALS": 1,
4706     "DOM_VK_A": 1,
4707     "DOM_VK_B": 1,
4708     "DOM_VK_C": 1,
4709     "DOM_VK_D": 1,
4710     "DOM_VK_E": 1,
4711     "DOM_VK_F": 1,
4712     "DOM_VK_G": 1,
4713     "DOM_VK_H": 1,
4714     "DOM_VK_I": 1,
4715     "DOM_VK_J": 1,
4716     "DOM_VK_K": 1,
4717     "DOM_VK_L": 1,
4718     "DOM_VK_M": 1,
4719     "DOM_VK_N": 1,
4720     "DOM_VK_O": 1,
4721     "DOM_VK_P": 1,
4722     "DOM_VK_Q": 1,
4723     "DOM_VK_R": 1,
4724     "DOM_VK_S": 1,
4725     "DOM_VK_T": 1,
4726     "DOM_VK_U": 1,
4727     "DOM_VK_V": 1,
4728     "DOM_VK_W": 1,
4729     "DOM_VK_X": 1,
4730     "DOM_VK_Y": 1,
4731     "DOM_VK_Z": 1,
4732     "DOM_VK_CONTEXT_MENU": 1,
4733     "DOM_VK_NUMPAD0": 1,
4734     "DOM_VK_NUMPAD1": 1,
4735     "DOM_VK_NUMPAD2": 1,
4736     "DOM_VK_NUMPAD3": 1,
4737     "DOM_VK_NUMPAD4": 1,
4738     "DOM_VK_NUMPAD5": 1,
4739     "DOM_VK_NUMPAD6": 1,
4740     "DOM_VK_NUMPAD7": 1,
4741     "DOM_VK_NUMPAD8": 1,
4742     "DOM_VK_NUMPAD9": 1,
4743     "DOM_VK_MULTIPLY": 1,
4744     "DOM_VK_ADD": 1,
4745     "DOM_VK_SEPARATOR": 1,
4746     "DOM_VK_SUBTRACT": 1,
4747     "DOM_VK_DECIMAL": 1,
4748     "DOM_VK_DIVIDE": 1,
4749     "DOM_VK_F1": 1,
4750     "DOM_VK_F2": 1,
4751     "DOM_VK_F3": 1,
4752     "DOM_VK_F4": 1,
4753     "DOM_VK_F5": 1,
4754     "DOM_VK_F6": 1,
4755     "DOM_VK_F7": 1,
4756     "DOM_VK_F8": 1,
4757     "DOM_VK_F9": 1,
4758     "DOM_VK_F10": 1,
4759     "DOM_VK_F11": 1,
4760     "DOM_VK_F12": 1,
4761     "DOM_VK_F13": 1,
4762     "DOM_VK_F14": 1,
4763     "DOM_VK_F15": 1,
4764     "DOM_VK_F16": 1,
4765     "DOM_VK_F17": 1,
4766     "DOM_VK_F18": 1,
4767     "DOM_VK_F19": 1,
4768     "DOM_VK_F20": 1,
4769     "DOM_VK_F21": 1,
4770     "DOM_VK_F22": 1,
4771     "DOM_VK_F23": 1,
4772     "DOM_VK_F24": 1,
4773     "DOM_VK_NUM_LOCK": 1,
4774     "DOM_VK_SCROLL_LOCK": 1,
4775     "DOM_VK_COMMA": 1,
4776     "DOM_VK_PERIOD": 1,
4777     "DOM_VK_SLASH": 1,
4778     "DOM_VK_BACK_QUOTE": 1,
4779     "DOM_VK_OPEN_BRACKET": 1,
4780     "DOM_VK_BACK_SLASH": 1,
4781     "DOM_VK_CLOSE_BRACKET": 1,
4782     "DOM_VK_QUOTE": 1,
4783     "DOM_VK_META": 1,
4784
4785     "SVG_ZOOMANDPAN_DISABLE": 1,
4786     "SVG_ZOOMANDPAN_MAGNIFY": 1,
4787     "SVG_ZOOMANDPAN_UNKNOWN": 1
4788 };
4789
4790 this.cssInfo =
4791 {
4792     "background": ["bgRepeat", "bgAttachment", "bgPosition", "color", "systemColor", "none"],
4793     "background-attachment": ["bgAttachment"],
4794     "background-color": ["color", "systemColor"],
4795     "background-image": ["none"],
4796     "background-position": ["bgPosition"],
4797     "background-repeat": ["bgRepeat"],
4798
4799     "border": ["borderStyle", "thickness", "color", "systemColor", "none"],
4800     "border-top": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
4801     "border-right": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
4802     "border-bottom": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
4803     "border-left": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
4804     "border-collapse": ["borderCollapse"],
4805     "border-color": ["color", "systemColor"],
4806     "border-top-color": ["color", "systemColor"],
4807     "border-right-color": ["color", "systemColor"],
4808     "border-bottom-color": ["color", "systemColor"],
4809     "border-left-color": ["color", "systemColor"],
4810     "border-spacing": [],
4811     "border-style": ["borderStyle"],
4812     "border-top-style": ["borderStyle"],
4813     "border-right-style": ["borderStyle"],
4814     "border-bottom-style": ["borderStyle"],
4815     "border-left-style": ["borderStyle"],
4816     "border-width": ["thickness"],
4817     "border-top-width": ["thickness"],
4818     "border-right-width": ["thickness"],
4819     "border-bottom-width": ["thickness"],
4820     "border-left-width": ["thickness"],
4821
4822     "bottom": ["auto"],
4823     "caption-side": ["captionSide"],
4824     "clear": ["clear", "none"],
4825     "clip": ["auto"],
4826     "color": ["color", "systemColor"],
4827     "content": ["content"],
4828     "counter-increment": ["none"],
4829     "counter-reset": ["none"],
4830     "cursor": ["cursor", "none"],
4831     "direction": ["direction"],
4832     "display": ["display", "none"],
4833     "empty-cells": [],
4834     "float": ["float", "none"],
4835     "font": ["fontStyle", "fontVariant", "fontWeight", "fontFamily"],
4836
4837     "font-family": ["fontFamily"],
4838     "font-size": ["fontSize"],
4839     "font-size-adjust": [],
4840     "font-stretch": [],
4841     "font-style": ["fontStyle"],
4842     "font-variant": ["fontVariant"],
4843     "font-weight": ["fontWeight"],
4844
4845     "height": ["auto"],
4846     "left": ["auto"],
4847     "letter-spacing": [],
4848     "line-height": [],
4849
4850     "list-style": ["listStyleType", "listStylePosition", "none"],
4851     "list-style-image": ["none"],
4852     "list-style-position": ["listStylePosition"],
4853     "list-style-type": ["listStyleType", "none"],
4854
4855     "margin": [],
4856     "margin-top": [],
4857     "margin-right": [],
4858     "margin-bottom": [],
4859     "margin-left": [],
4860
4861     "marker-offset": ["auto"],
4862     "min-height": ["none"],
4863     "max-height": ["none"],
4864     "min-width": ["none"],
4865     "max-width": ["none"],
4866
4867     "outline": ["borderStyle", "color", "systemColor", "none"],
4868     "outline-color": ["color", "systemColor"],
4869     "outline-style": ["borderStyle"],
4870     "outline-width": [],
4871
4872     "overflow": ["overflow", "auto"],
4873     "overflow-x": ["overflow", "auto"],
4874     "overflow-y": ["overflow", "auto"],
4875
4876     "padding": [],
4877     "padding-top": [],
4878     "padding-right": [],
4879     "padding-bottom": [],
4880     "padding-left": [],
4881
4882     "position": ["position"],
4883     "quotes": ["none"],
4884     "right": ["auto"],
4885     "table-layout": ["tableLayout", "auto"],
4886     "text-align": ["textAlign"],
4887     "text-decoration": ["textDecoration", "none"],
4888     "text-indent": [],
4889     "text-shadow": [],
4890     "text-transform": ["textTransform", "none"],
4891     "top": ["auto"],
4892     "unicode-bidi": [],
4893     "vertical-align": ["verticalAlign"],
4894     "white-space": ["whiteSpace"],
4895     "width": ["auto"],
4896     "word-spacing": [],
4897     "z-index": [],
4898
4899     "-moz-appearance": ["mozAppearance"],
4900     "-moz-border-radius": [],
4901     "-moz-border-radius-bottomleft": [],
4902     "-moz-border-radius-bottomright": [],
4903     "-moz-border-radius-topleft": [],
4904     "-moz-border-radius-topright": [],
4905     "-moz-border-top-colors": ["color", "systemColor"],
4906     "-moz-border-right-colors": ["color", "systemColor"],
4907     "-moz-border-bottom-colors": ["color", "systemColor"],
4908     "-moz-border-left-colors": ["color", "systemColor"],
4909     "-moz-box-align": ["mozBoxAlign"],
4910     "-moz-box-direction": ["mozBoxDirection"],
4911     "-moz-box-flex": [],
4912     "-moz-box-ordinal-group": [],
4913     "-moz-box-orient": ["mozBoxOrient"],
4914     "-moz-box-pack": ["mozBoxPack"],
4915     "-moz-box-sizing": ["mozBoxSizing"],
4916     "-moz-opacity": [],
4917     "-moz-user-focus": ["userFocus", "none"],
4918     "-moz-user-input": ["userInput"],
4919     "-moz-user-modify": [],
4920     "-moz-user-select": ["userSelect", "none"],
4921     "-moz-background-clip": [],
4922     "-moz-background-inline-policy": [],
4923     "-moz-background-origin": [],
4924     "-moz-binding": [],
4925     "-moz-column-count": [],
4926     "-moz-column-gap": [],
4927     "-moz-column-width": [],
4928     "-moz-image-region": []
4929 };
4930
4931 this.inheritedStyleNames =
4932 {
4933     "border-collapse": 1,
4934     "border-spacing": 1,
4935     "border-style": 1,
4936     "caption-side": 1,
4937     "color": 1,
4938     "cursor": 1,
4939     "direction": 1,
4940     "empty-cells": 1,
4941     "font": 1,
4942     "font-family": 1,
4943     "font-size-adjust": 1,
4944     "font-size": 1,
4945     "font-style": 1,
4946     "font-variant": 1,
4947     "font-weight": 1,
4948     "letter-spacing": 1,
4949     "line-height": 1,
4950     "list-style": 1,
4951     "list-style-image": 1,
4952     "list-style-position": 1,
4953     "list-style-type": 1,
4954     "quotes": 1,
4955     "text-align": 1,
4956     "text-decoration": 1,
4957     "text-indent": 1,
4958     "text-shadow": 1,
4959     "text-transform": 1,
4960     "white-space": 1,
4961     "word-spacing": 1
4962 };
4963
4964 this.cssKeywords =
4965 {
4966     "appearance":
4967     [
4968         "button",
4969         "button-small",
4970         "checkbox",
4971         "checkbox-container",
4972         "checkbox-small",
4973         "dialog",
4974         "listbox",
4975         "menuitem",
4976         "menulist",
4977         "menulist-button",
4978         "menulist-textfield",
4979         "menupopup",
4980         "progressbar",
4981         "radio",
4982         "radio-container",
4983         "radio-small",
4984         "resizer",
4985         "scrollbar",
4986         "scrollbarbutton-down",
4987         "scrollbarbutton-left",
4988         "scrollbarbutton-right",
4989         "scrollbarbutton-up",
4990         "scrollbartrack-horizontal",
4991         "scrollbartrack-vertical",
4992         "separator",
4993         "statusbar",
4994         "tab",
4995         "tab-left-edge",
4996         "tabpanels",
4997         "textfield",
4998         "toolbar",
4999         "toolbarbutton",
5000         "toolbox",
5001         "tooltip",
5002         "treeheadercell",
5003         "treeheadersortarrow",
5004         "treeitem",
5005         "treetwisty",
5006         "treetwistyopen",
5007         "treeview",
5008         "window"
5009     ],
5010
5011     "systemColor":
5012     [
5013         "ActiveBorder",
5014         "ActiveCaption",
5015         "AppWorkspace",
5016         "Background",
5017         "ButtonFace",
5018         "ButtonHighlight",
5019         "ButtonShadow",
5020         "ButtonText",
5021         "CaptionText",
5022         "GrayText",
5023         "Highlight",
5024         "HighlightText",
5025         "InactiveBorder",
5026         "InactiveCaption",
5027         "InactiveCaptionText",
5028         "InfoBackground",
5029         "InfoText",
5030         "Menu",
5031         "MenuText",
5032         "Scrollbar",
5033         "ThreeDDarkShadow",
5034         "ThreeDFace",
5035         "ThreeDHighlight",
5036         "ThreeDLightShadow",
5037         "ThreeDShadow",
5038         "Window",
5039         "WindowFrame",
5040         "WindowText",
5041         "-moz-field",
5042         "-moz-fieldtext",
5043         "-moz-workspace",
5044         "-moz-visitedhyperlinktext",
5045         "-moz-use-text-color"
5046     ],
5047
5048     "color":
5049     [
5050         "AliceBlue",
5051         "AntiqueWhite",
5052         "Aqua",
5053         "Aquamarine",
5054         "Azure",
5055         "Beige",
5056         "Bisque",
5057         "Black",
5058         "BlanchedAlmond",
5059         "Blue",
5060         "BlueViolet",
5061         "Brown",
5062         "BurlyWood",
5063         "CadetBlue",
5064         "Chartreuse",
5065         "Chocolate",
5066         "Coral",
5067         "CornflowerBlue",
5068         "Cornsilk",
5069         "Crimson",
5070         "Cyan",
5071         "DarkBlue",
5072         "DarkCyan",
5073         "DarkGoldenRod",
5074         "DarkGray",
5075         "DarkGreen",
5076         "DarkKhaki",
5077         "DarkMagenta",
5078         "DarkOliveGreen",
5079         "DarkOrange",
5080         "DarkOrchid",
5081         "DarkRed",
5082         "DarkSalmon",
5083         "DarkSeaGreen",
5084         "DarkSlateBlue",
5085         "DarkSlateGray",
5086         "DarkTurquoise",
5087         "DarkViolet",
5088         "DeepPink",
5089         "DarkSkyBlue",
5090         "DimGray",
5091         "DodgerBlue",
5092         "Feldspar",
5093         "FireBrick",
5094         "FloralWhite",
5095         "ForestGreen",
5096         "Fuchsia",
5097         "Gainsboro",
5098         "GhostWhite",
5099         "Gold",
5100         "GoldenRod",
5101         "Gray",
5102         "Green",
5103         "GreenYellow",
5104         "HoneyDew",
5105         "HotPink",
5106         "IndianRed",
5107         "Indigo",
5108         "Ivory",
5109         "Khaki",
5110         "Lavender",
5111         "LavenderBlush",
5112         "LawnGreen",
5113         "LemonChiffon",
5114         "LightBlue",
5115         "LightCoral",
5116         "LightCyan",
5117         "LightGoldenRodYellow",
5118         "LightGrey",
5119         "LightGreen",
5120         "LightPink",
5121         "LightSalmon",
5122         "LightSeaGreen",
5123         "LightSkyBlue",
5124         "LightSlateBlue",
5125         "LightSlateGray",
5126         "LightSteelBlue",
5127         "LightYellow",
5128         "Lime",
5129         "LimeGreen",
5130         "Linen",
5131         "Magenta",
5132         "Maroon",
5133         "MediumAquaMarine",
5134         "MediumBlue",
5135         "MediumOrchid",
5136         "MediumPurple",
5137         "MediumSeaGreen",
5138         "MediumSlateBlue",
5139         "MediumSpringGreen",
5140         "MediumTurquoise",
5141         "MediumVioletRed",
5142         "MidnightBlue",
5143         "MintCream",
5144         "MistyRose",
5145         "Moccasin",
5146         "NavajoWhite",
5147         "Navy",
5148         "OldLace",
5149         "Olive",
5150         "OliveDrab",
5151         "Orange",
5152         "OrangeRed",
5153         "Orchid",
5154         "PaleGoldenRod",
5155         "PaleGreen",
5156         "PaleTurquoise",
5157         "PaleVioletRed",
5158         "PapayaWhip",
5159         "PeachPuff",
5160         "Peru",
5161         "Pink",
5162         "Plum",
5163         "PowderBlue",
5164         "Purple",
5165         "Red",
5166         "RosyBrown",
5167         "RoyalBlue",
5168         "SaddleBrown",
5169         "Salmon",
5170         "SandyBrown",
5171         "SeaGreen",
5172         "SeaShell",
5173         "Sienna",
5174         "Silver",
5175         "SkyBlue",
5176         "SlateBlue",
5177         "SlateGray",
5178         "Snow",
5179         "SpringGreen",
5180         "SteelBlue",
5181         "Tan",
5182         "Teal",
5183         "Thistle",
5184         "Tomato",
5185         "Turquoise",
5186         "Violet",
5187         "VioletRed",
5188         "Wheat",
5189         "White",
5190         "WhiteSmoke",
5191         "Yellow",
5192         "YellowGreen",
5193         "transparent",
5194         "invert"
5195     ],
5196
5197     "auto":
5198     [
5199         "auto"
5200     ],
5201
5202     "none":
5203     [
5204         "none"
5205     ],
5206
5207     "captionSide":
5208     [
5209         "top",
5210         "bottom",
5211         "left",
5212         "right"
5213     ],
5214
5215     "clear":
5216     [
5217         "left",
5218         "right",
5219         "both"
5220     ],
5221
5222     "cursor":
5223     [
5224         "auto",
5225         "cell",
5226         "context-menu",
5227         "crosshair",
5228         "default",
5229         "help",
5230         "pointer",
5231         "progress",
5232         "move",
5233         "e-resize",
5234         "all-scroll",
5235         "ne-resize",
5236         "nw-resize",
5237         "n-resize",
5238         "se-resize",
5239         "sw-resize",
5240         "s-resize",
5241         "w-resize",
5242         "ew-resize",
5243         "ns-resize",
5244         "nesw-resize",
5245         "nwse-resize",
5246         "col-resize",
5247         "row-resize",
5248         "text",
5249         "vertical-text",
5250         "wait",
5251         "alias",
5252         "copy",
5253         "move",
5254         "no-drop",
5255         "not-allowed",
5256         "-moz-alias",
5257         "-moz-cell",
5258         "-moz-copy",
5259         "-moz-grab",
5260         "-moz-grabbing",
5261         "-moz-contextmenu",
5262         "-moz-zoom-in",
5263         "-moz-zoom-out",
5264         "-moz-spinning"
5265     ],
5266
5267     "direction":
5268     [
5269         "ltr",
5270         "rtl"
5271     ],
5272
5273     "bgAttachment":
5274     [
5275         "scroll",
5276         "fixed"
5277     ],
5278
5279     "bgPosition":
5280     [
5281         "top",
5282         "center",
5283         "bottom",
5284         "left",
5285         "right"
5286     ],
5287
5288     "bgRepeat":
5289     [
5290         "repeat",
5291         "repeat-x",
5292         "repeat-y",
5293         "no-repeat"
5294     ],
5295
5296     "borderStyle":
5297     [
5298         "hidden",
5299         "dotted",
5300         "dashed",
5301         "solid",
5302         "double",
5303         "groove",
5304         "ridge",
5305         "inset",
5306         "outset",
5307         "-moz-bg-inset",
5308         "-moz-bg-outset",
5309         "-moz-bg-solid"
5310     ],
5311
5312     "borderCollapse":
5313     [
5314         "collapse",
5315         "separate"
5316     ],
5317
5318     "overflow":
5319     [
5320         "visible",
5321         "hidden",
5322         "scroll",
5323         "-moz-scrollbars-horizontal",
5324         "-moz-scrollbars-none",
5325         "-moz-scrollbars-vertical"
5326     ],
5327
5328     "listStyleType":
5329     [
5330         "disc",
5331         "circle",
5332         "square",
5333         "decimal",
5334         "decimal-leading-zero",
5335         "lower-roman",
5336         "upper-roman",
5337         "lower-greek",
5338         "lower-alpha",
5339         "lower-latin",
5340         "upper-alpha",
5341         "upper-latin",
5342         "hebrew",
5343         "armenian",
5344         "georgian",
5345         "cjk-ideographic",
5346         "hiragana",
5347         "katakana",
5348         "hiragana-iroha",
5349         "katakana-iroha",
5350         "inherit"
5351     ],
5352
5353     "listStylePosition":
5354     [
5355         "inside",
5356         "outside"
5357     ],
5358
5359     "content":
5360     [
5361         "open-quote",
5362         "close-quote",
5363         "no-open-quote",
5364         "no-close-quote",
5365         "inherit"
5366     ],
5367
5368     "fontStyle":
5369     [
5370         "normal",
5371         "italic",
5372         "oblique",
5373         "inherit"
5374     ],
5375
5376     "fontVariant":
5377     [
5378         "normal",
5379         "small-caps",
5380         "inherit"
5381     ],
5382
5383     "fontWeight":
5384     [
5385         "normal",
5386         "bold",
5387         "bolder",
5388         "lighter",
5389         "inherit"
5390     ],
5391
5392     "fontSize":
5393     [
5394         "xx-small",
5395         "x-small",
5396         "small",
5397         "medium",
5398         "large",
5399         "x-large",
5400         "xx-large",
5401         "smaller",
5402         "larger"
5403     ],
5404
5405     "fontFamily":
5406     [
5407         "Arial",
5408         "Comic Sans MS",
5409         "Georgia",
5410         "Tahoma",
5411         "Verdana",
5412         "Times New Roman",
5413         "Trebuchet MS",
5414         "Lucida Grande",
5415         "Helvetica",
5416         "serif",
5417         "sans-serif",
5418         "cursive",
5419         "fantasy",
5420         "monospace",
5421         "caption",
5422         "icon",
5423         "menu",
5424         "message-box",
5425         "small-caption",
5426         "status-bar",
5427         "inherit"
5428     ],
5429
5430     "display":
5431     [
5432         "block",
5433         "inline",
5434         "inline-block",
5435         "list-item",
5436         "marker",
5437         "run-in",
5438         "compact",
5439         "table",
5440         "inline-table",
5441         "table-row-group",
5442         "table-column",
5443         "table-column-group",
5444         "table-header-group",
5445         "table-footer-group",
5446         "table-row",
5447         "table-cell",
5448         "table-caption",
5449         "-moz-box",
5450         "-moz-compact",
5451         "-moz-deck",
5452         "-moz-grid",
5453         "-moz-grid-group",
5454         "-moz-grid-line",
5455         "-moz-groupbox",
5456         "-moz-inline-block",
5457         "-moz-inline-box",
5458         "-moz-inline-grid",
5459         "-moz-inline-stack",
5460         "-moz-inline-table",
5461         "-moz-marker",
5462         "-moz-popup",
5463         "-moz-runin",
5464         "-moz-stack"
5465     ],
5466
5467     "position":
5468     [
5469         "static",
5470         "relative",
5471         "absolute",
5472         "fixed",
5473         "inherit"
5474     ],
5475
5476     "float":
5477     [
5478         "left",
5479         "right"
5480     ],
5481
5482     "textAlign":
5483     [
5484         "left",
5485         "right",
5486         "center",
5487         "justify"
5488     ],
5489
5490     "tableLayout":
5491     [
5492         "fixed"
5493     ],
5494
5495     "textDecoration":
5496     [
5497         "underline",
5498         "overline",
5499         "line-through",
5500         "blink"
5501     ],
5502
5503     "textTransform":
5504     [
5505         "capitalize",
5506         "lowercase",
5507         "uppercase",
5508         "inherit"
5509     ],
5510
5511     "unicodeBidi":
5512     [
5513         "normal",
5514         "embed",
5515         "bidi-override"
5516     ],
5517
5518     "whiteSpace":
5519     [
5520         "normal",
5521         "pre",
5522         "nowrap"
5523     ],
5524
5525     "verticalAlign":
5526     [
5527         "baseline",
5528         "sub",
5529         "super",
5530         "top",
5531         "text-top",
5532         "middle",
5533         "bottom",
5534         "text-bottom",
5535         "inherit"
5536     ],
5537
5538     "thickness":
5539     [
5540         "thin",
5541         "medium",
5542         "thick"
5543     ],
5544
5545     "userFocus":
5546     [
5547         "ignore",
5548         "normal"
5549     ],
5550
5551     "userInput":
5552     [
5553         "disabled",
5554         "enabled"
5555     ],
5556
5557     "userSelect":
5558     [
5559         "normal"
5560     ],
5561
5562     "mozBoxSizing":
5563     [
5564         "content-box",
5565         "padding-box",
5566         "border-box"
5567     ],
5568
5569     "mozBoxAlign":
5570     [
5571         "start",
5572         "center",
5573         "end",
5574         "baseline",
5575         "stretch"
5576     ],
5577
5578     "mozBoxDirection":
5579     [
5580         "normal",
5581         "reverse"
5582     ],
5583
5584     "mozBoxOrient":
5585     [
5586         "horizontal",
5587         "vertical"
5588     ],
5589
5590     "mozBoxPack":
5591     [
5592         "start",
5593         "center",
5594         "end"
5595     ]
5596 };
5597
5598 this.nonEditableTags =
5599 {
5600     "HTML": 1,
5601     "HEAD": 1,
5602     "html": 1,
5603     "head": 1
5604 };
5605
5606 this.innerEditableTags =
5607 {
5608     "BODY": 1,
5609     "body": 1
5610 };
5611
5612 this.selfClosingTags =
5613 { // End tags for void elements are forbidden http://wiki.whatwg.org/wiki/HTML_vs._XHTML
5614     "meta": 1,
5615     "link": 1,
5616     "area": 1,
5617     "base": 1,
5618     "col": 1,
5619     "input": 1,
5620     "img": 1,
5621     "br": 1,
5622     "hr": 1,
5623     "param":1,
5624     "embed":1
5625 };
5626
5627 var invisibleTags = this.invisibleTags =
5628 {
5629     "HTML": 1,
5630     "HEAD": 1,
5631     "TITLE": 1,
5632     "META": 1,
5633     "LINK": 1,
5634     "STYLE": 1,
5635     "SCRIPT": 1,
5636     "NOSCRIPT": 1,
5637     "BR": 1,
5638     "PARAM": 1,
5639     "COL": 1,
5640
5641     "html": 1,
5642     "head": 1,
5643     "title": 1,
5644     "meta": 1,
5645     "link": 1,
5646     "style": 1,
5647     "script": 1,
5648     "noscript": 1,
5649     "br": 1,
5650     "param": 1,
5651     "col": 1
5652     /*
5653     "window": 1,
5654     "browser": 1,
5655     "frame": 1,
5656     "tabbrowser": 1,
5657     "WINDOW": 1,
5658     "BROWSER": 1,
5659     "FRAME": 1,
5660     "TABBROWSER": 1,
5661     */
5662 };
5663
5664
5665 if (typeof KeyEvent == "undefined") {
5666     this.KeyEvent = {
5667         DOM_VK_CANCEL: 3,
5668         DOM_VK_HELP: 6,
5669         DOM_VK_BACK_SPACE: 8,
5670         DOM_VK_TAB: 9,
5671         DOM_VK_CLEAR: 12,
5672         DOM_VK_RETURN: 13,
5673         DOM_VK_ENTER: 14,
5674         DOM_VK_SHIFT: 16,
5675         DOM_VK_CONTROL: 17,
5676         DOM_VK_ALT: 18,
5677         DOM_VK_PAUSE: 19,
5678         DOM_VK_CAPS_LOCK: 20,
5679         DOM_VK_ESCAPE: 27,
5680         DOM_VK_SPACE: 32,
5681         DOM_VK_PAGE_UP: 33,
5682         DOM_VK_PAGE_DOWN: 34,
5683         DOM_VK_END: 35,
5684         DOM_VK_HOME: 36,
5685         DOM_VK_LEFT: 37,
5686         DOM_VK_UP: 38,
5687         DOM_VK_RIGHT: 39,
5688         DOM_VK_DOWN: 40,
5689         DOM_VK_PRINTSCREEN: 44,
5690         DOM_VK_INSERT: 45,
5691         DOM_VK_DELETE: 46,
5692         DOM_VK_0: 48,
5693         DOM_VK_1: 49,
5694         DOM_VK_2: 50,
5695         DOM_VK_3: 51,
5696         DOM_VK_4: 52,
5697         DOM_VK_5: 53,
5698         DOM_VK_6: 54,
5699         DOM_VK_7: 55,
5700         DOM_VK_8: 56,
5701         DOM_VK_9: 57,
5702         DOM_VK_SEMICOLON: 59,
5703         DOM_VK_EQUALS: 61,
5704         DOM_VK_A: 65,
5705         DOM_VK_B: 66,
5706         DOM_VK_C: 67,
5707         DOM_VK_D: 68,
5708         DOM_VK_E: 69,
5709         DOM_VK_F: 70,
5710         DOM_VK_G: 71,
5711         DOM_VK_H: 72,
5712         DOM_VK_I: 73,
5713         DOM_VK_J: 74,
5714         DOM_VK_K: 75,
5715         DOM_VK_L: 76,
5716         DOM_VK_M: 77,
5717         DOM_VK_N: 78,
5718         DOM_VK_O: 79,
5719         DOM_VK_P: 80,
5720         DOM_VK_Q: 81,
5721         DOM_VK_R: 82,
5722         DOM_VK_S: 83,
5723         DOM_VK_T: 84,
5724         DOM_VK_U: 85,
5725         DOM_VK_V: 86,
5726         DOM_VK_W: 87,
5727         DOM_VK_X: 88,
5728         DOM_VK_Y: 89,
5729         DOM_VK_Z: 90,
5730         DOM_VK_CONTEXT_MENU: 93,
5731         DOM_VK_NUMPAD0: 96,
5732         DOM_VK_NUMPAD1: 97,
5733         DOM_VK_NUMPAD2: 98,
5734         DOM_VK_NUMPAD3: 99,
5735         DOM_VK_NUMPAD4: 100,
5736         DOM_VK_NUMPAD5: 101,
5737         DOM_VK_NUMPAD6: 102,
5738         DOM_VK_NUMPAD7: 103,
5739         DOM_VK_NUMPAD8: 104,
5740         DOM_VK_NUMPAD9: 105,
5741         DOM_VK_MULTIPLY: 106,
5742         DOM_VK_ADD: 107,
5743         DOM_VK_SEPARATOR: 108,
5744         DOM_VK_SUBTRACT: 109,
5745         DOM_VK_DECIMAL: 110,
5746         DOM_VK_DIVIDE: 111,
5747         DOM_VK_F1: 112,
5748         DOM_VK_F2: 113,
5749         DOM_VK_F3: 114,
5750         DOM_VK_F4: 115,
5751         DOM_VK_F5: 116,
5752         DOM_VK_F6: 117,
5753         DOM_VK_F7: 118,
5754         DOM_VK_F8: 119,
5755         DOM_VK_F9: 120,
5756         DOM_VK_F10: 121,
5757         DOM_VK_F11: 122,
5758         DOM_VK_F12: 123,
5759         DOM_VK_F13: 124,
5760         DOM_VK_F14: 125,
5761         DOM_VK_F15: 126,
5762         DOM_VK_F16: 127,
5763         DOM_VK_F17: 128,
5764         DOM_VK_F18: 129,
5765         DOM_VK_F19: 130,
5766         DOM_VK_F20: 131,
5767         DOM_VK_F21: 132,
5768         DOM_VK_F22: 133,
5769         DOM_VK_F23: 134,
5770         DOM_VK_F24: 135,
5771         DOM_VK_NUM_LOCK: 144,
5772         DOM_VK_SCROLL_LOCK: 145,
5773         DOM_VK_COMMA: 188,
5774         DOM_VK_PERIOD: 190,
5775         DOM_VK_SLASH: 191,
5776         DOM_VK_BACK_QUOTE: 192,
5777         DOM_VK_OPEN_BRACKET: 219,
5778         DOM_VK_BACK_SLASH: 220,
5779         DOM_VK_CLOSE_BRACKET: 221,
5780         DOM_VK_QUOTE: 222,
5781         DOM_VK_META: 224
5782     };
5783 }
5784
5785
5786 // ************************************************************************************************
5787 // Ajax
5788
5789 /**
5790  * @namespace
5791  */
5792 this.Ajax =
5793 {
5794
5795     requests: [],
5796     transport: null,
5797     states: ["Uninitialized","Loading","Loaded","Interactive","Complete"],
5798
5799     initialize: function()
5800     {
5801         this.transport = FBL.getNativeXHRObject();
5802     },
5803
5804     getXHRObject: function()
5805     {
5806         var xhrObj = false;
5807         try
5808         {
5809             xhrObj = new XMLHttpRequest();
5810         }
5811         catch(e)
5812         {
5813             var progid = [
5814                     "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0",
5815                     "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"
5816                 ];
5817
5818             for ( var i=0; i < progid.length; ++i ) {
5819                 try
5820                 {
5821                     xhrObj = new ActiveXObject(progid[i]);
5822                 }
5823                 catch(e)
5824                 {
5825                     continue;
5826                 }
5827                 break;
5828             }
5829         }
5830         finally
5831         {
5832             return xhrObj;
5833         }
5834     },
5835
5836
5837     /**
5838      * Create a AJAX request.
5839      *
5840      * @name request
5841      * @param {Object}   options               request options
5842      * @param {String}   options.url           URL to be requested
5843      * @param {String}   options.type          Request type ("get" ou "post"). Default is "get".
5844      * @param {Boolean}  options.async         Asynchronous flag. Default is "true".
5845      * @param {String}   options.dataType      Data type ("text", "html", "xml" or "json"). Default is "text".
5846      * @param {String}   options.contentType   Content-type of the data being sent. Default is "application/x-www-form-urlencoded".
5847      * @param {Function} options.onLoading     onLoading callback
5848      * @param {Function} options.onLoaded      onLoaded callback
5849      * @param {Function} options.onInteractive onInteractive callback
5850      * @param {Function} options.onComplete    onComplete callback
5851      * @param {Function} options.onUpdate      onUpdate callback
5852      * @param {Function} options.onSuccess     onSuccess callback
5853      * @param {Function} options.onFailure     onFailure callback
5854      */
5855     request: function(options)
5856     {
5857         // process options
5858         var o = FBL.extend(
5859                 {
5860                     // default values
5861                     type: "get",
5862                     async: true,
5863                     dataType: "text",
5864                     contentType: "application/x-www-form-urlencoded"
5865                 },
5866                 options || {}
5867             );
5868
5869         this.requests.push(o);
5870
5871         var s = this.getState();
5872         if (s == "Uninitialized" || s == "Complete" || s == "Loaded")
5873             this.sendRequest();
5874     },
5875
5876     serialize: function(data)
5877     {
5878         var r = [""], rl = 0;
5879         if (data) {
5880             if (typeof data == "string")  r[rl++] = data;
5881
5882             else if (data.innerHTML && data.elements) {
5883                 for (var i=0,el,l=(el=data.elements).length; i < l; i++)
5884                     if (el[i].name) {
5885                         r[rl++] = encodeURIComponent(el[i].name);
5886                         r[rl++] = "=";
5887                         r[rl++] = encodeURIComponent(el[i].value);
5888                         r[rl++] = "&";
5889                     }
5890
5891             } else
5892                 for(var param in data) {
5893                     r[rl++] = encodeURIComponent(param);
5894                     r[rl++] = "=";
5895                     r[rl++] = encodeURIComponent(data[param]);
5896                     r[rl++] = "&";
5897                 }
5898         }
5899         return r.join("").replace(/&$/, "");
5900     },
5901
5902     sendRequest: function()
5903     {
5904         var t = FBL.Ajax.transport, r = FBL.Ajax.requests.shift(), data;
5905
5906         // open XHR object
5907         t.open(r.type, r.url, r.async);
5908
5909         //setRequestHeaders();
5910
5911         // indicates that it is a XHR request to the server
5912         t.setRequestHeader("X-Requested-With", "XMLHttpRequest");
5913
5914         // if data is being sent, sets the appropriate content-type
5915         if (data = FBL.Ajax.serialize(r.data))
5916             t.setRequestHeader("Content-Type", r.contentType);
5917
5918         /** @ignore */
5919         // onreadystatechange handler
5920         t.onreadystatechange = function()
5921         {
5922             FBL.Ajax.onStateChange(r);
5923         };
5924
5925         // send the request
5926         t.send(data);
5927     },
5928
5929     /**
5930      * Handles the state change
5931      */
5932     onStateChange: function(options)
5933     {
5934         var fn, o = options, t = this.transport;
5935         var state = this.getState(t);
5936
5937         if (fn = o["on" + state]) fn(this.getResponse(o), o);
5938
5939         if (state == "Complete")
5940         {
5941             var success = t.status == 200, response = this.getResponse(o);
5942
5943             if (fn = o["onUpdate"])
5944               fn(response, o);
5945
5946             if (fn = o["on" + (success ? "Success" : "Failure")])
5947               fn(response, o);
5948
5949             t.onreadystatechange = FBL.emptyFn;
5950
5951             if (this.requests.length > 0)
5952                 setTimeout(this.sendRequest, 10);
5953         }
5954     },
5955
5956     /**
5957      * gets the appropriate response value according the type
5958      */
5959     getResponse: function(options)
5960     {
5961         var t = this.transport, type = options.dataType;
5962
5963         if      (t.status != 200) return t.statusText;
5964         else if (type == "text")  return t.responseText;
5965         else if (type == "html")  return t.responseText;
5966         else if (type == "xml")   return t.responseXML;
5967         else if (type == "json")  return eval("(" + t.responseText + ")");
5968     },
5969
5970     /**
5971      * returns the current state of the XHR object
5972      */
5973     getState: function()
5974     {
5975         return this.states[this.transport.readyState];
5976     }
5977
5978 };
5979
5980
5981 // ************************************************************************************************
5982 // Cookie, from http://www.quirksmode.org/js/cookies.html
5983
5984 this.createCookie = function(name,value,days)
5985 {
5986     if ('cookie' in document)
5987     {
5988         if (days)
5989         {
5990             var date = new Date();
5991             date.setTime(date.getTime()+(days*24*60*60*1000));
5992             var expires = "; expires="+date.toGMTString();
5993         }
5994         else
5995             var expires = "";
5996
5997         document.cookie = name+"="+value+expires+"; path=/";
5998     }
5999 };
6000
6001 this.readCookie = function (name)
6002 {
6003     if ('cookie' in document)
6004     {
6005         var nameEQ = name + "=";
6006         var ca = document.cookie.split(';');
6007
6008         for(var i=0; i < ca.length; i++)
6009         {
6010             var c = ca[i];
6011             while (c.charAt(0)==' ') c = c.substring(1,c.length);
6012             if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
6013         }
6014     }
6015
6016     return null;
6017 };
6018
6019 this.removeCookie = function(name)
6020 {
6021     this.createCookie(name, "", -1);
6022 };
6023
6024
6025 // ************************************************************************************************
6026 // http://www.mister-pixel.com/#Content__state=is_that_simple
6027 var fixIE6BackgroundImageCache = function(doc)
6028 {
6029     doc = doc || document;
6030     try
6031     {
6032         doc.execCommand("BackgroundImageCache", false, true);
6033     }
6034     catch(E)
6035     {
6036
6037     }
6038 };
6039
6040 // ************************************************************************************************
6041 // calculatePixelsPerInch
6042
6043 var resetStyle = "margin:0; padding:0; border:0; position:absolute; overflow:hidden; display:block;";
6044
6045 var calculatePixelsPerInch = function calculatePixelsPerInch(doc, body)
6046 {
6047     var inch = FBL.createGlobalElement("div");
6048     inch.style.cssText = resetStyle + "width:1in; height:1in; position:absolute; top:-1234px; left:-1234px;";
6049     body.appendChild(inch);
6050
6051     FBL.pixelsPerInch = {
6052         x: inch.offsetWidth,
6053         y: inch.offsetHeight
6054     };
6055
6056     body.removeChild(inch);
6057 };
6058
6059
6060 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6061
6062 this.SourceLink = function(url, line, type, object, instance)
6063 {
6064     this.href = url;
6065     this.instance = instance;
6066     this.line = line;
6067     this.type = type;
6068     this.object = object;
6069 };
6070
6071 this.SourceLink.prototype =
6072 {
6073     toString: function()
6074     {
6075         return this.href;
6076     },
6077     toJSON: function() // until 3.1...
6078     {
6079         return "{\"href\":\""+this.href+"\", "+
6080             (this.line?("\"line\":"+this.line+","):"")+
6081             (this.type?(" \"type\":\""+this.type+"\","):"")+
6082                     "}";
6083     }
6084
6085 };
6086
6087 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6088
6089 this.SourceText = function(lines, owner)
6090 {
6091     this.lines = lines;
6092     this.owner = owner;
6093 };
6094
6095 this.SourceText.getLineAsHTML = function(lineNo)
6096 {
6097     return escapeForSourceLine(this.lines[lineNo-1]);
6098 };
6099
6100
6101 // ************************************************************************************************
6102 }).apply(FBL);
6103
6104 /* See license.txt for terms of usage */
6105
6106 FBL.ns( /** @scope s_i18n */ function() { with (FBL) {
6107 // ************************************************************************************************
6108
6109 // TODO: xxxpedro localization
6110 var oSTR =
6111 {
6112     "NoMembersWarning": "There are no properties to show for this object.",
6113
6114     "EmptyStyleSheet": "There are no rules in this stylesheet.",
6115     "EmptyElementCSS": "This element has no style rules.",
6116     "AccessRestricted": "Access to restricted URI denied.",
6117
6118     "net.label.Parameters": "Parameters",
6119     "net.label.Source": "Source",
6120     "URLParameters": "Params",
6121
6122     "EditStyle": "Edit Element Style...",
6123     "NewRule": "New Rule...",
6124
6125     "NewProp": "New Property...",
6126     "EditProp": 'Edit "%s"',
6127     "DeleteProp": 'Delete "%s"',
6128     "DisableProp": 'Disable "%s"'
6129 };
6130
6131 // ************************************************************************************************
6132
6133 FBL.$STR = function(name)
6134 {
6135     return oSTR.hasOwnProperty(name) ? oSTR[name] : name;
6136 };
6137
6138 FBL.$STRF = function(name, args)
6139 {
6140     if (!oSTR.hasOwnProperty(name)) return name;
6141
6142     var format = oSTR[name];
6143     var objIndex = 0;
6144
6145     var parts = parseFormat(format);
6146     var trialIndex = objIndex;
6147     var objects = args;
6148
6149     for (var i= 0; i < parts.length; i++)
6150     {
6151         var part = parts[i];
6152         if (part && typeof(part) == "object")
6153         {
6154             if (++trialIndex > objects.length)  // then too few parameters for format, assume unformatted.
6155             {
6156                 format = "";
6157                 objIndex = -1;
6158                 parts.length = 0;
6159                 break;
6160             }
6161         }
6162
6163     }
6164
6165     var result = [];
6166     for (var i = 0; i < parts.length; ++i)
6167     {
6168         var part = parts[i];
6169         if (part && typeof(part) == "object")
6170         {
6171             result.push(""+args.shift());
6172         }
6173         else
6174             result.push(part);
6175     }
6176
6177     return result.join("");
6178 };
6179
6180 // ************************************************************************************************
6181
6182 var parseFormat = function parseFormat(format)
6183 {
6184     var parts = [];
6185     if (format.length <= 0)
6186         return parts;
6187
6188     var reg = /((^%|.%)(\d+)?(\.)([a-zA-Z]))|((^%|.%)([a-zA-Z]))/;
6189     for (var m = reg.exec(format); m; m = reg.exec(format))
6190     {
6191         if (m[0].substr(0, 2) == "%%")
6192         {
6193             parts.push(format.substr(0, m.index));
6194             parts.push(m[0].substr(1));
6195         }
6196         else
6197         {
6198             var type = m[8] ? m[8] : m[5];
6199             var precision = m[3] ? parseInt(m[3]) : (m[4] == "." ? -1 : 0);
6200
6201             var rep = null;
6202             switch (type)
6203             {
6204                 case "s":
6205                     rep = FirebugReps.Text;
6206                     break;
6207                 case "f":
6208                 case "i":
6209                 case "d":
6210                     rep = FirebugReps.Number;
6211                     break;
6212                 case "o":
6213                     rep = null;
6214                     break;
6215             }
6216
6217             parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index+1));
6218             parts.push({rep: rep, precision: precision, type: ("%" + type)});
6219         }
6220
6221         format = format.substr(m.index+m[0].length);
6222     }
6223
6224     parts.push(format);
6225     return parts;
6226 };
6227
6228 // ************************************************************************************************
6229 }});
6230
6231 /* See license.txt for terms of usage */
6232
6233 FBL.ns( /** @scope s_firebug */ function() { with (FBL) {
6234 // ************************************************************************************************
6235
6236 // ************************************************************************************************
6237 // Globals
6238
6239 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6240 // Internals
6241
6242 var modules = [];
6243 var panelTypes = [];
6244 var panelTypeMap = {};
6245 var reps = [];
6246
6247 var parentPanelMap = {};
6248
6249
6250 // ************************************************************************************************
6251 // Firebug
6252
6253 /**
6254  * @namespace describe Firebug
6255  * @exports FBL.Firebug as Firebug
6256  */
6257 FBL.Firebug =
6258 {
6259     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6260     version:  "Firebug Lite 1.4.0",
6261     revision: "$Revision$",
6262
6263     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6264     modules: modules,
6265     panelTypes: panelTypes,
6266     panelTypeMap: panelTypeMap,
6267     reps: reps,
6268
6269     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6270     // Initialization
6271
6272     initialize: function()
6273     {
6274         if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.initialize", "initializing application");
6275
6276         Firebug.browser = new Context(Env.browser);
6277         Firebug.context = Firebug.browser;
6278
6279         Firebug.loadPrefs();
6280         Firebug.context.persistedState.isOpen = false;
6281
6282         // Document must be cached before chrome initialization
6283         cacheDocument();
6284
6285         if (Firebug.Inspector && Firebug.Inspector.create)
6286             Firebug.Inspector.create();
6287
6288         if (FBL.CssAnalyzer && FBL.CssAnalyzer.processAllStyleSheets)
6289             FBL.CssAnalyzer.processAllStyleSheets(Firebug.browser.document);
6290
6291         FirebugChrome.initialize();
6292
6293         dispatch(modules, "initialize", []);
6294
6295         if (Firebug.disableResourceFetching)
6296             Firebug.Console.logFormatted(["Some Firebug Lite features are not working because " +
6297                         "resource fetching is disabled. To enabled it set the Firebug Lite option " +
6298                         "\"disableResourceFetching\" to \"false\". More info at " +
6299                         "http://getfirebug.com/firebuglite#Options"],
6300                         Firebug.context, "warn");
6301
6302         if (Env.onLoad)
6303         {
6304             var onLoad = Env.onLoad;
6305             delete Env.onLoad;
6306
6307             setTimeout(onLoad, 200);
6308         }
6309     },
6310
6311     shutdown: function()
6312     {
6313         if (Firebug.saveCookies)
6314             Firebug.savePrefs();
6315
6316         if (Firebug.Inspector)
6317             Firebug.Inspector.destroy();
6318
6319         dispatch(modules, "shutdown", []);
6320
6321         var chromeMap = FirebugChrome.chromeMap;
6322
6323         for (var name in chromeMap)
6324         {
6325             if (chromeMap.hasOwnProperty(name))
6326             {
6327                 try
6328                 {
6329                     chromeMap[name].destroy();
6330                 }
6331                 catch(E)
6332                 {
6333                     if (FBTrace.DBG_ERRORS) FBTrace.sysout("chrome.destroy() failed to: " + name);
6334                 }
6335             }
6336         }
6337
6338         Firebug.Lite.Cache.Element.clear();
6339         Firebug.Lite.Cache.StyleSheet.clear();
6340
6341         Firebug.browser = null;
6342         Firebug.context = null;
6343     },
6344
6345     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6346     // Registration
6347
6348     registerModule: function()
6349     {
6350         modules.push.apply(modules, arguments);
6351
6352         if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.registerModule");
6353     },
6354
6355     registerPanel: function()
6356     {
6357         panelTypes.push.apply(panelTypes, arguments);
6358
6359         for (var i = 0, panelType; panelType = arguments[i]; ++i)
6360         {
6361             panelTypeMap[panelType.prototype.name] = arguments[i];
6362
6363             if (panelType.prototype.parentPanel)
6364                 parentPanelMap[panelType.prototype.parentPanel] = 1;
6365         }
6366
6367         if (FBTrace.DBG_INITIALIZE)
6368             for (var i = 0; i < arguments.length; ++i)
6369                 FBTrace.sysout("Firebug.registerPanel", arguments[i].prototype.name);
6370     },
6371
6372     registerRep: function()
6373     {
6374         reps.push.apply(reps, arguments);
6375     },
6376
6377     unregisterRep: function()
6378     {
6379         for (var i = 0; i < arguments.length; ++i)
6380             remove(reps, arguments[i]);
6381     },
6382
6383     setDefaultReps: function(funcRep, rep)
6384     {
6385         FBL.defaultRep = rep;
6386         FBL.defaultFuncRep = funcRep;
6387     },
6388
6389     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6390     // Reps
6391
6392     getRep: function(object)
6393     {
6394         var type = typeof object;
6395         if (isIE && isFunction(object))
6396             type = "function";
6397
6398         for (var i = 0; i < reps.length; ++i)
6399         {
6400             var rep = reps[i];
6401             try
6402             {
6403                 if (rep.supportsObject(object, type))
6404                 {
6405                     if (FBTrace.DBG_DOM)
6406                         FBTrace.sysout("getRep type: "+type+" object: "+object, rep);
6407                     return rep;
6408                 }
6409             }
6410             catch (exc)
6411             {
6412                 if (FBTrace.DBG_ERRORS)
6413                 {
6414                     FBTrace.sysout("firebug.getRep FAILS: ", exc.message || exc);
6415                     FBTrace.sysout("firebug.getRep reps["+i+"/"+reps.length+"]: Rep="+reps[i].className);
6416                     // TODO: xxxpedro add trace to FBTrace logs like in Firebug
6417                     //firebug.trace();
6418                 }
6419             }
6420         }
6421
6422         return (type == 'function') ? defaultFuncRep : defaultRep;
6423     },
6424
6425     getRepObject: function(node)
6426     {
6427         var target = null;
6428         for (var child = node; child; child = child.parentNode)
6429         {
6430             if (hasClass(child, "repTarget"))
6431                 target = child;
6432
6433             if (child.repObject)
6434             {
6435                 if (!target && hasClass(child, "repIgnore"))
6436                     break;
6437                 else
6438                     return child.repObject;
6439             }
6440         }
6441     },
6442
6443     getRepNode: function(node)
6444     {
6445         for (var child = node; child; child = child.parentNode)
6446         {
6447             if (child.repObject)
6448                 return child;
6449         }
6450     },
6451
6452     getElementByRepObject: function(element, object)
6453     {
6454         for (var child = element.firstChild; child; child = child.nextSibling)
6455         {
6456             if (child.repObject == object)
6457                 return child;
6458         }
6459     },
6460
6461     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6462     // Preferences
6463
6464     getPref: function(name)
6465     {
6466         return Firebug[name];
6467     },
6468
6469     setPref: function(name, value)
6470     {
6471         Firebug[name] = value;
6472
6473         Firebug.savePrefs();
6474     },
6475
6476     setPrefs: function(prefs)
6477     {
6478         for (var name in prefs)
6479         {
6480             if (prefs.hasOwnProperty(name))
6481                 Firebug[name] = prefs[name];
6482         }
6483
6484         Firebug.savePrefs();
6485     },
6486
6487     restorePrefs: function()
6488     {
6489         var Options = Env.DefaultOptions;
6490
6491         for (var name in Options)
6492         {
6493             Firebug[name] = Options[name];
6494         }
6495     },
6496
6497     loadPrefs: function()
6498     {
6499         this.restorePrefs();
6500
6501         var prefs = Store.get("FirebugLite") || {};
6502         var options = prefs.options;
6503         var persistedState = prefs.persistedState || FBL.defaultPersistedState;
6504
6505         for (var name in options)
6506         {
6507             if (options.hasOwnProperty(name))
6508                 Firebug[name] = options[name];
6509         }
6510
6511         if (Firebug.context && persistedState)
6512             Firebug.context.persistedState = persistedState;
6513     },
6514
6515     savePrefs: function()
6516     {
6517         var prefs = {
6518             options: {}
6519         };
6520
6521         var EnvOptions = Env.Options;
6522         var options = prefs.options;
6523         for (var name in EnvOptions)
6524         {
6525             if (EnvOptions.hasOwnProperty(name))
6526             {
6527                 options[name] = Firebug[name];
6528             }
6529         }
6530
6531         var persistedState = Firebug.context.persistedState;
6532         if (!persistedState)
6533         {
6534             persistedState = Firebug.context.persistedState = FBL.defaultPersistedState;
6535         }
6536
6537         prefs.persistedState = persistedState;
6538
6539         Store.set("FirebugLite", prefs);
6540     },
6541
6542     erasePrefs: function()
6543     {
6544         Store.remove("FirebugLite");
6545         this.restorePrefs();
6546     }
6547 };
6548
6549 Firebug.restorePrefs();
6550
6551 // xxxpedro should we remove this?
6552 window.Firebug = FBL.Firebug;
6553
6554 if (!Env.Options.enablePersistent ||
6555      Env.Options.enablePersistent && Env.isChromeContext ||
6556      Env.isDebugMode)
6557         Env.browser.window.Firebug = FBL.Firebug;
6558
6559
6560 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6561 // Other methods
6562
6563 FBL.cacheDocument = function cacheDocument()
6564 {
6565     var ElementCache = Firebug.Lite.Cache.Element;
6566     var els = Firebug.browser.document.getElementsByTagName("*");
6567     for (var i=0, l=els.length, el; i<l; i++)
6568     {
6569         el = els[i];
6570         ElementCache(el);
6571     }
6572 };
6573
6574 // ************************************************************************************************
6575
6576 /**
6577  * @class
6578  *
6579  * Support for listeners registration. This object also extended by Firebug.Module so,
6580  * all modules supports listening automatically. Notice that array of listeners
6581  * is created for each intance of a module within initialize method. Thus all derived
6582  * module classes must ensure that Firebug.Module.initialize method is called for the
6583  * super class.
6584  */
6585 Firebug.Listener = function()
6586 {
6587     // The array is created when the first listeners is added.
6588     // It can't be created here since derived objects would share
6589     // the same array.
6590     this.fbListeners = null;
6591 };
6592
6593 Firebug.Listener.prototype =
6594 {
6595     addListener: function(listener)
6596     {
6597         if (!this.fbListeners)
6598             this.fbListeners = []; // delay the creation until the objects are created so 'this' causes new array for each module
6599
6600         this.fbListeners.push(listener);
6601     },
6602
6603     removeListener: function(listener)
6604     {
6605         remove(this.fbListeners, listener);  // if this.fbListeners is null, remove is being called with no add
6606     }
6607 };
6608
6609 // ************************************************************************************************
6610
6611
6612 // ************************************************************************************************
6613 // Module
6614
6615 /**
6616  * @module Base class for all modules. Every derived module object must be registered using
6617  * <code>Firebug.registerModule</code> method. There is always one instance of a module object
6618  * per browser window.
6619  * @extends Firebug.Listener
6620  */
6621 Firebug.Module = extend(new Firebug.Listener(),
6622 /** @extend Firebug.Module */
6623 {
6624     /**
6625      * Called when the window is opened.
6626      */
6627     initialize: function()
6628     {
6629     },
6630
6631     /**
6632      * Called when the window is closed.
6633      */
6634     shutdown: function()
6635     {
6636     },
6637
6638     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6639
6640     /**
6641      * Called when a new context is created but before the page is loaded.
6642      */
6643     initContext: function(context)
6644     {
6645     },
6646
6647     /**
6648      * Called after a context is detached to a separate window;
6649      */
6650     reattachContext: function(browser, context)
6651     {
6652     },
6653
6654     /**
6655      * Called when a context is destroyed. Module may store info on persistedState for reloaded pages.
6656      */
6657     destroyContext: function(context, persistedState)
6658     {
6659     },
6660
6661     // Called when a FF tab is create or activated (user changes FF tab)
6662     // Called after context is created or with context == null (to abort?)
6663     showContext: function(browser, context)
6664     {
6665     },
6666
6667     /**
6668      * Called after a context's page gets DOMContentLoaded
6669      */
6670     loadedContext: function(context)
6671     {
6672     },
6673
6674     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6675
6676     showPanel: function(browser, panel)
6677     {
6678     },
6679
6680     showSidePanel: function(browser, panel)
6681     {
6682     },
6683
6684     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6685
6686     updateOption: function(name, value)
6687     {
6688     },
6689
6690     getObjectByURL: function(context, url)
6691     {
6692     }
6693 });
6694
6695 // ************************************************************************************************
6696 // Panel
6697
6698 /**
6699  * @panel Base class for all panels. Every derived panel must define a constructor and
6700  * register with "Firebug.registerPanel" method. An instance of the panel
6701  * object is created by the framework for each browser tab where Firebug is activated.
6702  */
6703 Firebug.Panel =
6704 {
6705     name: "HelloWorld",
6706     title: "Hello World!",
6707
6708     parentPanel: null,
6709
6710     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6711
6712     options: {
6713         hasCommandLine: false,
6714         hasStatusBar: false,
6715         hasToolButtons: false,
6716
6717         // Pre-rendered panels are those included in the skin file (firebug.html)
6718         isPreRendered: false,
6719         innerHTMLSync: false
6720
6721         /*
6722         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6723         // To be used by external extensions
6724         panelHTML: "",
6725         panelCSS: "",
6726
6727         toolButtonsHTML: ""
6728         /**/
6729     },
6730
6731     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6732
6733     tabNode: null,
6734     panelNode: null,
6735     sidePanelNode: null,
6736     statusBarNode: null,
6737     toolButtonsNode: null,
6738
6739     panelBarNode: null,
6740
6741     sidePanelBarBoxNode: null,
6742     sidePanelBarNode: null,
6743
6744     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6745
6746     sidePanelBar: null,
6747
6748     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6749
6750     searchable: false,
6751     editable: true,
6752     order: 2147483647,
6753     statusSeparator: "<",
6754
6755     create: function(context, doc)
6756     {
6757         this.hasSidePanel = parentPanelMap.hasOwnProperty(this.name);
6758
6759         this.panelBarNode = $("fbPanelBar1");
6760         this.sidePanelBarBoxNode = $("fbPanelBar2");
6761
6762         if (this.hasSidePanel)
6763         {
6764             this.sidePanelBar = extend({}, PanelBar);
6765             this.sidePanelBar.create(this);
6766         }
6767
6768         var options = this.options = extend(Firebug.Panel.options, this.options);
6769         var panelId = "fb" + this.name;
6770
6771         if (options.isPreRendered)
6772         {
6773             this.panelNode = $(panelId);
6774
6775             this.tabNode = $(panelId + "Tab");
6776             this.tabNode.style.display = "block";
6777
6778             if (options.hasToolButtons)
6779             {
6780                 this.toolButtonsNode = $(panelId + "Buttons");
6781             }
6782
6783             if (options.hasStatusBar)
6784             {
6785                 this.statusBarBox = $("fbStatusBarBox");
6786                 this.statusBarNode = $(panelId + "StatusBar");
6787             }
6788         }
6789         else
6790         {
6791             var containerSufix = this.parentPanel ? "2" : "1";
6792
6793             // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6794             // Create Panel
6795             var panelNode = this.panelNode = createElement("div", {
6796                 id: panelId,
6797                 className: "fbPanel"
6798             });
6799
6800             $("fbPanel" + containerSufix).appendChild(panelNode);
6801
6802             // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6803             // Create Panel Tab
6804             var tabHTML = '<span class="fbTabL"></span><span class="fbTabText">' +
6805                     this.title + '</span><span class="fbTabR"></span>';
6806
6807             var tabNode = this.tabNode = createElement("a", {
6808                 id: panelId + "Tab",
6809                 className: "fbTab fbHover",
6810                 innerHTML: tabHTML
6811             });
6812
6813             if (isIE6)
6814             {
6815                 tabNode.href = "javascript:void(0)";
6816             }
6817
6818             var panelBarNode = this.parentPanel ?
6819                     Firebug.chrome.getPanel(this.parentPanel).sidePanelBarNode :
6820                     this.panelBarNode;
6821
6822             panelBarNode.appendChild(tabNode);
6823             tabNode.style.display = "block";
6824
6825             // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6826             // create ToolButtons
6827             if (options.hasToolButtons)
6828             {
6829                 this.toolButtonsNode = createElement("span", {
6830                     id: panelId + "Buttons",
6831                     className: "fbToolbarButtons"
6832                 });
6833
6834                 $("fbToolbarButtons").appendChild(this.toolButtonsNode);
6835             }
6836
6837             // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6838             // create StatusBar
6839             if (options.hasStatusBar)
6840             {
6841                 this.statusBarBox = $("fbStatusBarBox");
6842
6843                 this.statusBarNode = createElement("span", {
6844                     id: panelId + "StatusBar",
6845                     className: "fbToolbarButtons fbStatusBar"
6846                 });
6847
6848                 this.statusBarBox.appendChild(this.statusBarNode);
6849             }
6850
6851             // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6852             // create SidePanel
6853         }
6854
6855         this.containerNode = this.panelNode.parentNode;
6856
6857         if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.Panel.create", this.name);
6858
6859         // xxxpedro contextMenu
6860         this.onContextMenu = bind(this.onContextMenu, this);
6861
6862         /*
6863         this.context = context;
6864         this.document = doc;
6865
6866         this.panelNode = doc.createElement("div");
6867         this.panelNode.ownerPanel = this;
6868
6869         setClass(this.panelNode, "panelNode panelNode-"+this.name+" contextUID="+context.uid);
6870         doc.body.appendChild(this.panelNode);
6871
6872         if (FBTrace.DBG_INITIALIZE)
6873             FBTrace.sysout("firebug.initialize panelNode for "+this.name+"\n");
6874
6875         this.initializeNode(this.panelNode);
6876         /**/
6877     },
6878
6879     destroy: function(state) // Panel may store info on state
6880     {
6881         if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.Panel.destroy", this.name);
6882
6883         if (this.hasSidePanel)
6884         {
6885             this.sidePanelBar.destroy();
6886             this.sidePanelBar = null;
6887         }
6888
6889         this.options = null;
6890         this.name = null;
6891         this.parentPanel = null;
6892
6893         this.tabNode = null;
6894         this.panelNode = null;
6895         this.containerNode = null;
6896
6897         this.toolButtonsNode = null;
6898         this.statusBarBox = null;
6899         this.statusBarNode = null;
6900
6901         //if (this.panelNode)
6902         //    delete this.panelNode.ownerPanel;
6903
6904         //this.destroyNode();
6905     },
6906
6907     initialize: function()
6908     {
6909         if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.Panel.initialize", this.name);
6910
6911         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6912         if (this.hasSidePanel)
6913         {
6914             this.sidePanelBar.initialize();
6915         }
6916
6917         var options = this.options = extend(Firebug.Panel.options, this.options);
6918         var panelId = "fb" + this.name;
6919
6920         this.panelNode = $(panelId);
6921
6922         this.tabNode = $(panelId + "Tab");
6923         this.tabNode.style.display = "block";
6924
6925         if (options.hasStatusBar)
6926         {
6927             this.statusBarBox = $("fbStatusBarBox");
6928             this.statusBarNode = $(panelId + "StatusBar");
6929         }
6930
6931         if (options.hasToolButtons)
6932         {
6933             this.toolButtonsNode = $(panelId + "Buttons");
6934         }
6935
6936         this.containerNode = this.panelNode.parentNode;
6937
6938         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6939         // restore persistent state
6940         this.containerNode.scrollTop = this.lastScrollTop;
6941
6942         // xxxpedro contextMenu
6943         addEvent(this.containerNode, "contextmenu", this.onContextMenu);
6944
6945
6946         /// TODO: xxxpedro infoTip Hack
6947         Firebug.chrome.currentPanel =
6948                 Firebug.chrome.selectedPanel && Firebug.chrome.selectedPanel.sidePanelBar ?
6949                 Firebug.chrome.selectedPanel.sidePanelBar.selectedPanel :
6950                 Firebug.chrome.selectedPanel;
6951
6952         Firebug.showInfoTips = true;
6953         if (Firebug.InfoTip)
6954             Firebug.InfoTip.initializeBrowser(Firebug.chrome);
6955     },
6956
6957     shutdown: function()
6958     {
6959         if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.Panel.shutdown", this.name);
6960
6961         /// TODO: xxxpedro infoTip Hack
6962         if (Firebug.InfoTip)
6963             Firebug.InfoTip.uninitializeBrowser(Firebug.chrome);
6964
6965         if (Firebug.chrome.largeCommandLineVisible)
6966             Firebug.chrome.hideLargeCommandLine();
6967
6968         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6969         if (this.hasSidePanel)
6970         {
6971             // TODO: xxxpedro firebug1.3a6
6972             // new PanelBar mechanism will need to call shutdown to hide the panels (so it
6973             // doesn't appears in other panel's sidePanelBar. Therefore, we need to implement
6974             // a "remember selected panel" feature in the sidePanelBar
6975             //this.sidePanelBar.shutdown();
6976         }
6977
6978         // store persistent state
6979         this.lastScrollTop = this.containerNode.scrollTop;
6980
6981         // xxxpedro contextMenu
6982         removeEvent(this.containerNode, "contextmenu", this.onContextMenu);
6983     },
6984
6985     detach: function(oldChrome, newChrome)
6986     {
6987         if (oldChrome && oldChrome.selectedPanel && oldChrome.selectedPanel.name == this.name)
6988             this.lastScrollTop = oldChrome.selectedPanel.containerNode.scrollTop;
6989     },
6990
6991     reattach: function(doc)
6992     {
6993         if (this.options.innerHTMLSync)
6994             this.synchronizeUI();
6995     },
6996
6997     synchronizeUI: function()
6998     {
6999         this.containerNode.scrollTop = this.lastScrollTop || 0;
7000     },
7001
7002     show: function(state)
7003     {
7004         var options = this.options;
7005
7006         if (options.hasStatusBar)
7007         {
7008             this.statusBarBox.style.display = "inline";
7009             this.statusBarNode.style.display = "inline";
7010         }
7011
7012         if (options.hasToolButtons)
7013         {
7014             this.toolButtonsNode.style.display = "inline";
7015         }
7016
7017         this.panelNode.style.display = "block";
7018
7019         this.visible = true;
7020
7021         if (!this.parentPanel)
7022             Firebug.chrome.layout(this);
7023     },
7024
7025     hide: function(state)
7026     {
7027         var options = this.options;
7028
7029         if (options.hasStatusBar)
7030         {
7031             this.statusBarBox.style.display = "none";
7032             this.statusBarNode.style.display = "none";
7033         }
7034
7035         if (options.hasToolButtons)
7036         {
7037             this.toolButtonsNode.style.display = "none";
7038         }
7039
7040         this.panelNode.style.display = "none";
7041
7042         this.visible = false;
7043     },
7044
7045     watchWindow: function(win)
7046     {
7047     },
7048
7049     unwatchWindow: function(win)
7050     {
7051     },
7052
7053     updateOption: function(name, value)
7054     {
7055     },
7056
7057     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7058
7059     /**
7060      * Toolbar helpers
7061      */
7062     showToolbarButtons: function(buttonsId, show)
7063     {
7064         try
7065         {
7066             if (!this.context.browser) // XXXjjb this is bug. Somehow the panel context is not FirebugContext.
7067             {
7068                 if (FBTrace.DBG_ERRORS)
7069                     FBTrace.sysout("firebug.Panel showToolbarButtons this.context has no browser, this:", this);
7070
7071                 return;
7072             }
7073             var buttons = this.context.browser.chrome.$(buttonsId);
7074             if (buttons)
7075                 collapse(buttons, show ? "false" : "true");
7076         }
7077         catch (exc)
7078         {
7079             if (FBTrace.DBG_ERRORS)
7080             {
7081                 FBTrace.dumpProperties("firebug.Panel showToolbarButtons FAILS", exc);
7082                 if (!this.context.browser)FBTrace.dumpStack("firebug.Panel showToolbarButtons no browser");
7083             }
7084         }
7085     },
7086
7087     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7088
7089     /**
7090      * Returns a number indicating the view's ability to inspect the object.
7091      *
7092      * Zero means not supported, and higher numbers indicate specificity.
7093      */
7094     supportsObject: function(object)
7095     {
7096         return 0;
7097     },
7098
7099     hasObject: function(object)  // beyond type testing, is this object selectable?
7100     {
7101         return false;
7102     },
7103
7104     select: function(object, forceUpdate)
7105     {
7106         if (!object)
7107             object = this.getDefaultSelection(this.context);
7108
7109         if(FBTrace.DBG_PANELS)
7110             FBTrace.sysout("firebug.select "+this.name+" forceUpdate: "+forceUpdate+" "+object+((object==this.selection)?"==":"!=")+this.selection);
7111
7112         if (forceUpdate || object != this.selection)
7113         {
7114             this.selection = object;
7115             this.updateSelection(object);
7116
7117             // TODO: xxxpedro
7118             // XXXjoe This is kind of cheating, but, feh.
7119             //Firebug.chrome.onPanelSelect(object, this);
7120             //if (uiListeners.length > 0)
7121             //    dispatch(uiListeners, "onPanelSelect", [object, this]);  // TODO: make Firebug.chrome a uiListener
7122         }
7123     },
7124
7125     updateSelection: function(object)
7126     {
7127     },
7128
7129     markChange: function(skipSelf)
7130     {
7131         if (this.dependents)
7132         {
7133             if (skipSelf)
7134             {
7135                 for (var i = 0; i < this.dependents.length; ++i)
7136                 {
7137                     var panelName = this.dependents[i];
7138                     if (panelName != this.name)
7139                         this.context.invalidatePanels(panelName);
7140                 }
7141             }
7142             else
7143                 this.context.invalidatePanels.apply(this.context, this.dependents);
7144         }
7145     },
7146
7147     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7148
7149     startInspecting: function()
7150     {
7151     },
7152
7153     stopInspecting: function(object, cancelled)
7154     {
7155     },
7156
7157     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7158
7159     search: function(text, reverse)
7160     {
7161     },
7162
7163     /**
7164      * Retrieves the search options that this modules supports.
7165      * This is used by the search UI to present the proper options.
7166      */
7167     getSearchOptionsMenuItems: function()
7168     {
7169         return [
7170             Firebug.Search.searchOptionMenu("search.Case Sensitive", "searchCaseSensitive")
7171         ];
7172     },
7173
7174     /**
7175      * Navigates to the next document whose match parameter returns true.
7176      */
7177     navigateToNextDocument: function(match, reverse)
7178     {
7179         // This is an approximation of the UI that is displayed by the location
7180         // selector. This should be close enough, although it may be better
7181         // to simply generate the sorted list within the module, rather than
7182         // sorting within the UI.
7183         var self = this;
7184         function compare(a, b) {
7185             var locA = self.getObjectDescription(a);
7186             var locB = self.getObjectDescription(b);
7187             if(locA.path > locB.path)
7188                 return 1;
7189             if(locA.path < locB.path)
7190                 return -1;
7191             if(locA.name > locB.name)
7192                 return 1;
7193             if(locA.name < locB.name)
7194                 return -1;
7195             return 0;
7196         }
7197         var allLocs = this.getLocationList().sort(compare);
7198         for (var curPos = 0; curPos < allLocs.length && allLocs[curPos] != this.location; curPos++);
7199
7200         function transformIndex(index) {
7201             if (reverse) {
7202                 // For the reverse case we need to implement wrap around.
7203                 var intermediate = curPos - index - 1;
7204                 return (intermediate < 0 ? allLocs.length : 0) + intermediate;
7205             } else {
7206                 return (curPos + index + 1) % allLocs.length;
7207             }
7208         };
7209
7210         for (var next = 0; next < allLocs.length - 1; next++)
7211         {
7212             var object = allLocs[transformIndex(next)];
7213
7214             if (match(object))
7215             {
7216                 this.navigate(object);
7217                 return object;
7218             }
7219         }
7220     },
7221
7222     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7223
7224     // Called when "Options" clicked. Return array of
7225     // {label: 'name', nol10n: true,  type: "checkbox", checked: <value>, command:function to set <value>}
7226     getOptionsMenuItems: function()
7227     {
7228         return null;
7229     },
7230
7231     /*
7232      * Called by chrome.onContextMenu to build the context menu when this panel has focus.
7233      * See also FirebugRep for a similar function also called by onContextMenu
7234      * Extensions may monkey patch and chain off this call
7235      * @param object: the 'realObject', a model value, eg a DOM property
7236      * @param target: the HTML element clicked on.
7237      * @return an array of menu items.
7238      */
7239     getContextMenuItems: function(object, target)
7240     {
7241         return [];
7242     },
7243
7244     getBreakOnMenuItems: function()
7245     {
7246         return [];
7247     },
7248
7249     getEditor: function(target, value)
7250     {
7251     },
7252
7253     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7254
7255     getDefaultSelection: function()
7256     {
7257         return null;
7258     },
7259
7260     browseObject: function(object)
7261     {
7262     },
7263
7264     getPopupObject: function(target)
7265     {
7266         return Firebug.getRepObject(target);
7267     },
7268
7269     getTooltipObject: function(target)
7270     {
7271         return Firebug.getRepObject(target);
7272     },
7273
7274     showInfoTip: function(infoTip, x, y)
7275     {
7276
7277     },
7278
7279     getObjectPath: function(object)
7280     {
7281         return null;
7282     },
7283
7284     // An array of objects that can be passed to getObjectLocation.
7285     // The list of things a panel can show, eg sourceFiles.
7286     // Only shown if panel.location defined and supportsObject true
7287     getLocationList: function()
7288     {
7289         return null;
7290     },
7291
7292     getDefaultLocation: function()
7293     {
7294         return null;
7295     },
7296
7297     getObjectLocation: function(object)
7298     {
7299         return "";
7300     },
7301
7302     // Text for the location list menu eg script panel source file list
7303     // return.path: group/category label, return.name: item label
7304     getObjectDescription: function(object)
7305     {
7306         var url = this.getObjectLocation(object);
7307         return FBL.splitURLBase(url);
7308     },
7309
7310     /*
7311      *  UI signal that a tab needs attention, eg Script panel is currently stopped on a breakpoint
7312      *  @param: show boolean, true turns on.
7313      */
7314     highlight: function(show)
7315     {
7316         var tab = this.getTab();
7317         if (!tab)
7318             return;
7319
7320         if (show)
7321             tab.setAttribute("highlight", "true");
7322         else
7323             tab.removeAttribute("highlight");
7324     },
7325
7326     getTab: function()
7327     {
7328         var chrome = Firebug.chrome;
7329
7330         var tab = chrome.$("fbPanelBar2").getTab(this.name);
7331         if (!tab)
7332             tab = chrome.$("fbPanelBar1").getTab(this.name);
7333         return tab;
7334     },
7335
7336     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7337     // Support for Break On Next
7338
7339     /**
7340      * Called by the framework when the user clicks on the Break On Next button.
7341      * @param {Boolean} armed Set to true if the Break On Next feature is
7342      * to be armed for action and set to false if the Break On Next should be disarmed.
7343      * If 'armed' is true, then the next call to shouldBreakOnNext should be |true|.
7344      */
7345     breakOnNext: function(armed)
7346     {
7347     },
7348
7349     /**
7350      * Called when a panel is selected/displayed. The method should return true
7351      * if the Break On Next feature is currently armed for this panel.
7352      */
7353     shouldBreakOnNext: function()
7354     {
7355         return false;
7356     },
7357
7358     /**
7359      * Returns labels for Break On Next tooltip (one for enabled and one for disabled state).
7360      * @param {Boolean} enabled Set to true if the Break On Next feature is
7361      * currently activated for this panel.
7362      */
7363     getBreakOnNextTooltip: function(enabled)
7364     {
7365         return null;
7366     },
7367
7368     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7369
7370     // xxxpedro contextMenu
7371     onContextMenu: function(event)
7372     {
7373         if (!this.getContextMenuItems)
7374             return;
7375
7376         cancelEvent(event, true);
7377
7378         var target = event.target || event.srcElement;
7379
7380         var menu = this.getContextMenuItems(this.selection, target);
7381         if (!menu)
7382             return;
7383
7384         var contextMenu = new Menu(
7385         {
7386             id: "fbPanelContextMenu",
7387
7388             items: menu
7389         });
7390
7391         contextMenu.show(event.clientX, event.clientY);
7392
7393         return true;
7394
7395         /*
7396         // TODO: xxxpedro move code to somewhere. code to get cross-browser
7397         // window to screen coordinates
7398         var box = Firebug.browser.getElementPosition(Firebug.chrome.node);
7399
7400         var screenY = 0;
7401
7402         // Firefox
7403         if (typeof window.mozInnerScreenY != "undefined")
7404         {
7405             screenY = window.mozInnerScreenY;
7406         }
7407         // Chrome
7408         else if (typeof window.innerHeight != "undefined")
7409         {
7410             screenY = window.outerHeight - window.innerHeight;
7411         }
7412         // IE
7413         else if (typeof window.screenTop != "undefined")
7414         {
7415             screenY = window.screenTop;
7416         }
7417
7418         contextMenu.show(event.screenX-box.left, event.screenY-screenY-box.top);
7419         /**/
7420     }
7421
7422     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7423 };
7424
7425
7426 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7427
7428 /**
7429  * MeasureBox
7430  * To get pixels size.width and size.height:
7431  * <ul><li>     this.startMeasuring(view); </li>
7432  *     <li>     var size = this.measureText(lineNoCharsSpacer); </li>
7433  *     <li>     this.stopMeasuring(); </li>
7434  * </ul>
7435  *
7436  * @namespace
7437  */
7438 Firebug.MeasureBox =
7439 {
7440     startMeasuring: function(target)
7441     {
7442         if (!this.measureBox)
7443         {
7444             this.measureBox = target.ownerDocument.createElement("span");
7445             this.measureBox.className = "measureBox";
7446         }
7447
7448         copyTextStyles(target, this.measureBox);
7449         target.ownerDocument.body.appendChild(this.measureBox);
7450     },
7451
7452     getMeasuringElement: function()
7453     {
7454         return this.measureBox;
7455     },
7456
7457     measureText: function(value)
7458     {
7459         this.measureBox.innerHTML = value ? escapeForSourceLine(value) : "m";
7460         return {width: this.measureBox.offsetWidth, height: this.measureBox.offsetHeight-1};
7461     },
7462
7463     measureInputText: function(value)
7464     {
7465         value = value ? escapeForTextNode(value) : "m";
7466         if (!Firebug.showTextNodesWithWhitespace)
7467             value = value.replace(/\t/g,'mmmmmm').replace(/\ /g,'m');
7468         this.measureBox.innerHTML = value;
7469         return {width: this.measureBox.offsetWidth, height: this.measureBox.offsetHeight-1};
7470     },
7471
7472     getBox: function(target)
7473     {
7474         var style = this.measureBox.ownerDocument.defaultView.getComputedStyle(this.measureBox, "");
7475         var box = getBoxFromStyles(style, this.measureBox);
7476         return box;
7477     },
7478
7479     stopMeasuring: function()
7480     {
7481         this.measureBox.parentNode.removeChild(this.measureBox);
7482     }
7483 };
7484
7485
7486 // ************************************************************************************************
7487 if (FBL.domplate) Firebug.Rep = domplate(
7488 {
7489     className: "",
7490     inspectable: true,
7491
7492     supportsObject: function(object, type)
7493     {
7494         return false;
7495     },
7496
7497     inspectObject: function(object, context)
7498     {
7499         Firebug.chrome.select(object);
7500     },
7501
7502     browseObject: function(object, context)
7503     {
7504     },
7505
7506     persistObject: function(object, context)
7507     {
7508     },
7509
7510     getRealObject: function(object, context)
7511     {
7512         return object;
7513     },
7514
7515     getTitle: function(object)
7516     {
7517         var label = safeToString(object);
7518
7519         var re = /\[object (.*?)\]/;
7520         var m = re.exec(label);
7521
7522         ///return m ? m[1] : label;
7523
7524         // if the label is in the "[object TYPE]" format return its type
7525         if (m)
7526         {
7527             return m[1];
7528         }
7529         // if it is IE we need to handle some special cases
7530         else if (
7531                 // safeToString() fails to recognize some objects in IE
7532                 isIE &&
7533                 // safeToString() returns "[object]" for some objects like window.Image
7534                 (label == "[object]" ||
7535                 // safeToString() returns undefined for some objects like window.clientInformation
7536                 typeof object == "object" && typeof label == "undefined")
7537             )
7538         {
7539             return "Object";
7540         }
7541         else
7542         {
7543             return label;
7544         }
7545     },
7546
7547     getTooltip: function(object)
7548     {
7549         return null;
7550     },
7551
7552     getContextMenuItems: function(object, target, context)
7553     {
7554         return [];
7555     },
7556
7557     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7558     // Convenience for domplates
7559
7560     STR: function(name)
7561     {
7562         return $STR(name);
7563     },
7564
7565     cropString: function(text)
7566     {
7567         return cropString(text);
7568     },
7569
7570     cropMultipleLines: function(text, limit)
7571     {
7572         return cropMultipleLines(text, limit);
7573     },
7574
7575     toLowerCase: function(text)
7576     {
7577         return text ? text.toLowerCase() : text;
7578     },
7579
7580     plural: function(n)
7581     {
7582         return n == 1 ? "" : "s";
7583     }
7584 });
7585
7586 // ************************************************************************************************
7587
7588
7589 // ************************************************************************************************
7590 }});
7591
7592 /* See license.txt for terms of usage */
7593
7594 FBL.ns( /** @scope s_gui */ function() { with (FBL) {
7595 // ************************************************************************************************
7596
7597 // ************************************************************************************************
7598 // Controller
7599
7600 /**@namespace*/
7601 FBL.Controller = {
7602
7603     controllers: null,
7604     controllerContext: null,
7605
7606     initialize: function(context)
7607     {
7608         this.controllers = [];
7609         this.controllerContext = context || Firebug.chrome;
7610     },
7611
7612     shutdown: function()
7613     {
7614         this.removeControllers();
7615
7616         //this.controllers = null;
7617         //this.controllerContext = null;
7618     },
7619
7620     addController: function()
7621     {
7622         for (var i=0, arg; arg=arguments[i]; i++)
7623         {
7624             // If the first argument is a string, make a selector query
7625             // within the controller node context
7626             if (typeof arg[0] == "string")
7627             {
7628                 arg[0] = $$(arg[0], this.controllerContext);
7629             }
7630
7631             // bind the handler to the proper context
7632             var handler = arg[2];
7633             arg[2] = bind(handler, this);
7634             // save the original handler as an extra-argument, so we can
7635             // look for it later, when removing a particular controller
7636             arg[3] = handler;
7637
7638             this.controllers.push(arg);
7639             addEvent.apply(this, arg);
7640         }
7641     },
7642
7643     removeController: function()
7644     {
7645         for (var i=0, arg; arg=arguments[i]; i++)
7646         {
7647             for (var j=0, c; c=this.controllers[j]; j++)
7648             {
7649                 if (arg[0] == c[0] && arg[1] == c[1] && arg[2] == c[3])
7650                     removeEvent.apply(this, c);
7651             }
7652         }
7653     },
7654
7655     removeControllers: function()
7656     {
7657         for (var i=0, c; c=this.controllers[i]; i++)
7658         {
7659             removeEvent.apply(this, c);
7660         }
7661     }
7662 };
7663
7664
7665 // ************************************************************************************************
7666 // PanelBar
7667
7668 /**@namespace*/
7669 FBL.PanelBar =
7670 {
7671     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7672
7673     panelMap: null,
7674
7675     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7676
7677     selectedPanel: null,
7678     parentPanelName: null,
7679
7680     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7681
7682     create: function(ownerPanel)
7683     {
7684         this.panelMap = {};
7685         this.ownerPanel = ownerPanel;
7686
7687         if (ownerPanel)
7688         {
7689             ownerPanel.sidePanelBarNode = createElement("span");
7690             ownerPanel.sidePanelBarNode.style.display = "none";
7691             ownerPanel.sidePanelBarBoxNode.appendChild(ownerPanel.sidePanelBarNode);
7692         }
7693
7694         var panels = Firebug.panelTypes;
7695         for (var i=0, p; p=panels[i]; i++)
7696         {
7697             if ( // normal Panel  of the Chrome's PanelBar
7698                 !ownerPanel && !p.prototype.parentPanel ||
7699                 // Child Panel of the current Panel's SidePanelBar
7700                 ownerPanel && p.prototype.parentPanel &&
7701                 ownerPanel.name == p.prototype.parentPanel)
7702             {
7703                 this.addPanel(p.prototype.name);
7704             }
7705         }
7706     },
7707
7708     destroy: function()
7709     {
7710         PanelBar.shutdown.call(this);
7711
7712         for (var name in this.panelMap)
7713         {
7714             this.removePanel(name);
7715
7716             var panel = this.panelMap[name];
7717             panel.destroy();
7718
7719             this.panelMap[name] = null;
7720             delete this.panelMap[name];
7721         }
7722
7723         this.panelMap = null;
7724         this.ownerPanel = null;
7725     },
7726
7727     initialize: function()
7728     {
7729         if (this.ownerPanel)
7730             this.ownerPanel.sidePanelBarNode.style.display = "inline";
7731
7732         for(var name in this.panelMap)
7733         {
7734             (function(self, name){
7735
7736                 // tab click handler
7737                 var onTabClick = function onTabClick()
7738                 {
7739                     self.selectPanel(name);
7740                     return false;
7741                 };
7742
7743                 Firebug.chrome.addController([self.panelMap[name].tabNode, "mousedown", onTabClick]);
7744
7745             })(this, name);
7746         }
7747     },
7748
7749     shutdown: function()
7750     {
7751         var selectedPanel = this.selectedPanel;
7752
7753         if (selectedPanel)
7754         {
7755             removeClass(selectedPanel.tabNode, "fbSelectedTab");
7756             selectedPanel.hide();
7757             selectedPanel.shutdown();
7758         }
7759
7760         if (this.ownerPanel)
7761             this.ownerPanel.sidePanelBarNode.style.display = "none";
7762
7763         this.selectedPanel = null;
7764     },
7765
7766     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7767
7768     addPanel: function(panelName, parentPanel)
7769     {
7770         var PanelType = Firebug.panelTypeMap[panelName];
7771         var panel = this.panelMap[panelName] = new PanelType();
7772
7773         panel.create();
7774     },
7775
7776     removePanel: function(panelName)
7777     {
7778         var panel = this.panelMap[panelName];
7779         if (panel.hasOwnProperty(panelName))
7780             panel.destroy();
7781     },
7782
7783     selectPanel: function(panelName)
7784     {
7785         var selectedPanel = this.selectedPanel;
7786         var panel = this.panelMap[panelName];
7787
7788         if (panel && selectedPanel != panel)
7789         {
7790             if (selectedPanel)
7791             {
7792                 removeClass(selectedPanel.tabNode, "fbSelectedTab");
7793                 selectedPanel.shutdown();
7794                 selectedPanel.hide();
7795             }
7796
7797             if (!panel.parentPanel)
7798                 Firebug.context.persistedState.selectedPanelName = panelName;
7799
7800             this.selectedPanel = panel;
7801
7802             setClass(panel.tabNode, "fbSelectedTab");
7803             panel.show();
7804             panel.initialize();
7805         }
7806     },
7807
7808     getPanel: function(panelName)
7809     {
7810         var panel = this.panelMap[panelName];
7811
7812         return panel;
7813     }
7814
7815 };
7816
7817 //************************************************************************************************
7818 // Button
7819
7820 /**
7821  * options.element
7822  * options.caption
7823  * options.title
7824  *
7825  * options.owner
7826  * options.className
7827  * options.pressedClassName
7828  *
7829  * options.onPress
7830  * options.onUnpress
7831  * options.onClick
7832  *
7833  * @class
7834  * @extends FBL.Controller
7835  *
7836  */
7837
7838 FBL.Button = function(options)
7839 {
7840     options = options || {};
7841
7842     append(this, options);
7843
7844     this.state = "unpressed";
7845     this.display = "unpressed";
7846
7847     if (this.element)
7848     {
7849         this.container = this.element.parentNode;
7850     }
7851     else
7852     {
7853         this.shouldDestroy = true;
7854
7855         this.container = this.owner.getPanel().toolButtonsNode;
7856
7857         this.element = createElement("a", {
7858             className: this.baseClassName + " " + this.className + " fbHover",
7859             innerHTML: this.caption
7860         });
7861
7862         if (this.title)
7863             this.element.title = this.title;
7864
7865         this.container.appendChild(this.element);
7866     }
7867 };
7868
7869 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7870
7871 Button.prototype = extend(Controller,
7872 /**@extend FBL.Button.prototype*/
7873 {
7874     type: "normal",
7875     caption: "caption",
7876     title: null,
7877
7878     className: "", // custom class
7879     baseClassName: "fbButton", // control class
7880     pressedClassName: "fbBtnPressed", // control pressed class
7881
7882     element: null,
7883     container: null,
7884     owner: null,
7885
7886     state: null,
7887     display: null,
7888
7889     destroy: function()
7890     {
7891         this.shutdown();
7892
7893         // only remove if it is a dynamically generated button (not pre-rendered)
7894         if (this.shouldDestroy)
7895             this.container.removeChild(this.element);
7896
7897         this.element = null;
7898         this.container = null;
7899         this.owner = null;
7900     },
7901
7902     initialize: function()
7903     {
7904         Controller.initialize.apply(this);
7905
7906         var element = this.element;
7907
7908         this.addController([element, "mousedown", this.handlePress]);
7909
7910         if (this.type == "normal")
7911             this.addController(
7912                 [element, "mouseup", this.handleUnpress],
7913                 [element, "mouseout", this.handleUnpress],
7914                 [element, "click", this.handleClick]
7915             );
7916     },
7917
7918     shutdown: function()
7919     {
7920         Controller.shutdown.apply(this);
7921     },
7922
7923     restore: function()
7924     {
7925         this.changeState("unpressed");
7926     },
7927
7928     changeState: function(state)
7929     {
7930         this.state = state;
7931         this.changeDisplay(state);
7932     },
7933
7934     changeDisplay: function(display)
7935     {
7936         if (display != this.display)
7937         {
7938             if (display == "pressed")
7939             {
7940                 setClass(this.element, this.pressedClassName);
7941             }
7942             else if (display == "unpressed")
7943             {
7944                 removeClass(this.element, this.pressedClassName);
7945             }
7946             this.display = display;
7947         }
7948     },
7949
7950     handlePress: function(event)
7951     {
7952         cancelEvent(event, true);
7953
7954         if (this.type == "normal")
7955         {
7956             this.changeDisplay("pressed");
7957             this.beforeClick = true;
7958         }
7959         else if (this.type == "toggle")
7960         {
7961             if (this.state == "pressed")
7962             {
7963                 this.changeState("unpressed");
7964
7965                 if (this.onUnpress)
7966                     this.onUnpress.apply(this.owner, arguments);
7967             }
7968             else
7969             {
7970                 this.changeState("pressed");
7971
7972                 if (this.onPress)
7973                     this.onPress.apply(this.owner, arguments);
7974             }
7975
7976             if (this.onClick)
7977                 this.onClick.apply(this.owner, arguments);
7978         }
7979
7980         return false;
7981     },
7982
7983     handleUnpress: function(event)
7984     {
7985         cancelEvent(event, true);
7986
7987         if (this.beforeClick)
7988             this.changeDisplay("unpressed");
7989
7990         return false;
7991     },
7992
7993     handleClick: function(event)
7994     {
7995         cancelEvent(event, true);
7996
7997         if (this.type == "normal")
7998         {
7999             if (this.onClick)
8000                 this.onClick.apply(this.owner);
8001
8002             this.changeState("unpressed");
8003         }
8004
8005         this.beforeClick = false;
8006
8007         return false;
8008     }
8009 });
8010
8011 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8012
8013 /**
8014  * @class
8015  * @extends FBL.Button
8016  */
8017 FBL.IconButton = function()
8018 {
8019     Button.apply(this, arguments);
8020 };
8021
8022 IconButton.prototype = extend(Button.prototype,
8023 /**@extend FBL.IconButton.prototype*/
8024 {
8025     baseClassName: "fbIconButton",
8026     pressedClassName: "fbIconPressed"
8027 });
8028
8029
8030 //************************************************************************************************
8031 // Menu
8032
8033 var menuItemProps = {"class": "$item.className", type: "$item.type", value: "$item.value",
8034         _command: "$item.command"};
8035
8036 if (isIE6)
8037     menuItemProps.href = "javascript:void(0)";
8038
8039 // Allow GUI to be loaded even when Domplate module is not installed.
8040 if (FBL.domplate)
8041 var MenuPlate = domplate(Firebug.Rep,
8042 {
8043     tag:
8044         DIV({"class": "fbMenu fbShadow"},
8045             DIV({"class": "fbMenuContent fbShadowContent"},
8046                 FOR("item", "$object.items|memberIterator",
8047                     TAG("$item.tag", {item: "$item"})
8048                 )
8049             )
8050         ),
8051
8052     itemTag:
8053         A(menuItemProps,
8054             "$item.label"
8055         ),
8056
8057     checkBoxTag:
8058         A(extend(menuItemProps, {checked : "$item.checked"}),
8059
8060             "$item.label"
8061         ),
8062
8063     radioButtonTag:
8064         A(extend(menuItemProps, {selected : "$item.selected"}),
8065
8066             "$item.label"
8067         ),
8068
8069     groupTag:
8070         A(extend(menuItemProps, {child: "$item.child"}),
8071             "$item.label"
8072         ),
8073
8074     shortcutTag:
8075         A(menuItemProps,
8076             "$item.label",
8077             SPAN({"class": "fbMenuShortcutKey"},
8078                 "$item.key"
8079             )
8080         ),
8081
8082     separatorTag:
8083         SPAN({"class": "fbMenuSeparator"}),
8084
8085     memberIterator: function(items)
8086     {
8087         var result = [];
8088
8089         for (var i=0, length=items.length; i<length; i++)
8090         {
8091             var item = items[i];
8092
8093             // separator representation
8094             if (typeof item == "string" && item.indexOf("-") == 0)
8095             {
8096                 result.push({tag: this.separatorTag});
8097                 continue;
8098             }
8099
8100             item = extend(item, {});
8101
8102             item.type = item.type || "";
8103             item.value = item.value || "";
8104
8105             var type = item.type;
8106
8107             // default item representation
8108             item.tag = this.itemTag;
8109
8110             var className = item.className || "";
8111
8112             className += "fbMenuOption fbHover ";
8113
8114             // specific representations
8115             if (type == "checkbox")
8116             {
8117                 className += "fbMenuCheckBox ";
8118                 item.tag = this.checkBoxTag;
8119             }
8120             else if (type == "radiobutton")
8121             {
8122                 className += "fbMenuRadioButton ";
8123                 item.tag = this.radioButtonTag;
8124             }
8125             else if (type == "group")
8126             {
8127                 className += "fbMenuGroup ";
8128                 item.tag = this.groupTag;
8129             }
8130             else if (type == "shortcut")
8131             {
8132                 className += "fbMenuShortcut ";
8133                 item.tag = this.shortcutTag;
8134             }
8135
8136             if (item.checked)
8137                 className += "fbMenuChecked ";
8138             else if (item.selected)
8139                 className += "fbMenuRadioSelected ";
8140
8141             if (item.disabled)
8142                 className += "fbMenuDisabled ";
8143
8144             item.className = className;
8145
8146             item.label = $STR(item.label);
8147
8148             result.push(item);
8149         }
8150
8151         return result;
8152     }
8153 });
8154
8155 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8156
8157 /**
8158  * options
8159  * options.element
8160  * options.id
8161  * options.items
8162  *
8163  * item.label
8164  * item.className
8165  * item.type
8166  * item.value
8167  * item.disabled
8168  * item.checked
8169  * item.selected
8170  * item.command
8171  * item.child
8172  *
8173  *
8174  * @class
8175  * @extends FBL.Controller
8176  *
8177  */
8178 FBL.Menu = function(options)
8179 {
8180     // if element is not pre-rendered, we must render it now
8181     if (!options.element)
8182     {
8183         if (options.getItems)
8184             options.items = options.getItems();
8185
8186         options.element = MenuPlate.tag.append(
8187                 {object: options},
8188                 getElementByClass(Firebug.chrome.document, "fbBody"),
8189                 MenuPlate
8190             );
8191     }
8192
8193     // extend itself with the provided options
8194     append(this, options);
8195
8196     if (typeof this.element == "string")
8197     {
8198         this.id = this.element;
8199         this.element = $(this.id);
8200     }
8201     else if (this.id)
8202     {
8203         this.element.id = this.id;
8204     }
8205
8206     this.element.firebugIgnore = true;
8207     this.elementStyle = this.element.style;
8208
8209     this.isVisible = false;
8210
8211     this.handleMouseDown = bind(this.handleMouseDown, this);
8212     this.handleMouseOver = bind(this.handleMouseOver, this);
8213     this.handleMouseOut = bind(this.handleMouseOut, this);
8214
8215     this.handleWindowMouseDown = bind(this.handleWindowMouseDown, this);
8216 };
8217
8218 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8219
8220 var menuMap = {};
8221
8222 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8223
8224 Menu.prototype =  extend(Controller,
8225 /**@extend FBL.Menu.prototype*/
8226 {
8227     destroy: function()
8228     {
8229         //if (this.element) console.log("destroy", this.element.id);
8230
8231         this.hide();
8232
8233         // if it is a childMenu, remove its reference from the parentMenu
8234         if (this.parentMenu)
8235             this.parentMenu.childMenu = null;
8236
8237         // remove the element from the document
8238         this.element.parentNode.removeChild(this.element);
8239
8240         // clear references
8241         this.element = null;
8242         this.elementStyle = null;
8243         this.parentMenu = null;
8244         this.parentTarget = null;
8245     },
8246
8247     initialize: function()
8248     {
8249         Controller.initialize.call(this);
8250
8251         this.addController(
8252                 [this.element, "mousedown", this.handleMouseDown],
8253                 [this.element, "mouseover", this.handleMouseOver]
8254              );
8255     },
8256
8257     shutdown: function()
8258     {
8259         Controller.shutdown.call(this);
8260     },
8261
8262     show: function(x, y)
8263     {
8264         this.initialize();
8265
8266         if (this.isVisible) return;
8267
8268         //console.log("show", this.element.id);
8269
8270         x = x || 0;
8271         y = y || 0;
8272
8273         if (this.parentMenu)
8274         {
8275             var oldChildMenu = this.parentMenu.childMenu;
8276             if (oldChildMenu && oldChildMenu != this)
8277             {
8278                 oldChildMenu.destroy();
8279             }
8280
8281             this.parentMenu.childMenu = this;
8282         }
8283         else
8284             addEvent(Firebug.chrome.document, "mousedown", this.handleWindowMouseDown);
8285
8286         this.elementStyle.display = "block";
8287         this.elementStyle.visibility = "hidden";
8288
8289         var size = Firebug.chrome.getSize();
8290
8291         x = Math.min(x, size.width - this.element.clientWidth - 10);
8292         x = Math.max(x, 0);
8293
8294         y = Math.min(y, size.height - this.element.clientHeight - 10);
8295         y = Math.max(y, 0);
8296
8297         this.elementStyle.left = x + "px";
8298         this.elementStyle.top = y + "px";
8299
8300         this.elementStyle.visibility = "visible";
8301
8302         this.isVisible = true;
8303
8304         if (isFunction(this.onShow))
8305             this.onShow.apply(this, arguments);
8306     },
8307
8308     hide: function()
8309     {
8310         this.clearHideTimeout();
8311         this.clearShowChildTimeout();
8312
8313         if (!this.isVisible) return;
8314
8315         //console.log("hide", this.element.id);
8316
8317         this.elementStyle.display = "none";
8318
8319         if(this.childMenu)
8320         {
8321             this.childMenu.destroy();
8322             this.childMenu = null;
8323         }
8324
8325         if(this.parentTarget)
8326             removeClass(this.parentTarget, "fbMenuGroupSelected");
8327
8328         this.isVisible = false;
8329
8330         this.shutdown();
8331
8332         if (isFunction(this.onHide))
8333             this.onHide.apply(this, arguments);
8334     },
8335
8336     showChildMenu: function(target)
8337     {
8338         var id = target.getAttribute("child");
8339
8340         var parent = this;
8341         var target = target;
8342
8343         this.showChildTimeout = Firebug.chrome.window.setTimeout(function(){
8344
8345             //if (!parent.isVisible) return;
8346
8347             var box = Firebug.chrome.getElementBox(target);
8348
8349             var childMenuObject = menuMap.hasOwnProperty(id) ?
8350                     menuMap[id] : {element: $(id)};
8351
8352             var childMenu = new Menu(extend(childMenuObject,
8353                 {
8354                     parentMenu: parent,
8355                     parentTarget: target
8356                 }));
8357
8358             var offsetLeft = isIE6 ? -1 : -6; // IE6 problem with fixed position
8359             childMenu.show(box.left + box.width + offsetLeft, box.top -6);
8360             setClass(target, "fbMenuGroupSelected");
8361
8362         },350);
8363     },
8364
8365     clearHideTimeout: function()
8366     {
8367         if (this.hideTimeout)
8368         {
8369             Firebug.chrome.window.clearTimeout(this.hideTimeout);
8370             delete this.hideTimeout;
8371         }
8372     },
8373
8374     clearShowChildTimeout: function()
8375     {
8376         if(this.showChildTimeout)
8377         {
8378             Firebug.chrome.window.clearTimeout(this.showChildTimeout);
8379             this.showChildTimeout = null;
8380         }
8381     },
8382
8383     handleMouseDown: function(event)
8384     {
8385         cancelEvent(event, true);
8386
8387         var topParent = this;
8388         while (topParent.parentMenu)
8389             topParent = topParent.parentMenu;
8390
8391         var target = event.target || event.srcElement;
8392
8393         target = getAncestorByClass(target, "fbMenuOption");
8394
8395         if(!target || hasClass(target, "fbMenuGroup"))
8396             return false;
8397
8398         if (target && !hasClass(target, "fbMenuDisabled"))
8399         {
8400             var type = target.getAttribute("type");
8401
8402             if (type == "checkbox")
8403             {
8404                 var checked = target.getAttribute("checked");
8405                 var value = target.getAttribute("value");
8406                 var wasChecked = hasClass(target, "fbMenuChecked");
8407
8408                 if (wasChecked)
8409                 {
8410                     removeClass(target, "fbMenuChecked");
8411                     target.setAttribute("checked", "");
8412                 }
8413                 else
8414                 {
8415                     setClass(target, "fbMenuChecked");
8416                     target.setAttribute("checked", "true");
8417                 }
8418
8419                 if (isFunction(this.onCheck))
8420                     this.onCheck.call(this, target, value, !wasChecked);
8421             }
8422
8423             if (type == "radiobutton")
8424             {
8425                 var selectedRadios = getElementsByClass(target.parentNode, "fbMenuRadioSelected");
8426
8427                 var group = target.getAttribute("group");
8428
8429                 for (var i = 0, length = selectedRadios.length; i < length; i++)
8430                 {
8431                     radio = selectedRadios[i];
8432
8433                     if (radio.getAttribute("group") == group)
8434                     {
8435                         removeClass(radio, "fbMenuRadioSelected");
8436                         radio.setAttribute("selected", "");
8437                     }
8438                 }
8439
8440                 setClass(target, "fbMenuRadioSelected");
8441                 target.setAttribute("selected", "true");
8442             }
8443
8444             var handler = null;
8445
8446             // target.command can be a function or a string.
8447             var cmd = target.command;
8448
8449             // If it is a function it will be used as the handler
8450             if (isFunction(cmd))
8451                 handler = cmd;
8452             // If it is a string it the property of the current menu object
8453             // will be used as the handler
8454             else if (typeof cmd == "string")
8455                 handler = this[cmd];
8456
8457             var closeMenu = true;
8458
8459             if (handler)
8460                 closeMenu = handler.call(this, target) !== false;
8461
8462             if (closeMenu)
8463                 topParent.hide();
8464         }
8465
8466         return false;
8467     },
8468
8469     handleWindowMouseDown: function(event)
8470     {
8471         //console.log("handleWindowMouseDown");
8472
8473         var target = event.target || event.srcElement;
8474
8475         target = getAncestorByClass(target, "fbMenu");
8476
8477         if (!target)
8478         {
8479             removeEvent(Firebug.chrome.document, "mousedown", this.handleWindowMouseDown);
8480             this.hide();
8481         }
8482     },
8483
8484     handleMouseOver: function(event)
8485     {
8486         //console.log("handleMouseOver", this.element.id);
8487
8488         this.clearHideTimeout();
8489         this.clearShowChildTimeout();
8490
8491         var target = event.target || event.srcElement;
8492
8493         target = getAncestorByClass(target, "fbMenuOption");
8494
8495         if(!target)
8496             return;
8497
8498         var childMenu = this.childMenu;
8499         if(childMenu)
8500         {
8501             removeClass(childMenu.parentTarget, "fbMenuGroupSelected");
8502
8503             if (childMenu.parentTarget != target && childMenu.isVisible)
8504             {
8505                 childMenu.clearHideTimeout();
8506                 childMenu.hideTimeout = Firebug.chrome.window.setTimeout(function(){
8507                     childMenu.destroy();
8508                 },300);
8509             }
8510         }
8511
8512         if(hasClass(target, "fbMenuGroup"))
8513         {
8514             this.showChildMenu(target);
8515         }
8516     }
8517 });
8518
8519 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8520
8521 append(Menu,
8522 /**@extend FBL.Menu*/
8523 {
8524     register: function(object)
8525     {
8526         menuMap[object.id] = object;
8527     },
8528
8529     check: function(element)
8530     {
8531         setClass(element, "fbMenuChecked");
8532         element.setAttribute("checked", "true");
8533     },
8534
8535     uncheck: function(element)
8536     {
8537         removeClass(element, "fbMenuChecked");
8538         element.setAttribute("checked", "");
8539     },
8540
8541     disable: function(element)
8542     {
8543         setClass(element, "fbMenuDisabled");
8544     },
8545
8546     enable: function(element)
8547     {
8548         removeClass(element, "fbMenuDisabled");
8549     }
8550 });
8551
8552
8553 //************************************************************************************************
8554 // Status Bar
8555
8556 /**@class*/
8557 function StatusBar(){};
8558
8559 StatusBar.prototype = extend(Controller, {
8560
8561 });
8562
8563 // ************************************************************************************************
8564
8565
8566 // ************************************************************************************************
8567 }});
8568
8569 /* See license.txt for terms of usage */
8570
8571 FBL.ns( /**@scope s_context*/ function() { with (FBL) {
8572 // ************************************************************************************************
8573
8574 // ************************************************************************************************
8575 // Globals
8576
8577 var refreshDelay = 300;
8578
8579 // Opera and some versions of webkit returns the wrong value of document.elementFromPoint()
8580 // function, without taking into account the scroll position. Safari 4 (webkit/531.21.8)
8581 // still have this issue. Google Chrome 4 (webkit/532.5) does not. So, we're assuming this
8582 // issue was fixed in the 532 version
8583 var shouldFixElementFromPoint = isOpera || isSafari && browserVersion < "532";
8584
8585 var evalError = "___firebug_evaluation_error___";
8586 var pixelsPerInch;
8587
8588 var resetStyle = "margin:0; padding:0; border:0; position:absolute; overflow:hidden; display:block;";
8589 var offscreenStyle = resetStyle + "top:-1234px; left:-1234px;";
8590
8591
8592 // ************************************************************************************************
8593 // Context
8594
8595 /** @class */
8596 FBL.Context = function(win)
8597 {
8598     this.window = win.window;
8599     this.document = win.document;
8600
8601     this.browser = Env.browser;
8602
8603     // Some windows in IE, like iframe, doesn't have the eval() method
8604     if (isIE && !this.window.eval)
8605     {
8606         // But after executing the following line the method magically appears!
8607         this.window.execScript("null");
8608         // Just to make sure the "magic" really happened
8609         if (!this.window.eval)
8610             throw new Error("Firebug Error: eval() method not found in this window");
8611     }
8612
8613     // Create a new "black-box" eval() method that runs in the global namespace
8614     // of the context window, without exposing the local variables declared
8615     // by the function that calls it
8616     this.eval = this.window.eval("new Function('" +
8617             "try{ return window.eval.apply(window,arguments) }catch(E){ E."+evalError+"=true; return E }" +
8618         "')");
8619 };
8620
8621 FBL.Context.prototype =
8622 {
8623     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8624     // partial-port of Firebug tabContext.js
8625
8626     browser: null,
8627     loaded: true,
8628
8629     setTimeout: function(fn, delay)
8630     {
8631         var win = this.window;
8632
8633         if (win.setTimeout == this.setTimeout)
8634             throw new Error("setTimeout recursion");
8635
8636         var timeout = win.setTimeout.apply ? // IE doesn't have apply method on setTimeout
8637                 win.setTimeout.apply(win, arguments) :
8638                 win.setTimeout(fn, delay);
8639
8640         if (!this.timeouts)
8641             this.timeouts = {};
8642
8643         this.timeouts[timeout] = 1;
8644
8645         return timeout;
8646     },
8647
8648     clearTimeout: function(timeout)
8649     {
8650         clearTimeout(timeout);
8651
8652         if (this.timeouts)
8653             delete this.timeouts[timeout];
8654     },
8655
8656     setInterval: function(fn, delay)
8657     {
8658         var win = this.window;
8659
8660         var timeout = win.setInterval.apply ? // IE doesn't have apply method on setTimeout
8661                 win.setInterval.apply(win, arguments) :
8662                 win.setInterval(fn, delay);
8663
8664         if (!this.intervals)
8665             this.intervals = {};
8666
8667         this.intervals[timeout] = 1;
8668
8669         return timeout;
8670     },
8671
8672     clearInterval: function(timeout)
8673     {
8674         clearInterval(timeout);
8675
8676         if (this.intervals)
8677             delete this.intervals[timeout];
8678     },
8679
8680     invalidatePanels: function()
8681     {
8682         if (!this.invalidPanels)
8683             this.invalidPanels = {};
8684
8685         for (var i = 0; i < arguments.length; ++i)
8686         {
8687             var panelName = arguments[i];
8688
8689             // avoid error. need to create a better getPanel() function as explained below
8690             if (!Firebug.chrome || !Firebug.chrome.selectedPanel)
8691                 return;
8692
8693             //var panel = this.getPanel(panelName, true);
8694             //TODO: xxxpedro context how to get all panels using a single function?
8695             // the current workaround to make the invalidation works is invalidating
8696             // only sidePanels. There's also a problem with panel name (LowerCase in Firebug Lite)
8697             var panel = Firebug.chrome.selectedPanel.sidePanelBar ?
8698                     Firebug.chrome.selectedPanel.sidePanelBar.getPanel(panelName, true) :
8699                     null;
8700
8701             if (panel && !panel.noRefresh)
8702                 this.invalidPanels[panelName] = 1;
8703         }
8704
8705         if (this.refreshTimeout)
8706         {
8707             this.clearTimeout(this.refreshTimeout);
8708             delete this.refreshTimeout;
8709         }
8710
8711         this.refreshTimeout = this.setTimeout(bindFixed(function()
8712         {
8713             var invalids = [];
8714
8715             for (var panelName in this.invalidPanels)
8716             {
8717                 //var panel = this.getPanel(panelName, true);
8718                 //TODO: xxxpedro context how to get all panels using a single function?
8719                 // the current workaround to make the invalidation works is invalidating
8720                 // only sidePanels. There's also a problem with panel name (LowerCase in Firebug Lite)
8721                 var panel = Firebug.chrome.selectedPanel.sidePanelBar ?
8722                         Firebug.chrome.selectedPanel.sidePanelBar.getPanel(panelName, true) :
8723                         null;
8724
8725                 if (panel)
8726                 {
8727                     if (panel.visible && !panel.editing)
8728                         panel.refresh();
8729                     else
8730                         panel.needsRefresh = true;
8731
8732                     // If the panel is being edited, we'll keep trying to
8733                     // refresh it until editing is done
8734                     if (panel.editing)
8735                         invalids.push(panelName);
8736                 }
8737             }
8738
8739             delete this.invalidPanels;
8740             delete this.refreshTimeout;
8741
8742             // Keep looping until every tab is valid
8743             if (invalids.length)
8744                 this.invalidatePanels.apply(this, invalids);
8745         }, this), refreshDelay);
8746     },
8747
8748
8749     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8750     // Evalutation Method
8751
8752     /**
8753      * Evaluates an expression in the current context window.
8754      *
8755      * @param {String}   expr           expression to be evaluated
8756      *
8757      * @param {String}   context        string indicating the global location
8758      *                                  of the object that will be used as the
8759      *                                  context. The context is referred in
8760      *                                  the expression as the "this" keyword.
8761      *                                  If no context is informed, the "window"
8762      *                                  context is used.
8763      *
8764      * @param {String}   api            string indicating the global location
8765      *                                  of the object that will be used as the
8766      *                                  api of the evaluation.
8767      *
8768      * @param {Function} errorHandler(message) error handler to be called
8769      *                                         if the evaluation fails.
8770      */
8771     evaluate: function(expr, context, api, errorHandler)
8772     {
8773         // the default context is the "window" object. It can be any string that represents
8774         // a global accessible element as: "my.namespaced.object"
8775         context = context || "window";
8776
8777         var isObjectLiteral = trim(expr).indexOf("{") == 0,
8778             cmd,
8779             result;
8780
8781         // if the context is the "window" object, we don't need a closure
8782         if (context == "window")
8783         {
8784             // If it is an object literal, then wrap the expression with parenthesis so we can
8785             // capture the return value
8786             if (isObjectLiteral)
8787             {
8788                 cmd = api ?
8789                     "with("+api+"){ ("+expr+") }" :
8790                     "(" + expr + ")";
8791             }
8792             else
8793             {
8794                 cmd = api ?
8795                     "with("+api+"){ "+expr+" }" :
8796                     expr;
8797             }
8798         }
8799         else
8800         {
8801             cmd = api ?
8802                 // with API and context, no return value
8803                 "(function(arguments){ with(" + api + "){ " +
8804                     expr +
8805                 " } }).call(" + context + ",undefined)"
8806                 :
8807                 // with context only, no return value
8808                 "(function(arguments){ " +
8809                     expr +
8810                 " }).call(" + context + ",undefined)";
8811         }
8812
8813         result = this.eval(cmd);
8814
8815         if (result && result[evalError])
8816         {
8817             var msg = result.name ? (result.name + ": ") : "";
8818             msg += result.message || result;
8819
8820             if (errorHandler)
8821                 result = errorHandler(msg);
8822             else
8823                 result = msg;
8824         }
8825
8826         return result;
8827     },
8828
8829
8830     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8831     // Window Methods
8832
8833     getWindowSize: function()
8834     {
8835         var width=0, height=0, el;
8836
8837         if (typeof this.window.innerWidth == "number")
8838         {
8839             width = this.window.innerWidth;
8840             height = this.window.innerHeight;
8841         }
8842         else if ((el=this.document.documentElement) && (el.clientHeight || el.clientWidth))
8843         {
8844             width = el.clientWidth;
8845             height = el.clientHeight;
8846         }
8847         else if ((el=this.document.body) && (el.clientHeight || el.clientWidth))
8848         {
8849             width = el.clientWidth;
8850             height = el.clientHeight;
8851         }
8852
8853         return {width: width, height: height};
8854     },
8855
8856     getWindowScrollSize: function()
8857     {
8858         var width=0, height=0, el;
8859
8860         // first try the document.documentElement scroll size
8861         if (!isIEQuiksMode && (el=this.document.documentElement) &&
8862            (el.scrollHeight || el.scrollWidth))
8863         {
8864             width = el.scrollWidth;
8865             height = el.scrollHeight;
8866         }
8867
8868         // then we need to check if document.body has a bigger scroll size value
8869         // because sometimes depending on the browser and the page, the document.body
8870         // scroll size returns a smaller (and wrong) measure
8871         if ((el=this.document.body) && (el.scrollHeight || el.scrollWidth) &&
8872             (el.scrollWidth > width || el.scrollHeight > height))
8873         {
8874             width = el.scrollWidth;
8875             height = el.scrollHeight;
8876         }
8877
8878         return {width: width, height: height};
8879     },
8880
8881     getWindowScrollPosition: function()
8882     {
8883         var top=0, left=0, el;
8884
8885         if(typeof this.window.pageYOffset == "number")
8886         {
8887             top = this.window.pageYOffset;
8888             left = this.window.pageXOffset;
8889         }
8890         else if((el=this.document.body) && (el.scrollTop || el.scrollLeft))
8891         {
8892             top = el.scrollTop;
8893             left = el.scrollLeft;
8894         }
8895         else if((el=this.document.documentElement) && (el.scrollTop || el.scrollLeft))
8896         {
8897             top = el.scrollTop;
8898             left = el.scrollLeft;
8899         }
8900
8901         return {top:top, left:left};
8902     },
8903
8904
8905     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8906     // Element Methods
8907
8908     getElementFromPoint: function(x, y)
8909     {
8910         if (shouldFixElementFromPoint)
8911         {
8912             var scroll = this.getWindowScrollPosition();
8913             return this.document.elementFromPoint(x + scroll.left, y + scroll.top);
8914         }
8915         else
8916             return this.document.elementFromPoint(x, y);
8917     },
8918
8919     getElementPosition: function(el)
8920     {
8921         var left = 0;
8922         var top = 0;
8923
8924         do
8925         {
8926             left += el.offsetLeft;
8927             top += el.offsetTop;
8928         }
8929         while (el = el.offsetParent);
8930
8931         return {left:left, top:top};
8932     },
8933
8934     getElementBox: function(el)
8935     {
8936         var result = {};
8937
8938         if (el.getBoundingClientRect)
8939         {
8940             var rect = el.getBoundingClientRect();
8941
8942             // fix IE problem with offset when not in fullscreen mode
8943             var offset = isIE ? this.document.body.clientTop || this.document.documentElement.clientTop: 0;
8944
8945             var scroll = this.getWindowScrollPosition();
8946
8947             result.top = Math.round(rect.top - offset + scroll.top);
8948             result.left = Math.round(rect.left - offset + scroll.left);
8949             result.height = Math.round(rect.bottom - rect.top);
8950             result.width = Math.round(rect.right - rect.left);
8951         }
8952         else
8953         {
8954             var position = this.getElementPosition(el);
8955
8956             result.top = position.top;
8957             result.left = position.left;
8958             result.height = el.offsetHeight;
8959             result.width = el.offsetWidth;
8960         }
8961
8962         return result;
8963     },
8964
8965
8966     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8967     // Measurement Methods
8968
8969     getMeasurement: function(el, name)
8970     {
8971         var result = {value: 0, unit: "px"};
8972
8973         var cssValue = this.getStyle(el, name);
8974
8975         if (!cssValue) return result;
8976         if (cssValue.toLowerCase() == "auto") return result;
8977
8978         var reMeasure = /(\d+\.?\d*)(.*)/;
8979         var m = cssValue.match(reMeasure);
8980
8981         if (m)
8982         {
8983             result.value = m[1]-0;
8984             result.unit = m[2].toLowerCase();
8985         }
8986
8987         return result;
8988     },
8989
8990     getMeasurementInPixels: function(el, name)
8991     {
8992         if (!el) return null;
8993
8994         var m = this.getMeasurement(el, name);
8995         var value = m.value;
8996         var unit = m.unit;
8997
8998         if (unit == "px")
8999             return value;
9000
9001         else if (unit == "pt")
9002             return this.pointsToPixels(name, value);
9003
9004         else if (unit == "em")
9005             return this.emToPixels(el, value);
9006
9007         else if (unit == "%")
9008             return this.percentToPixels(el, value);
9009
9010         else if (unit == "ex")
9011             return this.exToPixels(el, value);
9012
9013         // TODO: add other units. Maybe create a better general way
9014         // to calculate measurements in different units.
9015     },
9016
9017     getMeasurementBox1: function(el, name)
9018     {
9019         var sufixes = ["Top", "Left", "Bottom", "Right"];
9020         var result = [];
9021
9022         for(var i=0, sufix; sufix=sufixes[i]; i++)
9023             result[i] = Math.round(this.getMeasurementInPixels(el, name + sufix));
9024
9025         return {top:result[0], left:result[1], bottom:result[2], right:result[3]};
9026     },
9027
9028     getMeasurementBox: function(el, name)
9029     {
9030         var result = [];
9031         var sufixes = name == "border" ?
9032                 ["TopWidth", "LeftWidth", "BottomWidth", "RightWidth"] :
9033                 ["Top", "Left", "Bottom", "Right"];
9034
9035         if (isIE)
9036         {
9037             var propName, cssValue;
9038             var autoMargin = null;
9039
9040             for(var i=0, sufix; sufix=sufixes[i]; i++)
9041             {
9042                 propName = name + sufix;
9043
9044                 cssValue = el.currentStyle[propName] || el.style[propName];
9045
9046                 if (cssValue == "auto")
9047                 {
9048                     if (!autoMargin)
9049                         autoMargin = this.getCSSAutoMarginBox(el);
9050
9051                     result[i] = autoMargin[sufix.toLowerCase()];
9052                 }
9053                 else
9054                     result[i] = this.getMeasurementInPixels(el, propName);
9055
9056             }
9057
9058         }
9059         else
9060         {
9061             for(var i=0, sufix; sufix=sufixes[i]; i++)
9062                 result[i] = this.getMeasurementInPixels(el, name + sufix);
9063         }
9064
9065         return {top:result[0], left:result[1], bottom:result[2], right:result[3]};
9066     },
9067
9068     getCSSAutoMarginBox: function(el)
9069     {
9070         if (isIE && " meta title input script link a ".indexOf(" "+el.nodeName.toLowerCase()+" ") != -1)
9071             return {top:0, left:0, bottom:0, right:0};
9072             /**/
9073
9074         if (isIE && " h1 h2 h3 h4 h5 h6 h7 ul p ".indexOf(" "+el.nodeName.toLowerCase()+" ") == -1)
9075             return {top:0, left:0, bottom:0, right:0};
9076             /**/
9077
9078         var offsetTop = 0;
9079         if (false && isIEStantandMode)
9080         {
9081             var scrollSize = Firebug.browser.getWindowScrollSize();
9082             offsetTop = scrollSize.height;
9083         }
9084
9085         var box = this.document.createElement("div");
9086         //box.style.cssText = "margin:0; padding:1px; border: 0; position:static; overflow:hidden; visibility: hidden;";
9087         box.style.cssText = "margin:0; padding:1px; border: 0; visibility: hidden;";
9088
9089         var clone = el.cloneNode(false);
9090         var text = this.document.createTextNode("&nbsp;");
9091         clone.appendChild(text);
9092
9093         box.appendChild(clone);
9094
9095         this.document.body.appendChild(box);
9096
9097         var marginTop = clone.offsetTop - box.offsetTop - 1;
9098         var marginBottom = box.offsetHeight - clone.offsetHeight - 2 - marginTop;
9099
9100         var marginLeft = clone.offsetLeft - box.offsetLeft - 1;
9101         var marginRight = box.offsetWidth - clone.offsetWidth - 2 - marginLeft;
9102
9103         this.document.body.removeChild(box);
9104
9105         return {top:marginTop+offsetTop, left:marginLeft, bottom:marginBottom-offsetTop, right:marginRight};
9106     },
9107
9108     getFontSizeInPixels: function(el)
9109     {
9110         var size = this.getMeasurement(el, "fontSize");
9111
9112         if (size.unit == "px") return size.value;
9113
9114         // get font size, the dirty way
9115         var computeDirtyFontSize = function(el, calibration)
9116         {
9117             var div = this.document.createElement("div");
9118             var divStyle = offscreenStyle;
9119
9120             if (calibration)
9121                 divStyle +=  " font-size:"+calibration+"px;";
9122
9123             div.style.cssText = divStyle;
9124             div.innerHTML = "A";
9125             el.appendChild(div);
9126
9127             var value = div.offsetHeight;
9128             el.removeChild(div);
9129             return value;
9130         };
9131
9132         /*
9133         var calibrationBase = 200;
9134         var calibrationValue = computeDirtyFontSize(el, calibrationBase);
9135         var rate = calibrationBase / calibrationValue;
9136         /**/
9137
9138         // the "dirty technique" fails in some environments, so we're using a static value
9139         // based in some tests.
9140         var rate = 200 / 225;
9141
9142         var value = computeDirtyFontSize(el);
9143
9144         return value * rate;
9145     },
9146
9147
9148     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9149     // Unit Funtions
9150
9151     pointsToPixels: function(name, value, returnFloat)
9152     {
9153         var axis = /Top$|Bottom$/.test(name) ? "y" : "x";
9154
9155         var result = value * pixelsPerInch[axis] / 72;
9156
9157         return returnFloat ? result : Math.round(result);
9158     },
9159
9160     emToPixels: function(el, value)
9161     {
9162         if (!el) return null;
9163
9164         var fontSize = this.getFontSizeInPixels(el);
9165
9166         return Math.round(value * fontSize);
9167     },
9168
9169     exToPixels: function(el, value)
9170     {
9171         if (!el) return null;
9172
9173         // get ex value, the dirty way
9174         var div = this.document.createElement("div");
9175         div.style.cssText = offscreenStyle + "width:"+value + "ex;";
9176
9177         el.appendChild(div);
9178         var value = div.offsetWidth;
9179         el.removeChild(div);
9180
9181         return value;
9182     },
9183
9184     percentToPixels: function(el, value)
9185     {
9186         if (!el) return null;
9187
9188         // get % value, the dirty way
9189         var div = this.document.createElement("div");
9190         div.style.cssText = offscreenStyle + "width:"+value + "%;";
9191
9192         el.appendChild(div);
9193         var value = div.offsetWidth;
9194         el.removeChild(div);
9195
9196         return value;
9197     },
9198
9199     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9200
9201     getStyle: isIE ? function(el, name)
9202     {
9203         return el.currentStyle[name] || el.style[name] || undefined;
9204     }
9205     : function(el, name)
9206     {
9207         return this.document.defaultView.getComputedStyle(el,null)[name]
9208             || el.style[name] || undefined;
9209     }
9210
9211 };
9212
9213
9214 // ************************************************************************************************
9215 }});
9216
9217 /* See license.txt for terms of usage */
9218
9219 FBL.ns( /**@scope ns-chrome*/ function() { with (FBL) {
9220 // ************************************************************************************************
9221
9222 // ************************************************************************************************
9223 // Globals
9224
9225 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9226 // Window Options
9227
9228 var WindowDefaultOptions =
9229     {
9230         type: "frame",
9231         id: "FirebugUI"
9232         //height: 350 // obsolete
9233     },
9234
9235 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9236 // Instantiated objects
9237
9238     commandLine,
9239
9240 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9241 // Interface Elements Cache
9242
9243     fbTop,
9244     fbContent,
9245     fbContentStyle,
9246     fbBottom,
9247     fbBtnInspect,
9248
9249     fbToolbar,
9250
9251     fbPanelBox1,
9252     fbPanelBox1Style,
9253     fbPanelBox2,
9254     fbPanelBox2Style,
9255     fbPanelBar2Box,
9256     fbPanelBar2BoxStyle,
9257
9258     fbHSplitter,
9259     fbVSplitter,
9260     fbVSplitterStyle,
9261
9262     fbPanel1,
9263     fbPanel1Style,
9264     fbPanel2,
9265     fbPanel2Style,
9266
9267     fbConsole,
9268     fbConsoleStyle,
9269     fbHTML,
9270
9271     fbCommandLine,
9272     fbLargeCommandLine,
9273     fbLargeCommandButtons,
9274
9275 //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9276 // Cached size values
9277
9278     topHeight,
9279     topPartialHeight,
9280
9281 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9282
9283     chromeRedrawSkipRate = isIE ? 75 : isOpera ? 80 : 75,
9284
9285 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9286
9287     lastSelectedPanelName,
9288
9289 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9290
9291     focusCommandLineState = 0,
9292     lastFocusedPanelName,
9293
9294 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9295
9296     lastHSplitterMouseMove = 0,
9297     onHSplitterMouseMoveBuffer = null,
9298     onHSplitterMouseMoveTimer = null,
9299
9300 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9301
9302     lastVSplitterMouseMove = 0;
9303
9304 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9305
9306
9307 // ************************************************************************************************
9308 // FirebugChrome
9309
9310 FBL.defaultPersistedState =
9311 {
9312     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9313     isOpen: false,
9314     height: 300,
9315     sidePanelWidth: 350,
9316
9317     selectedPanelName: "Console",
9318     selectedHTMLElementId: null,
9319
9320     htmlSelectionStack: []
9321     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9322 };
9323
9324 /**@namespace*/
9325 FBL.FirebugChrome =
9326 {
9327     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9328
9329     //isOpen: false,
9330     //height: 300,
9331     //sidePanelWidth: 350,
9332
9333     //selectedPanelName: "Console",
9334     //selectedHTMLElementId: null,
9335
9336     chromeMap: {},
9337
9338     htmlSelectionStack: [],
9339
9340     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9341
9342     create: function()
9343     {
9344         if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("FirebugChrome.create", "creating chrome window");
9345
9346         createChromeWindow();
9347     },
9348
9349     initialize: function()
9350     {
9351         if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("FirebugChrome.initialize", "initializing chrome window");
9352
9353         if (Env.chrome.type == "frame" || Env.chrome.type == "div")
9354             ChromeMini.create(Env.chrome);
9355
9356         var chrome = Firebug.chrome = new Chrome(Env.chrome);
9357         FirebugChrome.chromeMap[chrome.type] = chrome;
9358
9359         addGlobalEvent("keydown", onGlobalKeyDown);
9360
9361         if (Env.Options.enablePersistent && chrome.type == "popup")
9362         {
9363             // TODO: xxxpedro persist - revise chrome synchronization when in persistent mode
9364             var frame = FirebugChrome.chromeMap.frame;
9365             if (frame)
9366                 frame.close();
9367
9368             //chrome.reattach(frame, chrome);
9369             //TODO: xxxpedro persist synchronize?
9370             chrome.initialize();
9371         }
9372     },
9373
9374     clone: function(FBChrome)
9375     {
9376         for (var name in FBChrome)
9377         {
9378             var prop = FBChrome[name];
9379             if (FBChrome.hasOwnProperty(name) && !isFunction(prop))
9380             {
9381                 this[name] = prop;
9382             }
9383         }
9384     }
9385 };
9386
9387
9388
9389 // ************************************************************************************************
9390 // Chrome Window Creation
9391
9392 var createChromeWindow = function(options)
9393 {
9394     options = extend(WindowDefaultOptions, options || {});
9395
9396     //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9397     // Locals
9398
9399     var browserWin = Env.browser.window;
9400     var browserContext = new Context(browserWin);
9401     var prefs = Store.get("FirebugLite");
9402     var persistedState = prefs && prefs.persistedState || defaultPersistedState;
9403
9404     var chrome = {},
9405
9406         context = options.context || Env.browser,
9407
9408         type = chrome.type = Env.Options.enablePersistent ?
9409                 "popup" :
9410                 options.type,
9411
9412         isChromeFrame = type == "frame",
9413
9414         useLocalSkin = Env.useLocalSkin,
9415
9416         url = useLocalSkin ?
9417                 Env.Location.skin :
9418                 "about:blank",
9419
9420         // document.body not available in XML+XSL documents in Firefox
9421         body = context.document.getElementsByTagName("body")[0],
9422
9423         formatNode = function(node)
9424         {
9425             if (!Env.isDebugMode)
9426             {
9427                 node.firebugIgnore = true;
9428             }
9429
9430             var browserWinSize = browserContext.getWindowSize();
9431             var height = persistedState.height || 300;
9432
9433             height = Math.min(browserWinSize.height, height);
9434             height = Math.max(200, height);
9435
9436             node.style.border = "0";
9437             node.style.visibility = "hidden";
9438             node.style.zIndex = "2147483647"; // MAX z-index = 2147483647
9439             node.style.position = noFixedPosition ? "absolute" : "fixed";
9440             node.style.width = "100%"; // "102%"; IE auto margin bug
9441             node.style.left = "0";
9442             node.style.bottom = noFixedPosition ? "-1px" : "0";
9443             node.style.height = height + "px";
9444
9445             // avoid flickering during chrome rendering
9446             //if (isFirefox)
9447             //    node.style.display = "none";
9448         },
9449
9450         createChromeDiv = function()
9451         {
9452             //Firebug.Console.warn("Firebug Lite GUI is working in 'windowless mode'. It may behave slower and receive interferences from the page in which it is installed.");
9453
9454             var node = chrome.node = createGlobalElement("div"),
9455                 style = createGlobalElement("style"),
9456
9457                 css = FirebugChrome.Skin.CSS
9458                         /*
9459                         .replace(/;/g, " !important;")
9460                         .replace(/!important\s!important/g, "!important")
9461                         .replace(/display\s*:\s*(\w+)\s*!important;/g, "display:$1;")*/,
9462
9463                         // reset some styles to minimize interference from the main page's style
9464                 rules = ".fbBody *{margin:0;padding:0;font-size:11px;line-height:13px;color:inherit;}" +
9465                         // load the chrome styles
9466                         css +
9467                         // adjust some remaining styles
9468                         ".fbBody #fbHSplitter{position:absolute !important;} .fbBody #fbHTML span{line-height:14px;} .fbBody .lineNo div{line-height:inherit !important;}";
9469             /*
9470             if (isIE)
9471             {
9472                 // IE7 CSS bug (FbChrome table bigger than its parent div)
9473                 rules += ".fbBody table.fbChrome{position: static !important;}";
9474             }/**/
9475
9476             style.type = "text/css";
9477
9478             if (style.styleSheet)
9479                 style.styleSheet.cssText = rules;
9480             else
9481                 style.appendChild(context.document.createTextNode(rules));
9482
9483             document.getElementsByTagName("head")[0].appendChild(style);
9484
9485             node.className = "fbBody";
9486             node.style.overflow = "hidden";
9487             node.innerHTML = getChromeDivTemplate();
9488
9489             if (isIE)
9490             {
9491                 // IE7 CSS bug (FbChrome table bigger than its parent div)
9492                 setTimeout(function(){
9493                 node.firstChild.style.height = "1px";
9494                 node.firstChild.style.position = "static";
9495                 },0);
9496                 /**/
9497             }
9498
9499             formatNode(node);
9500
9501             body.appendChild(node);
9502
9503             chrome.window = window;
9504             chrome.document = document;
9505             onChromeLoad(chrome);
9506         };
9507
9508     //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9509
9510     try
9511     {
9512         //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9513         // create the Chrome as a "div" (windowless mode)
9514         if (type == "div")
9515         {
9516             createChromeDiv();
9517             return;
9518         }
9519
9520         //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9521         // cretate the Chrome as an "iframe"
9522         else if (isChromeFrame)
9523         {
9524             // Create the Chrome Frame
9525             var node = chrome.node = createGlobalElement("iframe");
9526             node.setAttribute("src", url);
9527             node.setAttribute("frameBorder", "0");
9528
9529             formatNode(node);
9530
9531             body.appendChild(node);
9532
9533             // must set the id after appending to the document, otherwise will cause an
9534             // strange error in IE, making the iframe load the page in which the bookmarklet
9535             // was created (like getfirebug.com), before loading the injected UI HTML,
9536             // generating an "Access Denied" error.
9537             node.id = options.id;
9538         }
9539
9540         //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9541         // create the Chrome as a "popup"
9542         else
9543         {
9544             var height = persistedState.popupHeight || 300;
9545             var browserWinSize = browserContext.getWindowSize();
9546
9547             var browserWinLeft = typeof browserWin.screenX == "number" ?
9548                     browserWin.screenX : browserWin.screenLeft;
9549
9550             var popupLeft = typeof persistedState.popupLeft == "number" ?
9551                     persistedState.popupLeft : browserWinLeft;
9552
9553             var browserWinTop = typeof browserWin.screenY == "number" ?
9554                     browserWin.screenY : browserWin.screenTop;
9555
9556             var popupTop = typeof persistedState.popupTop == "number" ?
9557                     persistedState.popupTop :
9558                     Math.max(
9559                             0,
9560                             Math.min(
9561                                     browserWinTop + browserWinSize.height - height,
9562                                     // Google Chrome bug
9563                                     screen.availHeight - height - 61
9564                                 )
9565                             );
9566
9567             var popupWidth = typeof persistedState.popupWidth == "number" ?
9568                     persistedState.popupWidth :
9569                     Math.max(
9570                             0,
9571                             Math.min(
9572                                     browserWinSize.width,
9573                                     // Opera opens popup in a new tab if it's too big!
9574                                     screen.availWidth-10
9575                                 )
9576                             );
9577
9578             var popupHeight = typeof persistedState.popupHeight == "number" ?
9579                     persistedState.popupHeight : 300;
9580
9581             var options = [
9582                     "true,top=", popupTop,
9583                     ",left=", popupLeft,
9584                     ",height=", popupHeight,
9585                     ",width=", popupWidth,
9586                     ",resizable"
9587                 ].join(""),
9588
9589                 node = chrome.node = context.window.open(
9590                     url,
9591                     "popup",
9592                     options
9593                 );
9594
9595             if (node)
9596             {
9597                 try
9598                 {
9599                     node.focus();
9600                 }
9601                 catch(E)
9602                 {
9603                     alert("Firebug Error: Firebug popup was blocked.");
9604                     return;
9605                 }
9606             }
9607             else
9608             {
9609                 alert("Firebug Error: Firebug popup was blocked.");
9610                 return;
9611             }
9612         }
9613
9614         //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9615         // Inject the interface HTML if it is not using the local skin
9616
9617         if (!useLocalSkin)
9618         {
9619             var tpl = getChromeTemplate(!isChromeFrame),
9620                 doc = isChromeFrame ? node.contentWindow.document : node.document;
9621
9622             doc.write(tpl);
9623             doc.close();
9624         }
9625
9626         //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9627         // Wait the Window to be loaded
9628
9629         var win,
9630
9631             waitDelay = useLocalSkin ? isChromeFrame ? 200 : 300 : 100,
9632
9633             waitForWindow = function()
9634             {
9635                 if ( // Frame loaded... OR
9636                      isChromeFrame && (win=node.contentWindow) &&
9637                      node.contentWindow.document.getElementById("fbCommandLine") ||
9638
9639                      // Popup loaded
9640                      !isChromeFrame && (win=node.window) && node.document &&
9641                      node.document.getElementById("fbCommandLine") )
9642                 {
9643                     chrome.window = win.window;
9644                     chrome.document = win.document;
9645
9646                     // Prevent getting the wrong chrome height in FF when opening a popup
9647                     setTimeout(function(){
9648                         onChromeLoad(chrome);
9649                     }, useLocalSkin ? 200 : 0);
9650                 }
9651                 else
9652                     setTimeout(waitForWindow, waitDelay);
9653             };
9654
9655         waitForWindow();
9656     }
9657     catch(e)
9658     {
9659         var msg = e.message || e;
9660
9661         if (/access/i.test(msg))
9662         {
9663             // Firebug Lite could not create a window for its Graphical User Interface due to
9664             // a access restriction. This happens in some pages, when loading via bookmarklet.
9665             // In such cases, the only way is to load the GUI in a "windowless mode".
9666
9667             if (isChromeFrame)
9668                 body.removeChild(node);
9669             else if(type == "popup")
9670                 node.close();
9671
9672             // Load the GUI in a "windowless mode"
9673             createChromeDiv();
9674         }
9675         else
9676         {
9677             alert("Firebug Error: Firebug GUI could not be created.");
9678         }
9679     }
9680 };
9681
9682 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9683
9684 var onChromeLoad = function onChromeLoad(chrome)
9685 {
9686     Env.chrome = chrome;
9687
9688     if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Chrome onChromeLoad", "chrome window loaded");
9689
9690     if (Env.Options.enablePersistent)
9691     {
9692         // TODO: xxxpedro persist - make better chrome synchronization when in persistent mode
9693         Env.FirebugChrome = FirebugChrome;
9694
9695         chrome.window.Firebug = chrome.window.Firebug || {};
9696         chrome.window.Firebug.SharedEnv = Env;
9697
9698         if (Env.isDevelopmentMode)
9699         {
9700             Env.browser.window.FBDev.loadChromeApplication(chrome);
9701         }
9702         else
9703         {
9704             var doc = chrome.document;
9705             var script = doc.createElement("script");
9706             script.src = Env.Location.app + "#remote,persist";
9707             doc.getElementsByTagName("head")[0].appendChild(script);
9708         }
9709     }
9710     else
9711     {
9712         if (chrome.type == "frame" || chrome.type == "div")
9713         {
9714             // initialize the chrome application
9715             setTimeout(function(){
9716                 FBL.Firebug.initialize();
9717             },0);
9718         }
9719         else if (chrome.type == "popup")
9720         {
9721             var oldChrome = FirebugChrome.chromeMap.frame;
9722
9723             var newChrome = new Chrome(chrome);
9724
9725             // TODO: xxxpedro sync detach reattach attach
9726             dispatch(newChrome.panelMap, "detach", [oldChrome, newChrome]);
9727
9728             newChrome.reattach(oldChrome, newChrome);
9729         }
9730     }
9731 };
9732
9733 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9734
9735 var getChromeDivTemplate = function()
9736 {
9737     return FirebugChrome.Skin.HTML;
9738 };
9739
9740 var getChromeTemplate = function(isPopup)
9741 {
9742     var tpl = FirebugChrome.Skin;
9743     var r = [], i = -1;
9744
9745     r[++i] = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/DTD/strict.dtd">';
9746     r[++i] = '<html><head><title>';
9747     r[++i] = Firebug.version;
9748
9749     /*
9750     r[++i] = '</title><link href="';
9751     r[++i] = Env.Location.skinDir + 'firebug.css';
9752     r[++i] = '" rel="stylesheet" type="text/css" />';
9753     /**/
9754
9755     r[++i] = '</title><style>html,body{margin:0;padding:0;overflow:hidden;}';
9756     r[++i] = tpl.CSS;
9757     r[++i] = '</style>';
9758     /**/
9759
9760     r[++i] = '</head><body class="fbBody' + (isPopup ? ' FirebugPopup' : '') + '">';
9761     r[++i] = tpl.HTML;
9762     r[++i] = '</body></html>';
9763
9764     return r.join("");
9765 };
9766
9767
9768 // ************************************************************************************************
9769 // Chrome Class
9770
9771 /**@class*/
9772 var Chrome = function Chrome(chrome)
9773 {
9774     var type = chrome.type;
9775     var Base = type == "frame" || type == "div" ? ChromeFrameBase : ChromePopupBase;
9776
9777     append(this, Base);   // inherit from base class (ChromeFrameBase or ChromePopupBase)
9778     append(this, chrome); // inherit chrome window properties
9779     append(this, new Context(chrome.window)); // inherit from Context class
9780
9781     FirebugChrome.chromeMap[type] = this;
9782     Firebug.chrome = this;
9783     Env.chrome = chrome.window;
9784
9785     this.commandLineVisible = false;
9786     this.sidePanelVisible = false;
9787
9788     this.create();
9789
9790     return this;
9791 };
9792
9793 // ************************************************************************************************
9794 // ChromeBase
9795
9796 /**
9797  * @namespace
9798  * @extends FBL.Controller
9799  * @extends FBL.PanelBar
9800  **/
9801 var ChromeBase = {};
9802 append(ChromeBase, Controller);
9803 append(ChromeBase, PanelBar);
9804 append(ChromeBase,
9805 /**@extend ns-chrome-ChromeBase*/
9806 {
9807     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9808     // inherited properties
9809
9810     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9811     // inherited from createChrome function
9812
9813     node: null,
9814     type: null,
9815
9816     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9817     // inherited from Context.prototype
9818
9819     document: null,
9820     window: null,
9821
9822     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9823     // value properties
9824
9825     sidePanelVisible: false,
9826     commandLineVisible: false,
9827     largeCommandLineVisible: false,
9828
9829     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9830     // object properties
9831
9832     inspectButton: null,
9833
9834     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9835
9836     create: function()
9837     {
9838         PanelBar.create.call(this);
9839
9840         if (Firebug.Inspector)
9841             this.inspectButton = new Button({
9842                 type: "toggle",
9843                 element: $("fbChrome_btInspect"),
9844                 owner: Firebug.Inspector,
9845
9846                 onPress: Firebug.Inspector.startInspecting,
9847                 onUnpress: Firebug.Inspector.stopInspecting
9848             });
9849     },
9850
9851     destroy: function()
9852     {
9853         if(Firebug.Inspector)
9854             this.inspectButton.destroy();
9855
9856         PanelBar.destroy.call(this);
9857
9858         this.shutdown();
9859     },
9860
9861     testMenu: function()
9862     {
9863         var firebugMenu = new Menu(
9864         {
9865             id: "fbFirebugMenu",
9866
9867             items:
9868             [
9869                 {
9870                     label: "Open Firebug",
9871                     type: "shortcut",
9872                     key: isFirefox ? "Shift+F12" : "F12",
9873                     checked: true,
9874                     command: "toggleChrome"
9875                 },
9876                 {
9877                     label: "Open Firebug in New Window",
9878                     type: "shortcut",
9879                     key: isFirefox ? "Ctrl+Shift+F12" : "Ctrl+F12",
9880                     command: "openPopup"
9881                 },
9882                 {
9883                     label: "Inspect Element",
9884                     type: "shortcut",
9885                     key: "Ctrl+Shift+C",
9886                     command: "toggleInspect"
9887                 },
9888                 {
9889                     label: "Command Line",
9890                     type: "shortcut",
9891                     key: "Ctrl+Shift+L",
9892                     command: "focusCommandLine"
9893                 },
9894                 "-",
9895                 {
9896                     label: "Options",
9897                     type: "group",
9898                     child: "fbFirebugOptionsMenu"
9899                 },
9900                 "-",
9901                 {
9902                     label: "Firebug Lite Website...",
9903                     command: "visitWebsite"
9904                 },
9905                 {
9906                     label: "Discussion Group...",
9907                     command: "visitDiscussionGroup"
9908                 },
9909                 {
9910                     label: "Issue Tracker...",
9911                     command: "visitIssueTracker"
9912                 }
9913             ],
9914
9915             onHide: function()
9916             {
9917                 iconButton.restore();
9918             },
9919
9920             toggleChrome: function()
9921             {
9922                 Firebug.chrome.toggle();
9923             },
9924
9925             openPopup: function()
9926             {
9927                 Firebug.chrome.toggle(true, true);
9928             },
9929
9930             toggleInspect: function()
9931             {
9932                 Firebug.Inspector.toggleInspect();
9933             },
9934
9935             focusCommandLine: function()
9936             {
9937                 Firebug.chrome.focusCommandLine();
9938             },
9939
9940             visitWebsite: function()
9941             {
9942                 this.visit("http://getfirebug.com/lite.html");
9943             },
9944
9945             visitDiscussionGroup: function()
9946             {
9947                 this.visit("http://groups.google.com/group/firebug");
9948             },
9949
9950             visitIssueTracker: function()
9951             {
9952                 this.visit("http://code.google.com/p/fbug/issues/list");
9953             },
9954
9955             visit: function(url)
9956             {
9957                 window.open(url);
9958             }
9959
9960         });
9961
9962         /**@private*/
9963         var firebugOptionsMenu =
9964         {
9965             id: "fbFirebugOptionsMenu",
9966
9967             getItems: function()
9968             {
9969                 var cookiesDisabled = !Firebug.saveCookies;
9970
9971                 return [
9972                     {
9973                         label: "Start Opened",
9974                         type: "checkbox",
9975                         value: "startOpened",
9976                         checked: Firebug.startOpened,
9977                         disabled: cookiesDisabled
9978                     },
9979                     {
9980                         label: "Start in New Window",
9981                         type: "checkbox",
9982                         value: "startInNewWindow",
9983                         checked: Firebug.startInNewWindow,
9984                         disabled: cookiesDisabled
9985                     },
9986                     {
9987                         label: "Show Icon When Hidden",
9988                         type: "checkbox",
9989                         value: "showIconWhenHidden",
9990                         checked: Firebug.showIconWhenHidden,
9991                         disabled: cookiesDisabled
9992                     },
9993                     {
9994                         label: "Override Console Object",
9995                         type: "checkbox",
9996                         value: "overrideConsole",
9997                         checked: Firebug.overrideConsole,
9998                         disabled: cookiesDisabled
9999                     },
10000                     {
10001                         label: "Ignore Firebug Elements",
10002                         type: "checkbox",
10003                         value: "ignoreFirebugElements",
10004                         checked: Firebug.ignoreFirebugElements,
10005                         disabled: cookiesDisabled
10006                     },
10007                     {
10008                         label: "Disable When Firebug Active",
10009                         type: "checkbox",
10010                         value: "disableWhenFirebugActive",
10011                         checked: Firebug.disableWhenFirebugActive,
10012                         disabled: cookiesDisabled
10013                     },
10014                     {
10015                         label: "Disable XHR Listener",
10016                         type: "checkbox",
10017                         value: "disableXHRListener",
10018                         checked: Firebug.disableXHRListener,
10019                         disabled: cookiesDisabled
10020                     },
10021                     {
10022                         label: "Disable Resource Fetching",
10023                         type: "checkbox",
10024                         value: "disableResourceFetching",
10025                         checked: Firebug.disableResourceFetching,
10026                         disabled: cookiesDisabled
10027                     },
10028                     {
10029                         label: "Enable Trace Mode",
10030                         type: "checkbox",
10031                         value: "enableTrace",
10032                         checked: Firebug.enableTrace,
10033                         disabled: cookiesDisabled
10034                     },
10035                     {
10036                         label: "Enable Persistent Mode (experimental)",
10037                         type: "checkbox",
10038                         value: "enablePersistent",
10039                         checked: Firebug.enablePersistent,
10040                         disabled: cookiesDisabled
10041                     },
10042                     "-",
10043                     {
10044                         label: "Reset All Firebug Options",
10045                         command: "restorePrefs",
10046                         disabled: cookiesDisabled
10047                     }
10048                 ];
10049             },
10050
10051             onCheck: function(target, value, checked)
10052             {
10053                 Firebug.setPref(value, checked);
10054             },
10055
10056             restorePrefs: function(target)
10057             {
10058                 Firebug.erasePrefs();
10059
10060                 if (target)
10061                     this.updateMenu(target);
10062             },
10063
10064             updateMenu: function(target)
10065             {
10066                 var options = getElementsByClass(target.parentNode, "fbMenuOption");
10067
10068                 var firstOption = options[0];
10069                 var enabled = Firebug.saveCookies;
10070                 if (enabled)
10071                     Menu.check(firstOption);
10072                 else
10073                     Menu.uncheck(firstOption);
10074
10075                 if (enabled)
10076                     Menu.check(options[0]);
10077                 else
10078                     Menu.uncheck(options[0]);
10079
10080                 for (var i = 1, length = options.length; i < length; i++)
10081                 {
10082                     var option = options[i];
10083
10084                     var value = option.getAttribute("value");
10085                     var pref = Firebug[value];
10086
10087                     if (pref)
10088                         Menu.check(option);
10089                     else
10090                         Menu.uncheck(option);
10091
10092                     if (enabled)
10093                         Menu.enable(option);
10094                     else
10095                         Menu.disable(option);
10096                 }
10097             }
10098         };
10099
10100         Menu.register(firebugOptionsMenu);
10101
10102         var menu = firebugMenu;
10103
10104         var testMenuClick = function(event)
10105         {
10106             //console.log("testMenuClick");
10107             cancelEvent(event, true);
10108
10109             var target = event.target || event.srcElement;
10110
10111             if (menu.isVisible)
10112                 menu.hide();
10113             else
10114             {
10115                 var offsetLeft = isIE6 ? 1 : -4,  // IE6 problem with fixed position
10116
10117                     chrome = Firebug.chrome,
10118
10119                     box = chrome.getElementBox(target),
10120
10121                     offset = chrome.type == "div" ?
10122                             chrome.getElementPosition(chrome.node) :
10123                             {top: 0, left: 0};
10124
10125                 menu.show(
10126                             box.left + offsetLeft - offset.left,
10127                             box.top + box.height -5 - offset.top
10128                         );
10129             }
10130
10131             return false;
10132         };
10133
10134         var iconButton = new IconButton({
10135             type: "toggle",
10136             element: $("fbFirebugButton"),
10137
10138             onClick: testMenuClick
10139         });
10140
10141         iconButton.initialize();
10142
10143         //addEvent($("fbToolbarIcon"), "click", testMenuClick);
10144     },
10145
10146     initialize: function()
10147     {
10148         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10149         if (Env.bookmarkletOutdated)
10150             Firebug.Console.logFormatted([
10151                   "A new bookmarklet version is available. " +
10152                   "Please visit http://getfirebug.com/firebuglite#Install and update it."
10153                 ], Firebug.context, "warn");
10154
10155         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10156         if (Firebug.Console)
10157             Firebug.Console.flush();
10158
10159         if (Firebug.Trace)
10160             FBTrace.flush(Firebug.Trace);
10161
10162         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10163         if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.chrome.initialize", "initializing chrome application");
10164
10165         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10166         // initialize inherited classes
10167         Controller.initialize.call(this);
10168         PanelBar.initialize.call(this);
10169
10170         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10171         // create the interface elements cache
10172
10173         fbTop = $("fbTop");
10174         fbContent = $("fbContent");
10175         fbContentStyle = fbContent.style;
10176         fbBottom = $("fbBottom");
10177         fbBtnInspect = $("fbBtnInspect");
10178
10179         fbToolbar = $("fbToolbar");
10180
10181         fbPanelBox1 = $("fbPanelBox1");
10182         fbPanelBox1Style = fbPanelBox1.style;
10183         fbPanelBox2 = $("fbPanelBox2");
10184         fbPanelBox2Style = fbPanelBox2.style;
10185         fbPanelBar2Box = $("fbPanelBar2Box");
10186         fbPanelBar2BoxStyle = fbPanelBar2Box.style;
10187
10188         fbHSplitter = $("fbHSplitter");
10189         fbVSplitter = $("fbVSplitter");
10190         fbVSplitterStyle = fbVSplitter.style;
10191
10192         fbPanel1 = $("fbPanel1");
10193         fbPanel1Style = fbPanel1.style;
10194         fbPanel2 = $("fbPanel2");
10195         fbPanel2Style = fbPanel2.style;
10196
10197         fbConsole = $("fbConsole");
10198         fbConsoleStyle = fbConsole.style;
10199         fbHTML = $("fbHTML");
10200
10201         fbCommandLine = $("fbCommandLine");
10202         fbLargeCommandLine = $("fbLargeCommandLine");
10203         fbLargeCommandButtons = $("fbLargeCommandButtons");
10204
10205         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10206         // static values cache
10207         topHeight = fbTop.offsetHeight;
10208         topPartialHeight = fbToolbar.offsetHeight;
10209
10210         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10211
10212         disableTextSelection($("fbToolbar"));
10213         disableTextSelection($("fbPanelBarBox"));
10214         disableTextSelection($("fbPanelBar1"));
10215         disableTextSelection($("fbPanelBar2"));
10216
10217         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10218         // Add the "javascript:void(0)" href attributes used to make the hover effect in IE6
10219         if (isIE6 && Firebug.Selector)
10220         {
10221             // TODO: xxxpedro change to getElementsByClass
10222             var as = $$(".fbHover");
10223             for (var i=0, a; a=as[i]; i++)
10224             {
10225                 a.setAttribute("href", "javascript:void(0)");
10226             }
10227         }
10228
10229         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10230         // initialize all panels
10231         /*
10232         var panelMap = Firebug.panelTypes;
10233         for (var i=0, p; p=panelMap[i]; i++)
10234         {
10235             if (!p.parentPanel)
10236             {
10237                 this.addPanel(p.prototype.name);
10238             }
10239         }
10240         /**/
10241
10242         // ************************************************************************************************
10243         // ************************************************************************************************
10244         // ************************************************************************************************
10245         // ************************************************************************************************
10246
10247         if(Firebug.Inspector)
10248             this.inspectButton.initialize();
10249
10250         // ************************************************************************************************
10251         // ************************************************************************************************
10252         // ************************************************************************************************
10253         // ************************************************************************************************
10254
10255         this.addController(
10256             [$("fbLargeCommandLineIcon"), "click", this.showLargeCommandLine]
10257         );
10258
10259         // ************************************************************************************************
10260
10261         // Select the first registered panel
10262         // TODO: BUG IE7
10263         var self = this;
10264         setTimeout(function(){
10265             self.selectPanel(Firebug.context.persistedState.selectedPanelName);
10266
10267             if (Firebug.context.persistedState.selectedPanelName == "Console" && Firebug.CommandLine)
10268                 Firebug.chrome.focusCommandLine();
10269         },0);
10270
10271         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10272         //this.draw();
10273
10274
10275
10276
10277
10278
10279
10280
10281         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10282         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10283         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10284         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10285         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10286         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10287
10288         var onPanelMouseDown = function onPanelMouseDown(event)
10289         {
10290             //console.log("onPanelMouseDown", event.target || event.srcElement, event);
10291
10292             var target = event.target || event.srcElement;
10293
10294             if (FBL.isLeftClick(event))
10295             {
10296                 var editable = FBL.getAncestorByClass(target, "editable");
10297
10298                 // if an editable element has been clicked then start editing
10299                 if (editable)
10300                 {
10301                     Firebug.Editor.startEditing(editable);
10302                     FBL.cancelEvent(event);
10303                 }
10304                 // if any other element has been clicked then stop editing
10305                 else
10306                 {
10307                     if (!hasClass(target, "textEditorInner"))
10308                         Firebug.Editor.stopEditing();
10309                 }
10310             }
10311             else if (FBL.isMiddleClick(event) && Firebug.getRepNode(target))
10312             {
10313                 // Prevent auto-scroll when middle-clicking a rep object
10314                 FBL.cancelEvent(event);
10315             }
10316         };
10317
10318         Firebug.getElementPanel = function(element)
10319         {
10320             var panelNode = getAncestorByClass(element, "fbPanel");
10321             var id = panelNode.id.substr(2);
10322
10323             var panel = Firebug.chrome.panelMap[id];
10324
10325             if (!panel)
10326             {
10327                 if (Firebug.chrome.selectedPanel.sidePanelBar)
10328                     panel = Firebug.chrome.selectedPanel.sidePanelBar.panelMap[id];
10329             }
10330
10331             return panel;
10332         };
10333
10334
10335
10336         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10337
10338         // TODO: xxxpedro port to Firebug
10339
10340         // Improved window key code event listener. Only one "keydown" event will be attached
10341         // to the window, and the onKeyCodeListen() function will delegate which listeners
10342         // should be called according to the event.keyCode fired.
10343         var onKeyCodeListenersMap = [];
10344         var onKeyCodeListen = function(event)
10345         {
10346             for (var keyCode in onKeyCodeListenersMap)
10347             {
10348                 var listeners = onKeyCodeListenersMap[keyCode];
10349
10350                 for (var i = 0, listener; listener = listeners[i]; i++)
10351                 {
10352                     var filter = listener.filter || FBL.noKeyModifiers;
10353
10354                     if (event.keyCode == keyCode && (!filter || filter(event)))
10355                     {
10356                         listener.listener();
10357                         FBL.cancelEvent(event, true);
10358                         return false;
10359                     }
10360                 }
10361             }
10362         };
10363
10364         addEvent(Firebug.chrome.document, "keydown", onKeyCodeListen);
10365
10366         /**
10367          * @name keyCodeListen
10368          * @memberOf FBL.FirebugChrome
10369          */
10370         Firebug.chrome.keyCodeListen = function(key, filter, listener, capture)
10371         {
10372             var keyCode = KeyEvent["DOM_VK_"+key];
10373
10374             if (!onKeyCodeListenersMap[keyCode])
10375                 onKeyCodeListenersMap[keyCode] = [];
10376
10377             onKeyCodeListenersMap[keyCode].push({
10378                 filter: filter,
10379                 listener: listener
10380             });
10381
10382             return keyCode;
10383         };
10384
10385         /**
10386          * @name keyIgnore
10387          * @memberOf FBL.FirebugChrome
10388          */
10389         Firebug.chrome.keyIgnore = function(keyCode)
10390         {
10391             onKeyCodeListenersMap[keyCode] = null;
10392             delete onKeyCodeListenersMap[keyCode];
10393         };
10394
10395         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10396
10397         /**/
10398         // move to shutdown
10399         //removeEvent(Firebug.chrome.document, "keydown", listener[0]);
10400
10401
10402         /*
10403         Firebug.chrome.keyCodeListen = function(key, filter, listener, capture)
10404         {
10405             if (!filter)
10406                 filter = FBL.noKeyModifiers;
10407
10408             var keyCode = KeyEvent["DOM_VK_"+key];
10409
10410             var fn = function fn(event)
10411             {
10412                 if (event.keyCode == keyCode && (!filter || filter(event)))
10413                 {
10414                     listener();
10415                     FBL.cancelEvent(event, true);
10416                     return false;
10417                 }
10418             }
10419
10420             addEvent(Firebug.chrome.document, "keydown", fn);
10421
10422             return [fn, capture];
10423         };
10424
10425         Firebug.chrome.keyIgnore = function(listener)
10426         {
10427             removeEvent(Firebug.chrome.document, "keydown", listener[0]);
10428         };
10429         /**/
10430
10431
10432         this.addController(
10433                 [fbPanel1, "mousedown", onPanelMouseDown],
10434                 [fbPanel2, "mousedown", onPanelMouseDown]
10435              );
10436 /**/
10437         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10438         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10439         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10440         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10441         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10442         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10443
10444
10445         // menus can be used without domplate
10446         if (FBL.domplate)
10447             this.testMenu();
10448         /**/
10449
10450         //test XHR
10451         /*
10452         setTimeout(function(){
10453
10454         FBL.Ajax.request({url: "../content/firebug/boot.js"});
10455         FBL.Ajax.request({url: "../content/firebug/boot.js.invalid"});
10456
10457         },1000);
10458         /**/
10459     },
10460
10461     shutdown: function()
10462     {
10463         // ************************************************************************************************
10464         // ************************************************************************************************
10465         // ************************************************************************************************
10466         // ************************************************************************************************
10467
10468         if(Firebug.Inspector)
10469             this.inspectButton.shutdown();
10470
10471         // ************************************************************************************************
10472         // ************************************************************************************************
10473         // ************************************************************************************************
10474         // ************************************************************************************************
10475
10476         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10477
10478         // remove disableTextSelection event handlers
10479         restoreTextSelection($("fbToolbar"));
10480         restoreTextSelection($("fbPanelBarBox"));
10481         restoreTextSelection($("fbPanelBar1"));
10482         restoreTextSelection($("fbPanelBar2"));
10483
10484         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10485         // shutdown inherited classes
10486         Controller.shutdown.call(this);
10487         PanelBar.shutdown.call(this);
10488
10489         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10490         // Remove the interface elements cache (this must happen after calling
10491         // the shutdown method of all dependent components to avoid errors)
10492
10493         fbTop = null;
10494         fbContent = null;
10495         fbContentStyle = null;
10496         fbBottom = null;
10497         fbBtnInspect = null;
10498
10499         fbToolbar = null;
10500
10501         fbPanelBox1 = null;
10502         fbPanelBox1Style = null;
10503         fbPanelBox2 = null;
10504         fbPanelBox2Style = null;
10505         fbPanelBar2Box = null;
10506         fbPanelBar2BoxStyle = null;
10507
10508         fbHSplitter = null;
10509         fbVSplitter = null;
10510         fbVSplitterStyle = null;
10511
10512         fbPanel1 = null;
10513         fbPanel1Style = null;
10514         fbPanel2 = null;
10515
10516         fbConsole = null;
10517         fbConsoleStyle = null;
10518         fbHTML = null;
10519
10520         fbCommandLine = null;
10521         fbLargeCommandLine = null;
10522         fbLargeCommandButtons = null;
10523
10524         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10525         // static values cache
10526
10527         topHeight = null;
10528         topPartialHeight = null;
10529     },
10530
10531     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10532
10533     toggle: function(forceOpen, popup)
10534     {
10535         if(popup)
10536         {
10537             this.detach();
10538         }
10539         else
10540         {
10541             if (isOpera && Firebug.chrome.type == "popup" && Firebug.chrome.node.closed)
10542             {
10543                 var frame = FirebugChrome.chromeMap.frame;
10544                 frame.reattach();
10545
10546                 FirebugChrome.chromeMap.popup = null;
10547
10548                 frame.open();
10549
10550                 return;
10551             }
10552
10553             // If the context is a popup, ignores the toggle process
10554             if (Firebug.chrome.type == "popup") return;
10555
10556             var shouldOpen = forceOpen || !Firebug.context.persistedState.isOpen;
10557
10558             if(shouldOpen)
10559                this.open();
10560             else
10561                this.close();
10562         }
10563     },
10564
10565     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10566
10567     detach: function()
10568     {
10569         if(!FirebugChrome.chromeMap.popup)
10570         {
10571             this.close();
10572             createChromeWindow({type: "popup"});
10573         }
10574     },
10575
10576     reattach: function(oldChrome, newChrome)
10577     {
10578         Firebug.browser.window.Firebug = Firebug;
10579
10580         // chrome synchronization
10581         var newPanelMap = newChrome.panelMap;
10582         var oldPanelMap = oldChrome.panelMap;
10583
10584         var panel;
10585         for(var name in newPanelMap)
10586         {
10587             // TODO: xxxpedro innerHTML
10588             panel = newPanelMap[name];
10589             if (panel.options.innerHTMLSync)
10590                 panel.panelNode.innerHTML = oldPanelMap[name].panelNode.innerHTML;
10591         }
10592
10593         Firebug.chrome = newChrome;
10594
10595         // TODO: xxxpedro sync detach reattach attach
10596         //dispatch(Firebug.chrome.panelMap, "detach", [oldChrome, newChrome]);
10597
10598         if (newChrome.type == "popup")
10599         {
10600             newChrome.initialize();
10601             //dispatch(Firebug.modules, "initialize", []);
10602         }
10603         else
10604         {
10605             // TODO: xxxpedro only needed in persistent
10606             // should use FirebugChrome.clone, but popup FBChrome
10607             // isn't acessible
10608             Firebug.context.persistedState.selectedPanelName = oldChrome.selectedPanel.name;
10609         }
10610
10611         dispatch(newPanelMap, "reattach", [oldChrome, newChrome]);
10612     },
10613
10614     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10615
10616     draw: function()
10617     {
10618         var size = this.getSize();
10619
10620         // Height related values
10621         var commandLineHeight = Firebug.chrome.commandLineVisible ? fbCommandLine.offsetHeight : 0,
10622
10623             y = Math.max(size.height /* chrome height */, topHeight),
10624
10625             heightValue = Math.max(y - topHeight - commandLineHeight /* fixed height */, 0),
10626
10627             height = heightValue + "px",
10628
10629             // Width related values
10630             sideWidthValue = Firebug.chrome.sidePanelVisible ? Firebug.context.persistedState.sidePanelWidth : 0,
10631
10632             width = Math.max(size.width /* chrome width */ - sideWidthValue, 0) + "px";
10633
10634         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10635         // Height related rendering
10636         fbPanelBox1Style.height = height;
10637         fbPanel1Style.height = height;
10638
10639         if (isIE || isOpera)
10640         {
10641             // Fix IE and Opera problems with auto resizing the verticall splitter
10642             fbVSplitterStyle.height = Math.max(y - topPartialHeight - commandLineHeight, 0) + "px";
10643         }
10644         //xxxpedro FF2 only?
10645         /*
10646         else if (isFirefox)
10647         {
10648             // Fix Firefox problem with table rows with 100% height (fit height)
10649             fbContentStyle.maxHeight = Math.max(y - fixedHeight, 0)+ "px";
10650         }/**/
10651
10652         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10653         // Width related rendering
10654         fbPanelBox1Style.width = width;
10655         fbPanel1Style.width = width;
10656
10657         // SidePanel rendering
10658         if (Firebug.chrome.sidePanelVisible)
10659         {
10660             sideWidthValue = Math.max(sideWidthValue - 6, 0);
10661
10662             var sideWidth = sideWidthValue + "px";
10663
10664             fbPanelBox2Style.width = sideWidth;
10665
10666             fbVSplitterStyle.right = sideWidth;
10667
10668             if (Firebug.chrome.largeCommandLineVisible)
10669             {
10670                 fbLargeCommandLine = $("fbLargeCommandLine");
10671
10672                 fbLargeCommandLine.style.height = heightValue - 4 + "px";
10673                 fbLargeCommandLine.style.width = sideWidthValue - 2 + "px";
10674
10675                 fbLargeCommandButtons = $("fbLargeCommandButtons");
10676                 fbLargeCommandButtons.style.width = sideWidth;
10677             }
10678             else
10679             {
10680                 fbPanel2Style.height = height;
10681                 fbPanel2Style.width = sideWidth;
10682
10683                 fbPanelBar2BoxStyle.width = sideWidth;
10684             }
10685         }
10686     },
10687
10688     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10689
10690     getSize: function()
10691     {
10692         return this.type == "div" ?
10693             {
10694                 height: this.node.offsetHeight,
10695                 width: this.node.offsetWidth
10696             }
10697             :
10698             this.getWindowSize();
10699     },
10700
10701     resize: function()
10702     {
10703         var self = this;
10704
10705         // avoid partial resize when maximizing window
10706         setTimeout(function(){
10707             self.draw();
10708
10709             if (noFixedPosition && (self.type == "frame" || self.type == "div"))
10710                 self.fixIEPosition();
10711         }, 0);
10712     },
10713
10714     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10715
10716     layout: function(panel)
10717     {
10718         if (FBTrace.DBG_CHROME) FBTrace.sysout("Chrome.layout", "");
10719
10720         var options = panel.options;
10721
10722         changeCommandLineVisibility(options.hasCommandLine);
10723         changeSidePanelVisibility(panel.hasSidePanel);
10724
10725         Firebug.chrome.draw();
10726     },
10727
10728     showLargeCommandLine: function(hideToggleIcon)
10729     {
10730         var chrome = Firebug.chrome;
10731
10732         if (!chrome.largeCommandLineVisible)
10733         {
10734             chrome.largeCommandLineVisible = true;
10735
10736             if (chrome.selectedPanel.options.hasCommandLine)
10737             {
10738                 if (Firebug.CommandLine)
10739                     Firebug.CommandLine.blur();
10740
10741                 changeCommandLineVisibility(false);
10742             }
10743
10744             changeSidePanelVisibility(true);
10745
10746             fbLargeCommandLine.style.display = "block";
10747             fbLargeCommandButtons.style.display = "block";
10748
10749             fbPanel2Style.display = "none";
10750             fbPanelBar2BoxStyle.display = "none";
10751
10752             chrome.draw();
10753
10754             fbLargeCommandLine.focus();
10755
10756             if (Firebug.CommandLine)
10757                 Firebug.CommandLine.setMultiLine(true);
10758         }
10759     },
10760
10761     hideLargeCommandLine: function()
10762     {
10763         if (Firebug.chrome.largeCommandLineVisible)
10764         {
10765             Firebug.chrome.largeCommandLineVisible = false;
10766
10767             if (Firebug.CommandLine)
10768                 Firebug.CommandLine.setMultiLine(false);
10769
10770             fbLargeCommandLine.blur();
10771
10772             fbPanel2Style.display = "block";
10773             fbPanelBar2BoxStyle.display = "block";
10774
10775             fbLargeCommandLine.style.display = "none";
10776             fbLargeCommandButtons.style.display = "none";
10777
10778             changeSidePanelVisibility(false);
10779
10780             if (Firebug.chrome.selectedPanel.options.hasCommandLine)
10781                 changeCommandLineVisibility(true);
10782
10783             Firebug.chrome.draw();
10784
10785         }
10786     },
10787
10788     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10789
10790     focusCommandLine: function()
10791     {
10792         var selectedPanelName = this.selectedPanel.name, panelToSelect;
10793
10794         if (focusCommandLineState == 0 || selectedPanelName != "Console")
10795         {
10796             focusCommandLineState = 0;
10797             lastFocusedPanelName = selectedPanelName;
10798
10799             panelToSelect = "Console";
10800         }
10801         if (focusCommandLineState == 1)
10802         {
10803             panelToSelect = lastFocusedPanelName;
10804         }
10805
10806         this.selectPanel(panelToSelect);
10807
10808         try
10809         {
10810             if (Firebug.CommandLine)
10811             {
10812                 if (panelToSelect == "Console")
10813                     Firebug.CommandLine.focus();
10814                 else
10815                     Firebug.CommandLine.blur();
10816             }
10817         }
10818         catch(e)
10819         {
10820             //TODO: xxxpedro trace error
10821         }
10822
10823         focusCommandLineState = ++focusCommandLineState % 2;
10824     }
10825
10826 });
10827
10828 // ************************************************************************************************
10829 // ChromeFrameBase
10830
10831 /**
10832  * @namespace
10833  * @extends ns-chrome-ChromeBase
10834  */
10835 var ChromeFrameBase = extend(ChromeBase,
10836 /**@extend ns-chrome-ChromeFrameBase*/
10837 {
10838     create: function()
10839     {
10840         ChromeBase.create.call(this);
10841
10842         // restore display for the anti-flicker trick
10843         if (isFirefox)
10844             this.node.style.display = "block";
10845
10846         if (Env.Options.startInNewWindow)
10847         {
10848             this.close();
10849             this.toggle(true, true);
10850             return;
10851         }
10852
10853         if (Env.Options.startOpened)
10854             this.open();
10855         else
10856             this.close();
10857     },
10858
10859     destroy: function()
10860     {
10861         var size = Firebug.chrome.getWindowSize();
10862
10863         Firebug.context.persistedState.height = size.height;
10864
10865         if (Firebug.saveCookies)
10866             Firebug.savePrefs();
10867
10868         removeGlobalEvent("keydown", onGlobalKeyDown);
10869
10870         ChromeBase.destroy.call(this);
10871
10872         this.document = null;
10873         delete this.document;
10874
10875         this.window = null;
10876         delete this.window;
10877
10878         this.node.parentNode.removeChild(this.node);
10879         this.node = null;
10880         delete this.node;
10881     },
10882
10883     initialize: function()
10884     {
10885         //FBTrace.sysout("Frame", "initialize();")
10886         ChromeBase.initialize.call(this);
10887
10888         this.addController(
10889             [Firebug.browser.window, "resize", this.resize],
10890             [$("fbWindow_btClose"), "click", this.close],
10891             [$("fbWindow_btDetach"), "click", this.detach],
10892             [$("fbWindow_btDeactivate"), "click", this.deactivate]
10893         );
10894
10895         if (!Env.Options.enablePersistent)
10896             this.addController([Firebug.browser.window, "unload", Firebug.shutdown]);
10897
10898         if (noFixedPosition)
10899         {
10900             this.addController(
10901                 [Firebug.browser.window, "scroll", this.fixIEPosition]
10902             );
10903         }
10904
10905         fbVSplitter.onmousedown = onVSplitterMouseDown;
10906         fbHSplitter.onmousedown = onHSplitterMouseDown;
10907
10908         this.isInitialized = true;
10909     },
10910
10911     shutdown: function()
10912     {
10913         fbVSplitter.onmousedown = null;
10914         fbHSplitter.onmousedown = null;
10915
10916         ChromeBase.shutdown.apply(this);
10917
10918         this.isInitialized = false;
10919     },
10920
10921     reattach: function()
10922     {
10923         var frame = FirebugChrome.chromeMap.frame;
10924
10925         ChromeBase.reattach(FirebugChrome.chromeMap.popup, this);
10926     },
10927
10928     open: function()
10929     {
10930         if (!Firebug.context.persistedState.isOpen)
10931         {
10932             Firebug.context.persistedState.isOpen = true;
10933
10934             if (Env.isChromeExtension)
10935                 localStorage.setItem("Firebug", "1,1");
10936
10937             var node = this.node;
10938
10939             node.style.visibility = "hidden"; // Avoid flickering
10940
10941             if (Firebug.showIconWhenHidden)
10942             {
10943                 if (ChromeMini.isInitialized)
10944                 {
10945                     ChromeMini.shutdown();
10946                 }
10947
10948             }
10949             else
10950                 node.style.display = "block";
10951
10952             var main = $("fbChrome");
10953
10954             // IE6 throws an error when setting this property! why?
10955             //main.style.display = "table";
10956             main.style.display = "";
10957
10958             var self = this;
10959                 /// TODO: xxxpedro FOUC
10960                 node.style.visibility = "visible";
10961             setTimeout(function(){
10962                 ///node.style.visibility = "visible";
10963
10964                 //dispatch(Firebug.modules, "initialize", []);
10965                 self.initialize();
10966
10967                 if (noFixedPosition)
10968                     self.fixIEPosition();
10969
10970                 self.draw();
10971
10972             }, 10);
10973         }
10974     },
10975
10976     close: function()
10977     {
10978         if (Firebug.context.persistedState.isOpen)
10979         {
10980             if (this.isInitialized)
10981             {
10982                 //dispatch(Firebug.modules, "shutdown", []);
10983                 this.shutdown();
10984             }
10985
10986             Firebug.context.persistedState.isOpen = false;
10987
10988             if (Env.isChromeExtension)
10989                 localStorage.setItem("Firebug", "1,0");
10990
10991             var node = this.node;
10992
10993             if (Firebug.showIconWhenHidden)
10994             {
10995                 node.style.visibility = "hidden"; // Avoid flickering
10996
10997                 // TODO: xxxpedro - persist IE fixed?
10998                 var main = $("fbChrome", FirebugChrome.chromeMap.frame.document);
10999                 main.style.display = "none";
11000
11001                 ChromeMini.initialize();
11002
11003                 node.style.visibility = "visible";
11004             }
11005             else
11006                 node.style.display = "none";
11007         }
11008     },
11009
11010     deactivate: function()
11011     {
11012         // if it is running as a Chrome extension, dispatch a message to the extension signaling
11013         // that Firebug should be deactivated for the current tab
11014         if (Env.isChromeExtension)
11015         {
11016             localStorage.removeItem("Firebug");
11017             Firebug.GoogleChrome.dispatch("FB_deactivate");
11018
11019             // xxxpedro problem here regarding Chrome extension. We can't deactivate the whole
11020             // app, otherwise it won't be able to be reactivated without reloading the page.
11021             // but we need to stop listening global keys, otherwise the key activation won't work.
11022             Firebug.chrome.close();
11023         }
11024         else
11025         {
11026             Firebug.shutdown();
11027         }
11028     },
11029
11030     fixIEPosition: function()
11031     {
11032         // fix IE problem with offset when not in fullscreen mode
11033         var doc = this.document;
11034         var offset = isIE ? doc.body.clientTop || doc.documentElement.clientTop: 0;
11035
11036         var size = Firebug.browser.getWindowSize();
11037         var scroll = Firebug.browser.getWindowScrollPosition();
11038         var maxHeight = size.height;
11039         var height = this.node.offsetHeight;
11040
11041         var bodyStyle = doc.body.currentStyle;
11042
11043         this.node.style.top = maxHeight - height + scroll.top + "px";
11044
11045         if ((this.type == "frame" || this.type == "div") &&
11046             (bodyStyle.marginLeft || bodyStyle.marginRight))
11047         {
11048             this.node.style.width = size.width + "px";
11049         }
11050
11051         if (fbVSplitterStyle)
11052             fbVSplitterStyle.right = Firebug.context.persistedState.sidePanelWidth + "px";
11053
11054         this.draw();
11055     }
11056
11057 });
11058
11059
11060 // ************************************************************************************************
11061 // ChromeMini
11062
11063 /**
11064  * @namespace
11065  * @extends FBL.Controller
11066  */
11067 var ChromeMini = extend(Controller,
11068 /**@extend ns-chrome-ChromeMini*/
11069 {
11070     create: function(chrome)
11071     {
11072         append(this, chrome);
11073         this.type = "mini";
11074     },
11075
11076     initialize: function()
11077     {
11078         Controller.initialize.apply(this);
11079
11080         var doc = FirebugChrome.chromeMap.frame.document;
11081
11082         var mini = $("fbMiniChrome", doc);
11083         mini.style.display = "block";
11084
11085         var miniIcon = $("fbMiniIcon", doc);
11086         var width = miniIcon.offsetWidth + 10;
11087         miniIcon.title = "Open " + Firebug.version;
11088
11089         var errors = $("fbMiniErrors", doc);
11090         if (errors.offsetWidth)
11091             width += errors.offsetWidth + 10;
11092
11093         var node = this.node;
11094         node.style.height = "27px";
11095         node.style.width = width + "px";
11096         node.style.left = "";
11097         node.style.right = 0;
11098
11099         if (this.node.nodeName.toLowerCase() == "iframe")
11100         {
11101             node.setAttribute("allowTransparency", "true");
11102             this.document.body.style.backgroundColor = "transparent";
11103         }
11104         else
11105             node.style.background = "transparent";
11106
11107         if (noFixedPosition)
11108             this.fixIEPosition();
11109
11110         this.addController(
11111             [$("fbMiniIcon", doc), "click", onMiniIconClick]
11112         );
11113
11114         if (noFixedPosition)
11115         {
11116             this.addController(
11117                 [Firebug.browser.window, "scroll", this.fixIEPosition]
11118             );
11119         }
11120
11121         this.isInitialized = true;
11122     },
11123
11124     shutdown: function()
11125     {
11126         var node = this.node;
11127         node.style.height = Firebug.context.persistedState.height + "px";
11128         node.style.width = "100%";
11129         node.style.left = 0;
11130         node.style.right = "";
11131
11132         if (this.node.nodeName.toLowerCase() == "iframe")
11133         {
11134             node.setAttribute("allowTransparency", "false");
11135             this.document.body.style.backgroundColor = "#fff";
11136         }
11137         else
11138             node.style.background = "#fff";
11139
11140         if (noFixedPosition)
11141             this.fixIEPosition();
11142
11143         var doc = FirebugChrome.chromeMap.frame.document;
11144
11145         var mini = $("fbMiniChrome", doc);
11146         mini.style.display = "none";
11147
11148         Controller.shutdown.apply(this);
11149
11150         this.isInitialized = false;
11151     },
11152
11153     draw: function()
11154     {
11155
11156     },
11157
11158     fixIEPosition: ChromeFrameBase.fixIEPosition
11159
11160 });
11161
11162
11163 // ************************************************************************************************
11164 // ChromePopupBase
11165
11166 /**
11167  * @namespace
11168  * @extends ns-chrome-ChromeBase
11169  */
11170 var ChromePopupBase = extend(ChromeBase,
11171 /**@extend ns-chrome-ChromePopupBase*/
11172 {
11173
11174     initialize: function()
11175     {
11176         setClass(this.document.body, "FirebugPopup");
11177
11178         ChromeBase.initialize.call(this);
11179
11180         this.addController(
11181             [Firebug.chrome.window, "resize", this.resize],
11182             [Firebug.chrome.window, "unload", this.destroy]
11183             //[Firebug.chrome.window, "beforeunload", this.destroy]
11184         );
11185
11186         if (Env.Options.enablePersistent)
11187         {
11188             this.persist = bind(this.persist, this);
11189             addEvent(Firebug.browser.window, "unload", this.persist);
11190         }
11191         else
11192             this.addController(
11193                 [Firebug.browser.window, "unload", this.close]
11194             );
11195
11196         fbVSplitter.onmousedown = onVSplitterMouseDown;
11197     },
11198
11199     destroy: function()
11200     {
11201         var chromeWin = Firebug.chrome.window;
11202         var left = chromeWin.screenX || chromeWin.screenLeft;
11203         var top = chromeWin.screenY || chromeWin.screenTop;
11204         var size = Firebug.chrome.getWindowSize();
11205
11206         Firebug.context.persistedState.popupTop = top;
11207         Firebug.context.persistedState.popupLeft = left;
11208         Firebug.context.persistedState.popupWidth = size.width;
11209         Firebug.context.persistedState.popupHeight = size.height;
11210
11211         if (Firebug.saveCookies)
11212             Firebug.savePrefs();
11213
11214         // TODO: xxxpedro sync detach reattach attach
11215         var frame = FirebugChrome.chromeMap.frame;
11216
11217         if(frame)
11218         {
11219             dispatch(frame.panelMap, "detach", [this, frame]);
11220
11221             frame.reattach(this, frame);
11222         }
11223
11224         if (Env.Options.enablePersistent)
11225         {
11226             removeEvent(Firebug.browser.window, "unload", this.persist);
11227         }
11228
11229         ChromeBase.destroy.apply(this);
11230
11231         FirebugChrome.chromeMap.popup = null;
11232
11233         this.node.close();
11234     },
11235
11236     persist: function()
11237     {
11238         persistTimeStart = new Date().getTime();
11239
11240         removeEvent(Firebug.browser.window, "unload", this.persist);
11241
11242         Firebug.Inspector.destroy();
11243         Firebug.browser.window.FirebugOldBrowser = true;
11244
11245         var persistTimeStart = new Date().getTime();
11246
11247         var waitMainWindow = function()
11248         {
11249             var doc, head;
11250
11251             try
11252             {
11253                 if (window.opener && !window.opener.FirebugOldBrowser && (doc = window.opener.document)/* &&
11254                     doc.documentElement && (head = doc.documentElement.firstChild)*/)
11255                 {
11256
11257                     try
11258                     {
11259                         // exposes the FBL to the global namespace when in debug mode
11260                         if (Env.isDebugMode)
11261                         {
11262                             window.FBL = FBL;
11263                         }
11264
11265                         window.Firebug = Firebug;
11266                         window.opener.Firebug = Firebug;
11267
11268                         Env.browser = window.opener;
11269                         Firebug.browser = Firebug.context = new Context(Env.browser);
11270                         Firebug.loadPrefs();
11271
11272                         registerConsole();
11273
11274                         // the delay time should be calculated right after registering the
11275                         // console, once right after the console registration, call log messages
11276                         // will be properly handled
11277                         var persistDelay = new Date().getTime() - persistTimeStart;
11278
11279                         var chrome = Firebug.chrome;
11280                         addEvent(Firebug.browser.window, "unload", chrome.persist);
11281
11282                         FBL.cacheDocument();
11283                         Firebug.Inspector.create();
11284
11285                         Firebug.Console.logFormatted(
11286                             ["Firebug could not capture console calls during " +
11287                             persistDelay + "ms"],
11288                             Firebug.context,
11289                             "info"
11290                         );
11291
11292                         setTimeout(function(){
11293                             var htmlPanel = chrome.getPanel("HTML");
11294                             htmlPanel.createUI();
11295                         },50);
11296
11297                     }
11298                     catch(pE)
11299                     {
11300                         alert("persist error: " + (pE.message || pE));
11301                     }
11302
11303                 }
11304                 else
11305                 {
11306                     window.setTimeout(waitMainWindow, 0);
11307                 }
11308
11309             } catch (E) {
11310                 window.close();
11311             }
11312         };
11313
11314         waitMainWindow();
11315     },
11316
11317     close: function()
11318     {
11319         this.destroy();
11320     }
11321
11322 });
11323
11324
11325 //************************************************************************************************
11326 // UI helpers
11327
11328 var changeCommandLineVisibility = function changeCommandLineVisibility(visibility)
11329 {
11330     var last = Firebug.chrome.commandLineVisible;
11331     var visible = Firebug.chrome.commandLineVisible =
11332         typeof visibility == "boolean" ? visibility : !Firebug.chrome.commandLineVisible;
11333
11334     if (visible != last)
11335     {
11336         if (visible)
11337         {
11338             fbBottom.className = "";
11339
11340             if (Firebug.CommandLine)
11341                 Firebug.CommandLine.activate();
11342         }
11343         else
11344         {
11345             if (Firebug.CommandLine)
11346                 Firebug.CommandLine.deactivate();
11347
11348             fbBottom.className = "hide";
11349         }
11350     }
11351 };
11352
11353 var changeSidePanelVisibility = function changeSidePanelVisibility(visibility)
11354 {
11355     var last = Firebug.chrome.sidePanelVisible;
11356     Firebug.chrome.sidePanelVisible =
11357         typeof visibility == "boolean" ? visibility : !Firebug.chrome.sidePanelVisible;
11358
11359     if (Firebug.chrome.sidePanelVisible != last)
11360     {
11361         fbPanelBox2.className = Firebug.chrome.sidePanelVisible ? "" : "hide";
11362         fbPanelBar2Box.className = Firebug.chrome.sidePanelVisible ? "" : "hide";
11363     }
11364 };
11365
11366
11367 // ************************************************************************************************
11368 // F12 Handler
11369
11370 var onGlobalKeyDown = function onGlobalKeyDown(event)
11371 {
11372     var keyCode = event.keyCode;
11373     var shiftKey = event.shiftKey;
11374     var ctrlKey = event.ctrlKey;
11375
11376     if (keyCode == 123 /* F12 */ && (!isFirefox && !shiftKey || shiftKey && isFirefox))
11377     {
11378         Firebug.chrome.toggle(false, ctrlKey);
11379         cancelEvent(event, true);
11380
11381         // TODO: xxxpedro replace with a better solution. we're doing this
11382         // to allow reactivating with the F12 key after being deactivated
11383         if (Env.isChromeExtension)
11384         {
11385             Firebug.GoogleChrome.dispatch("FB_enableIcon");
11386         }
11387     }
11388     else if (keyCode == 67 /* C */ && ctrlKey && shiftKey)
11389     {
11390         Firebug.Inspector.toggleInspect();
11391         cancelEvent(event, true);
11392     }
11393     else if (keyCode == 76 /* L */ && ctrlKey && shiftKey)
11394     {
11395         Firebug.chrome.focusCommandLine();
11396         cancelEvent(event, true);
11397     }
11398 };
11399
11400 var onMiniIconClick = function onMiniIconClick(event)
11401 {
11402     Firebug.chrome.toggle(false, event.ctrlKey);
11403     cancelEvent(event, true);
11404 };
11405
11406
11407 // ************************************************************************************************
11408 // Horizontal Splitter Handling
11409
11410 var onHSplitterMouseDown = function onHSplitterMouseDown(event)
11411 {
11412     addGlobalEvent("mousemove", onHSplitterMouseMove);
11413     addGlobalEvent("mouseup", onHSplitterMouseUp);
11414
11415     if (isIE)
11416         addEvent(Firebug.browser.document.documentElement, "mouseleave", onHSplitterMouseUp);
11417
11418     fbHSplitter.className = "fbOnMovingHSplitter";
11419
11420     return false;
11421 };
11422
11423 var onHSplitterMouseMove = function onHSplitterMouseMove(event)
11424 {
11425     cancelEvent(event, true);
11426
11427     var clientY = event.clientY;
11428     var win = isIE
11429         ? event.srcElement.ownerDocument.parentWindow
11430         : event.target.defaultView || event.target.ownerDocument && event.target.ownerDocument.defaultView;
11431
11432     if (!win)
11433         return;
11434
11435     if (win != win.parent)
11436     {
11437         var frameElement = win.frameElement;
11438         if (frameElement)
11439         {
11440             var framePos = Firebug.browser.getElementPosition(frameElement).top;
11441             clientY += framePos;
11442
11443             if (frameElement.style.position != "fixed")
11444                 clientY -= Firebug.browser.getWindowScrollPosition().top;
11445         }
11446     }
11447
11448     if (isOpera && isQuiksMode && win.frameElement.id == "FirebugUI")
11449     {
11450         clientY = Firebug.browser.getWindowSize().height - win.frameElement.offsetHeight + clientY;
11451     }
11452
11453     /*
11454     console.log(
11455             typeof win.FBL != "undefined" ? "no-Chrome" : "Chrome",
11456             //win.frameElement.id,
11457             event.target,
11458             clientY
11459         );/**/
11460
11461     onHSplitterMouseMoveBuffer = clientY; // buffer
11462
11463     if (new Date().getTime() - lastHSplitterMouseMove > chromeRedrawSkipRate) // frame skipping
11464     {
11465         lastHSplitterMouseMove = new Date().getTime();
11466         handleHSplitterMouseMove();
11467     }
11468     else
11469         if (!onHSplitterMouseMoveTimer)
11470             onHSplitterMouseMoveTimer = setTimeout(handleHSplitterMouseMove, chromeRedrawSkipRate);
11471
11472     // improving the resizing performance by canceling the mouse event.
11473     // canceling events will prevent the page to receive such events, which would imply
11474     // in more processing being expended.
11475     cancelEvent(event, true);
11476     return false;
11477 };
11478
11479 var handleHSplitterMouseMove = function()
11480 {
11481     if (onHSplitterMouseMoveTimer)
11482     {
11483         clearTimeout(onHSplitterMouseMoveTimer);
11484         onHSplitterMouseMoveTimer = null;
11485     }
11486
11487     var clientY = onHSplitterMouseMoveBuffer;
11488
11489     var windowSize = Firebug.browser.getWindowSize();
11490     var scrollSize = Firebug.browser.getWindowScrollSize();
11491
11492     // compute chrome fixed size (top bar and command line)
11493     var commandLineHeight = Firebug.chrome.commandLineVisible ? fbCommandLine.offsetHeight : 0;
11494     var fixedHeight = topHeight + commandLineHeight;
11495     var chromeNode = Firebug.chrome.node;
11496
11497     var scrollbarSize = !isIE && (scrollSize.width > windowSize.width) ? 17 : 0;
11498
11499     //var height = !isOpera ? chromeNode.offsetTop + chromeNode.clientHeight : windowSize.height;
11500     var height =  windowSize.height;
11501
11502     // compute the min and max size of the chrome
11503     var chromeHeight = Math.max(height - clientY + 5 - scrollbarSize, fixedHeight);
11504         chromeHeight = Math.min(chromeHeight, windowSize.height - scrollbarSize);
11505
11506     Firebug.context.persistedState.height = chromeHeight;
11507     chromeNode.style.height = chromeHeight + "px";
11508
11509     if (noFixedPosition)
11510         Firebug.chrome.fixIEPosition();
11511
11512     Firebug.chrome.draw();
11513 };
11514
11515 var onHSplitterMouseUp = function onHSplitterMouseUp(event)
11516 {
11517     removeGlobalEvent("mousemove", onHSplitterMouseMove);
11518     removeGlobalEvent("mouseup", onHSplitterMouseUp);
11519
11520     if (isIE)
11521         removeEvent(Firebug.browser.document.documentElement, "mouseleave", onHSplitterMouseUp);
11522
11523     fbHSplitter.className = "";
11524
11525     Firebug.chrome.draw();
11526
11527     // avoid text selection in IE when returning to the document
11528     // after the mouse leaves the document during the resizing
11529     return false;
11530 };
11531
11532
11533 // ************************************************************************************************
11534 // Vertical Splitter Handling
11535
11536 var onVSplitterMouseDown = function onVSplitterMouseDown(event)
11537 {
11538     addGlobalEvent("mousemove", onVSplitterMouseMove);
11539     addGlobalEvent("mouseup", onVSplitterMouseUp);
11540
11541     return false;
11542 };
11543
11544 var onVSplitterMouseMove = function onVSplitterMouseMove(event)
11545 {
11546     if (new Date().getTime() - lastVSplitterMouseMove > chromeRedrawSkipRate) // frame skipping
11547     {
11548         var target = event.target || event.srcElement;
11549         if (target && target.ownerDocument) // avoid error when cursor reaches out of the chrome
11550         {
11551             var clientX = event.clientX;
11552             var win = document.all
11553                 ? event.srcElement.ownerDocument.parentWindow
11554                 : event.target.ownerDocument.defaultView;
11555
11556             if (win != win.parent)
11557                 clientX += win.frameElement ? win.frameElement.offsetLeft : 0;
11558
11559             var size = Firebug.chrome.getSize();
11560             var x = Math.max(size.width - clientX + 3, 6);
11561
11562             Firebug.context.persistedState.sidePanelWidth = x;
11563             Firebug.chrome.draw();
11564         }
11565
11566         lastVSplitterMouseMove = new Date().getTime();
11567     }
11568
11569     cancelEvent(event, true);
11570     return false;
11571 };
11572
11573 var onVSplitterMouseUp = function onVSplitterMouseUp(event)
11574 {
11575     removeGlobalEvent("mousemove", onVSplitterMouseMove);
11576     removeGlobalEvent("mouseup", onVSplitterMouseUp);
11577
11578     Firebug.chrome.draw();
11579 };
11580
11581
11582 // ************************************************************************************************
11583 }});
11584
11585 /* See license.txt for terms of usage */
11586
11587 FBL.ns(function() { with (FBL) {
11588 // ************************************************************************************************
11589
11590 Firebug.Lite =
11591 {
11592 };
11593
11594 // ************************************************************************************************
11595 }});
11596
11597
11598 /* See license.txt for terms of usage */
11599
11600 FBL.ns(function() { with (FBL) {
11601 // ************************************************************************************************
11602
11603 Firebug.Lite.Cache =
11604 {
11605     ID: "firebug-" + new Date().getTime()
11606 };
11607
11608 // ************************************************************************************************
11609
11610 /**
11611  * TODO: if a cached element is cloned, the expando property will be cloned too in IE
11612  * which will result in a bug. Firebug Lite will think the new cloned node is the old
11613  * one.
11614  *
11615  * TODO: Investigate a possibility of cache validation, to be customized by each
11616  * kind of cache. For ElementCache it should validate if the element still is
11617  * inserted at the DOM.
11618  */
11619 var cacheUID = 0;
11620 var createCache = function()
11621 {
11622     var map = {};
11623     var data = {};
11624
11625     var CID = Firebug.Lite.Cache.ID;
11626
11627     // better detection
11628     var supportsDeleteExpando = !document.all;
11629
11630     var cacheFunction = function(element)
11631     {
11632         return cacheAPI.set(element);
11633     };
11634
11635     var cacheAPI =
11636     {
11637         get: function(key)
11638         {
11639             return map.hasOwnProperty(key) ?
11640                     map[key] :
11641                     null;
11642         },
11643
11644         set: function(element)
11645         {
11646             var id = getValidatedKey(element);
11647
11648             if (!id)
11649             {
11650                 id = ++cacheUID;
11651                 element[CID] = id;
11652             }
11653
11654             if (!map.hasOwnProperty(id))
11655             {
11656                 map[id] = element;
11657                 data[id] = {};
11658             }
11659
11660             return id;
11661         },
11662
11663         unset: function(element)
11664         {
11665             var id = getValidatedKey(element);
11666
11667             if (!id) return;
11668
11669             if (supportsDeleteExpando)
11670             {
11671                 delete element[CID];
11672             }
11673             else if (element.removeAttribute)
11674             {
11675                 element.removeAttribute(CID);
11676             }
11677
11678             delete map[id];
11679             delete data[id];
11680
11681         },
11682
11683         key: function(element)
11684         {
11685             return getValidatedKey(element);
11686         },
11687
11688         has: function(element)
11689         {
11690             var id = getValidatedKey(element);
11691             return id && map.hasOwnProperty(id);
11692         },
11693
11694         each: function(callback)
11695         {
11696             for (var key in map)
11697             {
11698                 if (map.hasOwnProperty(key))
11699                 {
11700                     callback(key, map[key]);
11701                 }
11702             }
11703         },
11704
11705         data: function(element, name, value)
11706         {
11707             // set data
11708             if (value)
11709             {
11710                 if (!name) return null;
11711
11712                 var id = cacheAPI.set(element);
11713
11714                 return data[id][name] = value;
11715             }
11716             // get data
11717             else
11718             {
11719                 var id = cacheAPI.key(element);
11720
11721                 return data.hasOwnProperty(id) && data[id].hasOwnProperty(name) ?
11722                         data[id][name] :
11723                         null;
11724             }
11725         },
11726
11727         clear: function()
11728         {
11729             for (var id in map)
11730             {
11731                 var element = map[id];
11732                 cacheAPI.unset(element);
11733             }
11734         }
11735     };
11736
11737     var getValidatedKey = function(element)
11738     {
11739         var id = element[CID];
11740
11741         // If a cached element is cloned in IE, the expando property CID will be also
11742         // cloned (differently than other browsers) resulting in a bug: Firebug Lite
11743         // will think the new cloned node is the old one. To prevent this problem we're
11744         // checking if the cached element matches the given element.
11745         if (
11746             !supportsDeleteExpando &&   // the problem happens when supportsDeleteExpando is false
11747             id &&                       // the element has the expando property
11748             map.hasOwnProperty(id) &&   // there is a cached element with the same id
11749             map[id] != element          // but it is a different element than the current one
11750             )
11751         {
11752             // remove the problematic property
11753             element.removeAttribute(CID);
11754
11755             id = null;
11756         }
11757
11758         return id;
11759     };
11760
11761     FBL.append(cacheFunction, cacheAPI);
11762
11763     return cacheFunction;
11764 };
11765
11766 // ************************************************************************************************
11767
11768 // TODO: xxxpedro : check if we need really this on FBL scope
11769 Firebug.Lite.Cache.StyleSheet = createCache();
11770 Firebug.Lite.Cache.Element = createCache();
11771
11772 // TODO: xxxpedro
11773 Firebug.Lite.Cache.Event = createCache();
11774
11775
11776 // ************************************************************************************************
11777 }});
11778
11779
11780 /* See license.txt for terms of usage */
11781
11782 FBL.ns(function() { with (FBL) {
11783 // ************************************************************************************************
11784
11785 // ************************************************************************************************
11786 var sourceMap = {};
11787
11788 // ************************************************************************************************
11789 Firebug.Lite.Proxy =
11790 {
11791     // jsonp callbacks
11792     _callbacks: {},
11793
11794     /**
11795      * Load a resource, either locally (directly) or externally (via proxy) using
11796      * synchronous XHR calls. Loading external resources requires the proxy plugin to
11797      * be installed and configured (see /plugin/proxy/proxy.php).
11798      */
11799     load: function(url)
11800     {
11801         var resourceDomain = getDomain(url);
11802         var isLocalResource =
11803             // empty domain means local URL
11804             !resourceDomain ||
11805             // same domain means local too
11806             resourceDomain ==  Firebug.context.window.location.host; // TODO: xxxpedro context
11807
11808         return isLocalResource ? fetchResource(url) : fetchProxyResource(url);
11809     },
11810
11811     /**
11812      * Load a resource using JSONP technique.
11813      */
11814     loadJSONP: function(url, callback)
11815     {
11816         var script = createGlobalElement("script"),
11817             doc = Firebug.context.document,
11818
11819             uid = "" + new Date().getTime(),
11820             callbackName = "callback=Firebug.Lite.Proxy._callbacks." + uid,
11821
11822             jsonpURL = url.indexOf("?") != -1 ?
11823                     url + "&" + callbackName :
11824                     url + "?" + callbackName;
11825
11826         Firebug.Lite.Proxy._callbacks[uid] = function(data)
11827         {
11828             if (callback)
11829                 callback(data);
11830
11831             script.parentNode.removeChild(script);
11832             delete Firebug.Lite.Proxy._callbacks[uid];
11833         };
11834
11835         script.src = jsonpURL;
11836
11837         if (doc.documentElement)
11838             doc.documentElement.appendChild(script);
11839     },
11840
11841     /**
11842      * Load a resource using YQL (not reliable).
11843      */
11844     YQL: function(url, callback)
11845     {
11846         var yql = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20html%20where%20url%3D%22" +
11847                 encodeURIComponent(url) + "%22&format=xml";
11848
11849         this.loadJSONP(yql, function(data)
11850         {
11851             var source = data.results[0];
11852
11853             // clean up YQL bogus elements
11854             var match = /<body>\s+<p>([\s\S]+)<\/p>\s+<\/body>$/.exec(source);
11855             if (match)
11856                 source = match[1];
11857
11858             console.log(source);
11859         });
11860     }
11861 };
11862
11863 // ************************************************************************************************
11864
11865 Firebug.Lite.Proxy.fetchResourceDisabledMessage =
11866     "/* Firebug Lite resource fetching is disabled.\n" +
11867     "To enabled it set the Firebug Lite option \"disableResourceFetching\" to \"false\".\n" +
11868     "More info at http://getfirebug.com/firebuglite#Options */";
11869
11870 var fetchResource = function(url)
11871 {
11872     if (Firebug.disableResourceFetching)
11873     {
11874         var source = sourceMap[url] = Firebug.Lite.Proxy.fetchResourceDisabledMessage;
11875         return source;
11876     }
11877
11878     if (sourceMap.hasOwnProperty(url))
11879         return sourceMap[url];
11880
11881     // Getting the native XHR object so our calls won't be logged in the Console Panel
11882     var xhr = FBL.getNativeXHRObject();
11883     xhr.open("get", url, false);
11884     xhr.send();
11885
11886     var source = sourceMap[url] = xhr.responseText;
11887     return source;
11888 };
11889
11890 var fetchProxyResource = function(url)
11891 {
11892     if (sourceMap.hasOwnProperty(url))
11893         return sourceMap[url];
11894
11895     var proxyURL = Env.Location.baseDir + "plugin/proxy/proxy.php?url=" + encodeURIComponent(url);
11896     var response = fetchResource(proxyURL);
11897
11898     try
11899     {
11900         var data = eval("(" + response + ")");
11901     }
11902     catch(E)
11903     {
11904         return "ERROR: Firebug Lite Proxy plugin returned an invalid response.";
11905     }
11906
11907     var source = data ? data.contents : "";
11908     return source;
11909 };
11910
11911
11912 // ************************************************************************************************
11913 }});
11914
11915
11916 /* See license.txt for terms of usage */
11917
11918 FBL.ns(function() { with (FBL) {
11919 // ************************************************************************************************
11920
11921 Firebug.Lite.Style =
11922 {
11923 };
11924
11925 // ************************************************************************************************
11926 }});
11927
11928
11929 /* See license.txt for terms of usage */
11930
11931 FBL.ns(function() { with (FBL) {
11932 // ************************************************************************************************
11933
11934 Firebug.Lite.Script = function(window)
11935 {
11936     this.fileName = null;
11937     this.isValid = null;
11938     this.baseLineNumber = null;
11939     this.lineExtent = null;
11940     this.tag = null;
11941
11942     this.functionName = null;
11943     this.functionSource = null;
11944 };
11945
11946 Firebug.Lite.Script.prototype =
11947 {
11948     isLineExecutable: function(){},
11949     pcToLine: function(){},
11950     lineToPc: function(){},
11951
11952     toString: function()
11953     {
11954         return "Firebug.Lite.Script";
11955     }
11956 };
11957
11958 // ************************************************************************************************
11959 }});
11960
11961
11962 /* See license.txt for terms of usage */
11963
11964 FBL.ns(function() { with (FBL) {
11965 // ************************************************************************************************
11966
11967
11968 Firebug.Lite.Browser = function(window)
11969 {
11970     this.contentWindow = window;
11971     this.contentDocument = window.document;
11972     this.currentURI =
11973     {
11974         spec: window.location.href
11975     };
11976 };
11977
11978 Firebug.Lite.Browser.prototype =
11979 {
11980     toString: function()
11981     {
11982         return "Firebug.Lite.Browser";
11983     }
11984 };
11985
11986
11987 // ************************************************************************************************
11988 }});
11989
11990
11991 /* See license.txt for terms of usage */
11992
11993 /*
11994     http://www.JSON.org/json2.js
11995     2010-03-20
11996
11997     Public Domain.
11998
11999     NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
12000
12001     See http://www.JSON.org/js.html
12002
12003
12004     This code should be minified before deployment.
12005     See http://javascript.crockford.com/jsmin.html
12006
12007     USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
12008     NOT CONTROL.
12009
12010
12011     This file creates a global JSON object containing two methods: stringify
12012     and parse.
12013
12014         JSON.stringify(value, replacer, space)
12015             value       any JavaScript value, usually an object or array.
12016
12017             replacer    an optional parameter that determines how object
12018                         values are stringified for objects. It can be a
12019                         function or an array of strings.
12020
12021             space       an optional parameter that specifies the indentation
12022                         of nested structures. If it is omitted, the text will
12023                         be packed without extra whitespace. If it is a number,
12024                         it will specify the number of spaces to indent at each
12025                         level. If it is a string (such as '\t' or '&nbsp;'),
12026                         it contains the characters used to indent at each level.
12027
12028             This method produces a JSON text from a JavaScript value.
12029
12030             When an object value is found, if the object contains a toJSON
12031             method, its toJSON method will be called and the result will be
12032             stringified. A toJSON method does not serialize: it returns the
12033             value represented by the name/value pair that should be serialized,
12034             or undefined if nothing should be serialized. The toJSON method
12035             will be passed the key associated with the value, and this will be
12036             bound to the value
12037
12038             For example, this would serialize Dates as ISO strings.
12039
12040                 Date.prototype.toJSON = function (key) {
12041                     function f(n) {
12042                         // Format integers to have at least two digits.
12043                         return n < 10 ? '0' + n : n;
12044                     }
12045
12046                     return this.getUTCFullYear()   + '-' +
12047                          f(this.getUTCMonth() + 1) + '-' +
12048                          f(this.getUTCDate())      + 'T' +
12049                          f(this.getUTCHours())     + ':' +
12050                          f(this.getUTCMinutes())   + ':' +
12051                          f(this.getUTCSeconds())   + 'Z';
12052                 };
12053
12054             You can provide an optional replacer method. It will be passed the
12055             key and value of each member, with this bound to the containing
12056             object. The value that is returned from your method will be
12057             serialized. If your method returns undefined, then the member will
12058             be excluded from the serialization.
12059
12060             If the replacer parameter is an array of strings, then it will be
12061             used to select the members to be serialized. It filters the results
12062             such that only members with keys listed in the replacer array are
12063             stringified.
12064
12065             Values that do not have JSON representations, such as undefined or
12066             functions, will not be serialized. Such values in objects will be
12067             dropped; in arrays they will be replaced with null. You can use
12068             a replacer function to replace those with JSON values.
12069             JSON.stringify(undefined) returns undefined.
12070
12071             The optional space parameter produces a stringification of the
12072             value that is filled with line breaks and indentation to make it
12073             easier to read.
12074
12075             If the space parameter is a non-empty string, then that string will
12076             be used for indentation. If the space parameter is a number, then
12077             the indentation will be that many spaces.
12078
12079             Example:
12080
12081             text = JSON.stringify(['e', {pluribus: 'unum'}]);
12082             // text is '["e",{"pluribus":"unum"}]'
12083
12084
12085             text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
12086             // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
12087
12088             text = JSON.stringify([new Date()], function (key, value) {
12089                 return this[key] instanceof Date ?
12090                     'Date(' + this[key] + ')' : value;
12091             });
12092             // text is '["Date(---current time---)"]'
12093
12094
12095         JSON.parse(text, reviver)
12096             This method parses a JSON text to produce an object or array.
12097             It can throw a SyntaxError exception.
12098
12099             The optional reviver parameter is a function that can filter and
12100             transform the results. It receives each of the keys and values,
12101             and its return value is used instead of the original value.
12102             If it returns what it received, then the structure is not modified.
12103             If it returns undefined then the member is deleted.
12104
12105             Example:
12106
12107             // Parse the text. Values that look like ISO date strings will
12108             // be converted to Date objects.
12109
12110             myData = JSON.parse(text, function (key, value) {
12111                 var a;
12112                 if (typeof value === 'string') {
12113                     a =
12114 /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
12115                     if (a) {
12116                         return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
12117                             +a[5], +a[6]));
12118                     }
12119                 }
12120                 return value;
12121             });
12122
12123             myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
12124                 var d;
12125                 if (typeof value === 'string' &&
12126                         value.slice(0, 5) === 'Date(' &&
12127                         value.slice(-1) === ')') {
12128                     d = new Date(value.slice(5, -1));
12129                     if (d) {
12130                         return d;
12131                     }
12132                 }
12133                 return value;
12134             });
12135
12136
12137     This is a reference implementation. You are free to copy, modify, or
12138     redistribute.
12139 */
12140
12141 /*jslint evil: true, strict: false */
12142
12143 /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
12144     call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
12145     getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
12146     lastIndex, length, parse, prototype, push, replace, slice, stringify,
12147     test, toJSON, toString, valueOf
12148 */
12149
12150
12151 // Create a JSON object only if one does not already exist. We create the
12152 // methods in a closure to avoid creating global variables.
12153
12154 // ************************************************************************************************
12155
12156 var JSON = window.JSON || {};
12157
12158 // ************************************************************************************************
12159
12160 (function () {
12161
12162     function f(n) {
12163         // Format integers to have at least two digits.
12164         return n < 10 ? '0' + n : n;
12165     }
12166
12167     if (typeof Date.prototype.toJSON !== 'function') {
12168
12169         Date.prototype.toJSON = function (key) {
12170
12171             return isFinite(this.valueOf()) ?
12172                    this.getUTCFullYear()   + '-' +
12173                  f(this.getUTCMonth() + 1) + '-' +
12174                  f(this.getUTCDate())      + 'T' +
12175                  f(this.getUTCHours())     + ':' +
12176                  f(this.getUTCMinutes())   + ':' +
12177                  f(this.getUTCSeconds())   + 'Z' : null;
12178         };
12179
12180         String.prototype.toJSON =
12181         Number.prototype.toJSON =
12182         Boolean.prototype.toJSON = function (key) {
12183             return this.valueOf();
12184         };
12185     }
12186
12187     var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
12188         escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
12189         gap,
12190         indent,
12191         meta = {    // table of character substitutions
12192             '\b': '\\b',
12193             '\t': '\\t',
12194             '\n': '\\n',
12195             '\f': '\\f',
12196             '\r': '\\r',
12197             '"' : '\\"',
12198             '\\': '\\\\'
12199         },
12200         rep;
12201
12202
12203     function quote(string) {
12204
12205 // If the string contains no control characters, no quote characters, and no
12206 // backslash characters, then we can safely slap some quotes around it.
12207 // Otherwise we must also replace the offending characters with safe escape
12208 // sequences.
12209
12210         escapable.lastIndex = 0;
12211         return escapable.test(string) ?
12212             '"' + string.replace(escapable, function (a) {
12213                 var c = meta[a];
12214                 return typeof c === 'string' ? c :
12215                     '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
12216             }) + '"' :
12217             '"' + string + '"';
12218     }
12219
12220
12221     function str(key, holder) {
12222
12223 // Produce a string from holder[key].
12224
12225         var i,          // The loop counter.
12226             k,          // The member key.
12227             v,          // The member value.
12228             length,
12229             mind = gap,
12230             partial,
12231             value = holder[key];
12232
12233 // If the value has a toJSON method, call it to obtain a replacement value.
12234
12235         if (value && typeof value === 'object' &&
12236                 typeof value.toJSON === 'function') {
12237             value = value.toJSON(key);
12238         }
12239
12240 // If we were called with a replacer function, then call the replacer to
12241 // obtain a replacement value.
12242
12243         if (typeof rep === 'function') {
12244             value = rep.call(holder, key, value);
12245         }
12246
12247 // What happens next depends on the value's type.
12248
12249         switch (typeof value) {
12250         case 'string':
12251             return quote(value);
12252
12253         case 'number':
12254
12255 // JSON numbers must be finite. Encode non-finite numbers as null.
12256
12257             return isFinite(value) ? String(value) : 'null';
12258
12259         case 'boolean':
12260         case 'null':
12261
12262 // If the value is a boolean or null, convert it to a string. Note:
12263 // typeof null does not produce 'null'. The case is included here in
12264 // the remote chance that this gets fixed someday.
12265
12266             return String(value);
12267
12268 // If the type is 'object', we might be dealing with an object or an array or
12269 // null.
12270
12271         case 'object':
12272
12273 // Due to a specification blunder in ECMAScript, typeof null is 'object',
12274 // so watch out for that case.
12275
12276             if (!value) {
12277                 return 'null';
12278             }
12279
12280 // Make an array to hold the partial results of stringifying this object value.
12281
12282             gap += indent;
12283             partial = [];
12284
12285 // Is the value an array?
12286
12287             if (Object.prototype.toString.apply(value) === '[object Array]') {
12288
12289 // The value is an array. Stringify every element. Use null as a placeholder
12290 // for non-JSON values.
12291
12292                 length = value.length;
12293                 for (i = 0; i < length; i += 1) {
12294                     partial[i] = str(i, value) || 'null';
12295                 }
12296
12297 // Join all of the elements together, separated with commas, and wrap them in
12298 // brackets.
12299
12300                 v = partial.length === 0 ? '[]' :
12301                     gap ? '[\n' + gap +
12302                             partial.join(',\n' + gap) + '\n' +
12303                                 mind + ']' :
12304                           '[' + partial.join(',') + ']';
12305                 gap = mind;
12306                 return v;
12307             }
12308
12309 // If the replacer is an array, use it to select the members to be stringified.
12310
12311             if (rep && typeof rep === 'object') {
12312                 length = rep.length;
12313                 for (i = 0; i < length; i += 1) {
12314                     k = rep[i];
12315                     if (typeof k === 'string') {
12316                         v = str(k, value);
12317                         if (v) {
12318                             partial.push(quote(k) + (gap ? ': ' : ':') + v);
12319                         }
12320                     }
12321                 }
12322             } else {
12323
12324 // Otherwise, iterate through all of the keys in the object.
12325
12326                 for (k in value) {
12327                     if (Object.hasOwnProperty.call(value, k)) {
12328                         v = str(k, value);
12329                         if (v) {
12330                             partial.push(quote(k) + (gap ? ': ' : ':') + v);
12331                         }
12332                     }
12333                 }
12334             }
12335
12336 // Join all of the member texts together, separated with commas,
12337 // and wrap them in braces.
12338
12339             v = partial.length === 0 ? '{}' :
12340                 gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
12341                         mind + '}' : '{' + partial.join(',') + '}';
12342             gap = mind;
12343             return v;
12344         }
12345     }
12346
12347 // If the JSON object does not yet have a stringify method, give it one.
12348
12349     if (typeof JSON.stringify !== 'function') {
12350         JSON.stringify = function (value, replacer, space) {
12351
12352 // The stringify method takes a value and an optional replacer, and an optional
12353 // space parameter, and returns a JSON text. The replacer can be a function
12354 // that can replace values, or an array of strings that will select the keys.
12355 // A default replacer method can be provided. Use of the space parameter can
12356 // produce text that is more easily readable.
12357
12358             var i;
12359             gap = '';
12360             indent = '';
12361
12362 // If the space parameter is a number, make an indent string containing that
12363 // many spaces.
12364
12365             if (typeof space === 'number') {
12366                 for (i = 0; i < space; i += 1) {
12367                     indent += ' ';
12368                 }
12369
12370 // If the space parameter is a string, it will be used as the indent string.
12371
12372             } else if (typeof space === 'string') {
12373                 indent = space;
12374             }
12375
12376 // If there is a replacer, it must be a function or an array.
12377 // Otherwise, throw an error.
12378
12379             rep = replacer;
12380             if (replacer && typeof replacer !== 'function' &&
12381                     (typeof replacer !== 'object' ||
12382                      typeof replacer.length !== 'number')) {
12383                 throw new Error('JSON.stringify');
12384             }
12385
12386 // Make a fake root object containing our value under the key of ''.
12387 // Return the result of stringifying the value.
12388
12389             return str('', {'': value});
12390         };
12391     }
12392
12393
12394 // If the JSON object does not yet have a parse method, give it one.
12395
12396     if (typeof JSON.parse !== 'function') {
12397         JSON.parse = function (text, reviver) {
12398
12399 // The parse method takes a text and an optional reviver function, and returns
12400 // a JavaScript value if the text is a valid JSON text.
12401
12402             var j;
12403
12404             function walk(holder, key) {
12405
12406 // The walk method is used to recursively walk the resulting structure so
12407 // that modifications can be made.
12408
12409                 var k, v, value = holder[key];
12410                 if (value && typeof value === 'object') {
12411                     for (k in value) {
12412                         if (Object.hasOwnProperty.call(value, k)) {
12413                             v = walk(value, k);
12414                             if (v !== undefined) {
12415                                 value[k] = v;
12416                             } else {
12417                                 delete value[k];
12418                             }
12419                         }
12420                     }
12421                 }
12422                 return reviver.call(holder, key, value);
12423             }
12424
12425
12426 // Parsing happens in four stages. In the first stage, we replace certain
12427 // Unicode characters with escape sequences. JavaScript handles many characters
12428 // incorrectly, either silently deleting them, or treating them as line endings.
12429
12430             text = String(text);
12431             cx.lastIndex = 0;
12432             if (cx.test(text)) {
12433                 text = text.replace(cx, function (a) {
12434                     return '\\u' +
12435                         ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
12436                 });
12437             }
12438
12439 // In the second stage, we run the text against regular expressions that look
12440 // for non-JSON patterns. We are especially concerned with '()' and 'new'
12441 // because they can cause invocation, and '=' because it can cause mutation.
12442 // But just to be safe, we want to reject all unexpected forms.
12443
12444 // We split the second stage into 4 regexp operations in order to work around
12445 // crippling inefficiencies in IE's and Safari's regexp engines. First we
12446 // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
12447 // replace all simple value tokens with ']' characters. Third, we delete all
12448 // open brackets that follow a colon or comma or that begin the text. Finally,
12449 // we look to see that the remaining characters are only whitespace or ']' or
12450 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
12451
12452             if (/^[\],:{}\s]*$/.
12453 test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
12454 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
12455 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
12456
12457 // In the third stage we use the eval function to compile the text into a
12458 // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
12459 // in JavaScript: it can begin a block or an object literal. We wrap the text
12460 // in parens to eliminate the ambiguity.
12461
12462                 j = eval('(' + text + ')');
12463
12464 // In the optional fourth stage, we recursively walk the new structure, passing
12465 // each name/value pair to a reviver function for possible transformation.
12466
12467                 return typeof reviver === 'function' ?
12468                     walk({'': j}, '') : j;
12469             }
12470
12471 // If the text is not JSON parseable, then a SyntaxError is thrown.
12472
12473             throw new SyntaxError('JSON.parse');
12474         };
12475     }
12476
12477 // ************************************************************************************************
12478 // registration
12479
12480 FBL.JSON = JSON;
12481
12482 // ************************************************************************************************
12483 }());
12484
12485 /* See license.txt for terms of usage */
12486
12487 (function(){
12488 // ************************************************************************************************
12489
12490 /* Copyright (c) 2010-2011 Marcus Westin
12491  *
12492  * Permission is hereby granted, free of charge, to any person obtaining a copy
12493  * of this software and associated documentation files (the "Software"), to deal
12494  * in the Software without restriction, including without limitation the rights
12495  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12496  * copies of the Software, and to permit persons to whom the Software is
12497  * furnished to do so, subject to the following conditions:
12498  *
12499  * The above copyright notice and this permission notice shall be included in
12500  * all copies or substantial portions of the Software.
12501  *
12502  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12503  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12504  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
12505  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
12506  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12507  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
12508  * THE SOFTWARE.
12509  */
12510
12511 var store = (function(){
12512         var api = {},
12513                 win = window,
12514                 doc = win.document,
12515                 localStorageName = 'localStorage',
12516                 globalStorageName = 'globalStorage',
12517                 namespace = '__firebug__storejs__',
12518                 storage
12519
12520         api.disabled = false
12521         api.set = function(key, value) {}
12522         api.get = function(key) {}
12523         api.remove = function(key) {}
12524         api.clear = function() {}
12525         api.transact = function(key, transactionFn) {
12526                 var val = api.get(key)
12527                 if (typeof val == 'undefined') { val = {} }
12528                 transactionFn(val)
12529                 api.set(key, val)
12530         }
12531
12532         api.serialize = function(value) {
12533                 return JSON.stringify(value)
12534         }
12535         api.deserialize = function(value) {
12536                 if (typeof value != 'string') { return undefined }
12537                 return JSON.parse(value)
12538         }
12539
12540         // Functions to encapsulate questionable FireFox 3.6.13 behavior
12541         // when about.config::dom.storage.enabled === false
12542         // See https://github.com/marcuswestin/store.js/issues#issue/13
12543         function isLocalStorageNameSupported() {
12544                 try { return (localStorageName in win && win[localStorageName]) }
12545                 catch(err) { return false }
12546         }
12547
12548         function isGlobalStorageNameSupported() {
12549                 try { return (globalStorageName in win && win[globalStorageName] && win[globalStorageName][win.location.hostname]) }
12550                 catch(err) { return false }
12551         }
12552
12553         if (isLocalStorageNameSupported()) {
12554                 storage = win[localStorageName]
12555                 api.set = function(key, val) { storage.setItem(key, api.serialize(val)) }
12556                 api.get = function(key) { return api.deserialize(storage.getItem(key)) }
12557                 api.remove = function(key) { storage.removeItem(key) }
12558                 api.clear = function() { storage.clear() }
12559
12560         } else if (isGlobalStorageNameSupported()) {
12561                 storage = win[globalStorageName][win.location.hostname]
12562                 api.set = function(key, val) { storage[key] = api.serialize(val) }
12563                 api.get = function(key) { return api.deserialize(storage[key] && storage[key].value) }
12564                 api.remove = function(key) { delete storage[key] }
12565                 api.clear = function() { for (var key in storage ) { delete storage[key] } }
12566
12567         } else if (doc.documentElement.addBehavior) {
12568                 var storage = doc.createElement('div')
12569                 function withIEStorage(storeFunction) {
12570                         return function() {
12571                                 var args = Array.prototype.slice.call(arguments, 0)
12572                                 args.unshift(storage)
12573                                 // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
12574                                 // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
12575                                 // TODO: xxxpedro doc.body is not always available so we must use doc.documentElement.
12576                                 // We need to make sure this change won't affect the behavior of this library.
12577                                 doc.documentElement.appendChild(storage)
12578                                 storage.addBehavior('#default#userData')
12579                                 storage.load(localStorageName)
12580                                 var result = storeFunction.apply(api, args)
12581                                 doc.documentElement.removeChild(storage)
12582                                 return result
12583                         }
12584                 }
12585                 api.set = withIEStorage(function(storage, key, val) {
12586                         storage.setAttribute(key, api.serialize(val))
12587                         storage.save(localStorageName)
12588                 })
12589                 api.get = withIEStorage(function(storage, key) {
12590                         return api.deserialize(storage.getAttribute(key))
12591                 })
12592                 api.remove = withIEStorage(function(storage, key) {
12593                         storage.removeAttribute(key)
12594                         storage.save(localStorageName)
12595                 })
12596                 api.clear = withIEStorage(function(storage) {
12597                         var attributes = storage.XMLDocument.documentElement.attributes
12598                         storage.load(localStorageName)
12599                         for (var i=0, attr; attr = attributes[i]; i++) {
12600                                 storage.removeAttribute(attr.name)
12601                         }
12602                         storage.save(localStorageName)
12603                 })
12604         }
12605
12606         try {
12607                 api.set(namespace, namespace)
12608                 if (api.get(namespace) != namespace) { api.disabled = true }
12609                 api.remove(namespace)
12610         } catch(e) {
12611                 api.disabled = true
12612         }
12613
12614         return api
12615 })();
12616
12617 if (typeof module != 'undefined') { module.exports = store }
12618
12619
12620 // ************************************************************************************************
12621 // registration
12622
12623 FBL.Store = store;
12624
12625 // ************************************************************************************************
12626 })();
12627
12628 /* See license.txt for terms of usage */
12629
12630 FBL.ns( /**@scope s_selector*/ function() { with (FBL) {
12631 // ************************************************************************************************
12632
12633 /*
12634  * Sizzle CSS Selector Engine - v1.0
12635  *  Copyright 2009, The Dojo Foundation
12636  *  Released under the MIT, BSD, and GPL Licenses.
12637  *  More information: http://sizzlejs.com/
12638  */
12639
12640 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
12641     done = 0,
12642     toString = Object.prototype.toString,
12643     hasDuplicate = false,
12644     baseHasDuplicate = true;
12645
12646 // Here we check if the JavaScript engine is using some sort of
12647 // optimization where it does not always call our comparision
12648 // function. If that is the case, discard the hasDuplicate value.
12649 //   Thus far that includes Google Chrome.
12650 [0, 0].sort(function(){
12651     baseHasDuplicate = false;
12652     return 0;
12653 });
12654
12655 /**
12656  * @name Firebug.Selector
12657  * @namespace
12658  */
12659
12660 /**
12661  * @exports Sizzle as Firebug.Selector
12662  */
12663 var Sizzle = function(selector, context, results, seed) {
12664     results = results || [];
12665     var origContext = context = context || document;
12666
12667     if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
12668         return [];
12669     }
12670
12671     if ( !selector || typeof selector !== "string" ) {
12672         return results;
12673     }
12674
12675     var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context),
12676         soFar = selector;
12677
12678     // Reset the position of the chunker regexp (start from head)
12679     while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
12680         soFar = m[3];
12681
12682         parts.push( m[1] );
12683
12684         if ( m[2] ) {
12685             extra = m[3];
12686             break;
12687         }
12688     }
12689
12690     if ( parts.length > 1 && origPOS.exec( selector ) ) {
12691         if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
12692             set = posProcess( parts[0] + parts[1], context );
12693         } else {
12694             set = Expr.relative[ parts[0] ] ?
12695                 [ context ] :
12696                 Sizzle( parts.shift(), context );
12697
12698             while ( parts.length ) {
12699                 selector = parts.shift();
12700
12701                 if ( Expr.relative[ selector ] )
12702                     selector += parts.shift();
12703
12704                 set = posProcess( selector, set );
12705             }
12706         }
12707     } else {
12708         // Take a shortcut and set the context if the root selector is an ID
12709         // (but not if it'll be faster if the inner selector is an ID)
12710         if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
12711                 Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
12712             var ret = Sizzle.find( parts.shift(), context, contextXML );
12713             context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
12714         }
12715
12716         if ( context ) {
12717             var ret = seed ?
12718                 { expr: parts.pop(), set: makeArray(seed) } :
12719                 Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
12720             set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
12721
12722             if ( parts.length > 0 ) {
12723                 checkSet = makeArray(set);
12724             } else {
12725                 prune = false;
12726             }
12727
12728             while ( parts.length ) {
12729                 var cur = parts.pop(), pop = cur;
12730
12731                 if ( !Expr.relative[ cur ] ) {
12732                     cur = "";
12733                 } else {
12734                     pop = parts.pop();
12735                 }
12736
12737                 if ( pop == null ) {
12738                     pop = context;
12739                 }
12740
12741                 Expr.relative[ cur ]( checkSet, pop, contextXML );
12742             }
12743         } else {
12744             checkSet = parts = [];
12745         }
12746     }
12747
12748     if ( !checkSet ) {
12749         checkSet = set;
12750     }
12751
12752     if ( !checkSet ) {
12753         throw "Syntax error, unrecognized expression: " + (cur || selector);
12754     }
12755
12756     if ( toString.call(checkSet) === "[object Array]" ) {
12757         if ( !prune ) {
12758             results.push.apply( results, checkSet );
12759         } else if ( context && context.nodeType === 1 ) {
12760             for ( var i = 0; checkSet[i] != null; i++ ) {
12761                 if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
12762                     results.push( set[i] );
12763                 }
12764             }
12765         } else {
12766             for ( var i = 0; checkSet[i] != null; i++ ) {
12767                 if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
12768                     results.push( set[i] );
12769                 }
12770             }
12771         }
12772     } else {
12773         makeArray( checkSet, results );
12774     }
12775
12776     if ( extra ) {
12777         Sizzle( extra, origContext, results, seed );
12778         Sizzle.uniqueSort( results );
12779     }
12780
12781     return results;
12782 };
12783
12784 Sizzle.uniqueSort = function(results){
12785     if ( sortOrder ) {
12786         hasDuplicate = baseHasDuplicate;
12787         results.sort(sortOrder);
12788
12789         if ( hasDuplicate ) {
12790             for ( var i = 1; i < results.length; i++ ) {
12791                 if ( results[i] === results[i-1] ) {
12792                     results.splice(i--, 1);
12793                 }
12794             }
12795         }
12796     }
12797
12798     return results;
12799 };
12800
12801 Sizzle.matches = function(expr, set){
12802     return Sizzle(expr, null, null, set);
12803 };
12804
12805 Sizzle.find = function(expr, context, isXML){
12806     var set, match;
12807
12808     if ( !expr ) {
12809         return [];
12810     }
12811
12812     for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
12813         var type = Expr.order[i], match;
12814
12815         if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
12816             var left = match[1];
12817             match.splice(1,1);
12818
12819             if ( left.substr( left.length - 1 ) !== "\\" ) {
12820                 match[1] = (match[1] || "").replace(/\\/g, "");
12821                 set = Expr.find[ type ]( match, context, isXML );
12822                 if ( set != null ) {
12823                     expr = expr.replace( Expr.match[ type ], "" );
12824                     break;
12825                 }
12826             }
12827         }
12828     }
12829
12830     if ( !set ) {
12831         set = context.getElementsByTagName("*");
12832     }
12833
12834     return {set: set, expr: expr};
12835 };
12836
12837 Sizzle.filter = function(expr, set, inplace, not){
12838     var old = expr, result = [], curLoop = set, match, anyFound,
12839         isXMLFilter = set && set[0] && isXML(set[0]);
12840
12841     while ( expr && set.length ) {
12842         for ( var type in Expr.filter ) {
12843             if ( (match = Expr.match[ type ].exec( expr )) != null ) {
12844                 var filter = Expr.filter[ type ], found, item;
12845                 anyFound = false;
12846
12847                 if ( curLoop == result ) {
12848                     result = [];
12849                 }
12850
12851                 if ( Expr.preFilter[ type ] ) {
12852                     match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
12853
12854                     if ( !match ) {
12855                         anyFound = found = true;
12856                     } else if ( match === true ) {
12857                         continue;
12858                     }
12859                 }
12860
12861                 if ( match ) {
12862                     for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
12863                         if ( item ) {
12864                             found = filter( item, match, i, curLoop );
12865                             var pass = not ^ !!found;
12866
12867                             if ( inplace && found != null ) {
12868                                 if ( pass ) {
12869                                     anyFound = true;
12870                                 } else {
12871                                     curLoop[i] = false;
12872                                 }
12873                             } else if ( pass ) {
12874                                 result.push( item );
12875                                 anyFound = true;
12876                             }
12877                         }
12878                     }
12879                 }
12880
12881                 if ( found !== undefined ) {
12882                     if ( !inplace ) {
12883                         curLoop = result;
12884                     }
12885
12886                     expr = expr.replace( Expr.match[ type ], "" );
12887
12888                     if ( !anyFound ) {
12889                         return [];
12890                     }
12891
12892                     break;
12893                 }
12894             }
12895         }
12896
12897         // Improper expression
12898         if ( expr == old ) {
12899             if ( anyFound == null ) {
12900                 throw "Syntax error, unrecognized expression: " + expr;
12901             } else {
12902                 break;
12903             }
12904         }
12905
12906         old = expr;
12907     }
12908
12909     return curLoop;
12910 };
12911
12912 /**#@+ @ignore */
12913 var Expr = Sizzle.selectors = {
12914     order: [ "ID", "NAME", "TAG" ],
12915     match: {
12916         ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
12917         CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
12918         NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
12919         ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
12920         TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
12921         CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
12922         POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
12923         PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
12924     },
12925     leftMatch: {},
12926     attrMap: {
12927         "class": "className",
12928         "for": "htmlFor"
12929     },
12930     attrHandle: {
12931         href: function(elem){
12932             return elem.getAttribute("href");
12933         }
12934     },
12935     relative: {
12936         "+": function(checkSet, part, isXML){
12937             var isPartStr = typeof part === "string",
12938                 isTag = isPartStr && !/\W/.test(part),
12939                 isPartStrNotTag = isPartStr && !isTag;
12940
12941             if ( isTag && !isXML ) {
12942                 part = part.toUpperCase();
12943             }
12944
12945             for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
12946                 if ( (elem = checkSet[i]) ) {
12947                     while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
12948
12949                     checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
12950                         elem || false :
12951                         elem === part;
12952                 }
12953             }
12954
12955             if ( isPartStrNotTag ) {
12956                 Sizzle.filter( part, checkSet, true );
12957             }
12958         },
12959         ">": function(checkSet, part, isXML){
12960             var isPartStr = typeof part === "string";
12961
12962             if ( isPartStr && !/\W/.test(part) ) {
12963                 part = isXML ? part : part.toUpperCase();
12964
12965                 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
12966                     var elem = checkSet[i];
12967                     if ( elem ) {
12968                         var parent = elem.parentNode;
12969                         checkSet[i] = parent.nodeName === part ? parent : false;
12970                     }
12971                 }
12972             } else {
12973                 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
12974                     var elem = checkSet[i];
12975                     if ( elem ) {
12976                         checkSet[i] = isPartStr ?
12977                             elem.parentNode :
12978                             elem.parentNode === part;
12979                     }
12980                 }
12981
12982                 if ( isPartStr ) {
12983                     Sizzle.filter( part, checkSet, true );
12984                 }
12985             }
12986         },
12987         "": function(checkSet, part, isXML){
12988             var doneName = done++, checkFn = dirCheck;
12989
12990             if ( !/\W/.test(part) ) {
12991                 var nodeCheck = part = isXML ? part : part.toUpperCase();
12992                 checkFn = dirNodeCheck;
12993             }
12994
12995             checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
12996         },
12997         "~": function(checkSet, part, isXML){
12998             var doneName = done++, checkFn = dirCheck;
12999
13000             if ( typeof part === "string" && !/\W/.test(part) ) {
13001                 var nodeCheck = part = isXML ? part : part.toUpperCase();
13002                 checkFn = dirNodeCheck;
13003             }
13004
13005             checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
13006         }
13007     },
13008     find: {
13009         ID: function(match, context, isXML){
13010             if ( typeof context.getElementById !== "undefined" && !isXML ) {
13011                 var m = context.getElementById(match[1]);
13012                 return m ? [m] : [];
13013             }
13014         },
13015         NAME: function(match, context, isXML){
13016             if ( typeof context.getElementsByName !== "undefined" ) {
13017                 var ret = [], results = context.getElementsByName(match[1]);
13018
13019                 for ( var i = 0, l = results.length; i < l; i++ ) {
13020                     if ( results[i].getAttribute("name") === match[1] ) {
13021                         ret.push( results[i] );
13022                     }
13023                 }
13024
13025                 return ret.length === 0 ? null : ret;
13026             }
13027         },
13028         TAG: function(match, context){
13029             return context.getElementsByTagName(match[1]);
13030         }
13031     },
13032     preFilter: {
13033         CLASS: function(match, curLoop, inplace, result, not, isXML){
13034             match = " " + match[1].replace(/\\/g, "") + " ";
13035
13036             if ( isXML ) {
13037                 return match;
13038             }
13039
13040             for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
13041                 if ( elem ) {
13042                     if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
13043                         if ( !inplace )
13044                             result.push( elem );
13045                     } else if ( inplace ) {
13046                         curLoop[i] = false;
13047                     }
13048                 }
13049             }
13050
13051             return false;
13052         },
13053         ID: function(match){
13054             return match[1].replace(/\\/g, "");
13055         },
13056         TAG: function(match, curLoop){
13057             for ( var i = 0; curLoop[i] === false; i++ ){}
13058             return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
13059         },
13060         CHILD: function(match){
13061             if ( match[1] == "nth" ) {
13062                 // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
13063                 var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
13064                     match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
13065                     !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
13066
13067                 // calculate the numbers (first)n+(last) including if they are negative
13068                 match[2] = (test[1] + (test[2] || 1)) - 0;
13069                 match[3] = test[3] - 0;
13070             }
13071
13072             // TODO: Move to normal caching system
13073             match[0] = done++;
13074
13075             return match;
13076         },
13077         ATTR: function(match, curLoop, inplace, result, not, isXML){
13078             var name = match[1].replace(/\\/g, "");
13079
13080             if ( !isXML && Expr.attrMap[name] ) {
13081                 match[1] = Expr.attrMap[name];
13082             }
13083
13084             if ( match[2] === "~=" ) {
13085                 match[4] = " " + match[4] + " ";
13086             }
13087
13088             return match;
13089         },
13090         PSEUDO: function(match, curLoop, inplace, result, not){
13091             if ( match[1] === "not" ) {
13092                 // If we're dealing with a complex expression, or a simple one
13093                 if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
13094                     match[3] = Sizzle(match[3], null, null, curLoop);
13095                 } else {
13096                     var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
13097                     if ( !inplace ) {
13098                         result.push.apply( result, ret );
13099                     }
13100                     return false;
13101                 }
13102             } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
13103                 return true;
13104             }
13105
13106             return match;
13107         },
13108         POS: function(match){
13109             match.unshift( true );
13110             return match;
13111         }
13112     },
13113     filters: {
13114         enabled: function(elem){
13115             return elem.disabled === false && elem.type !== "hidden";
13116         },
13117         disabled: function(elem){
13118             return elem.disabled === true;
13119         },
13120         checked: function(elem){
13121             return elem.checked === true;
13122         },
13123         selected: function(elem){
13124             // Accessing this property makes selected-by-default
13125             // options in Safari work properly
13126             elem.parentNode.selectedIndex;
13127             return elem.selected === true;
13128         },
13129         parent: function(elem){
13130             return !!elem.firstChild;
13131         },
13132         empty: function(elem){
13133             return !elem.firstChild;
13134         },
13135         has: function(elem, i, match){
13136             return !!Sizzle( match[3], elem ).length;
13137         },
13138         header: function(elem){
13139             return /h\d/i.test( elem.nodeName );
13140         },
13141         text: function(elem){
13142             return "text" === elem.type;
13143         },
13144         radio: function(elem){
13145             return "radio" === elem.type;
13146         },
13147         checkbox: function(elem){
13148             return "checkbox" === elem.type;
13149         },
13150         file: function(elem){
13151             return "file" === elem.type;
13152         },
13153         password: function(elem){
13154             return "password" === elem.type;
13155         },
13156         submit: function(elem){
13157             return "submit" === elem.type;
13158         },
13159         image: function(elem){
13160             return "image" === elem.type;
13161         },
13162         reset: function(elem){
13163             return "reset" === elem.type;
13164         },
13165         button: function(elem){
13166             return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
13167         },
13168         input: function(elem){
13169             return /input|select|textarea|button/i.test(elem.nodeName);
13170         }
13171     },
13172     setFilters: {
13173         first: function(elem, i){
13174             return i === 0;
13175         },
13176         last: function(elem, i, match, array){
13177             return i === array.length - 1;
13178         },
13179         even: function(elem, i){
13180             return i % 2 === 0;
13181         },
13182         odd: function(elem, i){
13183             return i % 2 === 1;
13184         },
13185         lt: function(elem, i, match){
13186             return i < match[3] - 0;
13187         },
13188         gt: function(elem, i, match){
13189             return i > match[3] - 0;
13190         },
13191         nth: function(elem, i, match){
13192             return match[3] - 0 == i;
13193         },
13194         eq: function(elem, i, match){
13195             return match[3] - 0 == i;
13196         }
13197     },
13198     filter: {
13199         PSEUDO: function(elem, match, i, array){
13200             var name = match[1], filter = Expr.filters[ name ];
13201
13202             if ( filter ) {
13203                 return filter( elem, i, match, array );
13204             } else if ( name === "contains" ) {
13205                 return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
13206             } else if ( name === "not" ) {
13207                 var not = match[3];
13208
13209                 for ( var i = 0, l = not.length; i < l; i++ ) {
13210                     if ( not[i] === elem ) {
13211                         return false;
13212                     }
13213                 }
13214
13215                 return true;
13216             }
13217         },
13218         CHILD: function(elem, match){
13219             var type = match[1], node = elem;
13220             switch (type) {
13221                 case 'only':
13222                 case 'first':
13223                     while ( (node = node.previousSibling) )  {
13224                         if ( node.nodeType === 1 ) return false;
13225                     }
13226                     if ( type == 'first') return true;
13227                     node = elem;
13228                 case 'last':
13229                     while ( (node = node.nextSibling) )  {
13230                         if ( node.nodeType === 1 ) return false;
13231                     }
13232                     return true;
13233                 case 'nth':
13234                     var first = match[2], last = match[3];
13235
13236                     if ( first == 1 && last == 0 ) {
13237                         return true;
13238                     }
13239
13240                     var doneName = match[0],
13241                         parent = elem.parentNode;
13242
13243                     if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
13244                         var count = 0;
13245                         for ( node = parent.firstChild; node; node = node.nextSibling ) {
13246                             if ( node.nodeType === 1 ) {
13247                                 node.nodeIndex = ++count;
13248                             }
13249                         }
13250                         parent.sizcache = doneName;
13251                     }
13252
13253                     var diff = elem.nodeIndex - last;
13254                     if ( first == 0 ) {
13255                         return diff == 0;
13256                     } else {
13257                         return ( diff % first == 0 && diff / first >= 0 );
13258                     }
13259             }
13260         },
13261         ID: function(elem, match){
13262             return elem.nodeType === 1 && elem.getAttribute("id") === match;
13263         },
13264         TAG: function(elem, match){
13265             return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
13266         },
13267         CLASS: function(elem, match){
13268             return (" " + (elem.className || elem.getAttribute("class")) + " ")
13269                 .indexOf( match ) > -1;
13270         },
13271         ATTR: function(elem, match){
13272             var name = match[1],
13273                 result = Expr.attrHandle[ name ] ?
13274                     Expr.attrHandle[ name ]( elem ) :
13275                     elem[ name ] != null ?
13276                         elem[ name ] :
13277                         elem.getAttribute( name ),
13278                 value = result + "",
13279                 type = match[2],
13280                 check = match[4];
13281
13282             return result == null ?
13283                 type === "!=" :
13284                 type === "=" ?
13285                 value === check :
13286                 type === "*=" ?
13287                 value.indexOf(check) >= 0 :
13288                 type === "~=" ?
13289                 (" " + value + " ").indexOf(check) >= 0 :
13290                 !check ?
13291                 value && result !== false :
13292                 type === "!=" ?
13293                 value != check :
13294                 type === "^=" ?
13295                 value.indexOf(check) === 0 :
13296                 type === "$=" ?
13297                 value.substr(value.length - check.length) === check :
13298                 type === "|=" ?
13299                 value === check || value.substr(0, check.length + 1) === check + "-" :
13300                 false;
13301         },
13302         POS: function(elem, match, i, array){
13303             var name = match[2], filter = Expr.setFilters[ name ];
13304
13305             if ( filter ) {
13306                 return filter( elem, i, match, array );
13307             }
13308         }
13309     }
13310 };
13311
13312 var origPOS = Expr.match.POS;
13313
13314 for ( var type in Expr.match ) {
13315     Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
13316     Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source );
13317 }
13318
13319 var makeArray = function(array, results) {
13320     array = Array.prototype.slice.call( array, 0 );
13321
13322     if ( results ) {
13323         results.push.apply( results, array );
13324         return results;
13325     }
13326
13327     return array;
13328 };
13329
13330 // Perform a simple check to determine if the browser is capable of
13331 // converting a NodeList to an array using builtin methods.
13332 try {
13333     Array.prototype.slice.call( document.documentElement.childNodes, 0 );
13334
13335 // Provide a fallback method if it does not work
13336 } catch(e){
13337     makeArray = function(array, results) {
13338         var ret = results || [];
13339
13340         if ( toString.call(array) === "[object Array]" ) {
13341             Array.prototype.push.apply( ret, array );
13342         } else {
13343             if ( typeof array.length === "number" ) {
13344                 for ( var i = 0, l = array.length; i < l; i++ ) {
13345                     ret.push( array[i] );
13346                 }
13347             } else {
13348                 for ( var i = 0; array[i]; i++ ) {
13349                     ret.push( array[i] );
13350                 }
13351             }
13352         }
13353
13354         return ret;
13355     };
13356 }
13357
13358 var sortOrder;
13359
13360 if ( document.documentElement.compareDocumentPosition ) {
13361     sortOrder = function( a, b ) {
13362         if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
13363             if ( a == b ) {
13364                 hasDuplicate = true;
13365             }
13366             return 0;
13367         }
13368
13369         var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
13370         if ( ret === 0 ) {
13371             hasDuplicate = true;
13372         }
13373         return ret;
13374     };
13375 } else if ( "sourceIndex" in document.documentElement ) {
13376     sortOrder = function( a, b ) {
13377         if ( !a.sourceIndex || !b.sourceIndex ) {
13378             if ( a == b ) {
13379                 hasDuplicate = true;
13380             }
13381             return 0;
13382         }
13383
13384         var ret = a.sourceIndex - b.sourceIndex;
13385         if ( ret === 0 ) {
13386             hasDuplicate = true;
13387         }
13388         return ret;
13389     };
13390 } else if ( document.createRange ) {
13391     sortOrder = function( a, b ) {
13392         if ( !a.ownerDocument || !b.ownerDocument ) {
13393             if ( a == b ) {
13394                 hasDuplicate = true;
13395             }
13396             return 0;
13397         }
13398
13399         var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
13400         aRange.setStart(a, 0);
13401         aRange.setEnd(a, 0);
13402         bRange.setStart(b, 0);
13403         bRange.setEnd(b, 0);
13404         var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
13405         if ( ret === 0 ) {
13406             hasDuplicate = true;
13407         }
13408         return ret;
13409     };
13410 }
13411
13412 // Check to see if the browser returns elements by name when
13413 // querying by getElementById (and provide a workaround)
13414 (function(){
13415     // We're going to inject a fake input element with a specified name
13416     var form = document.createElement("div"),
13417         id = "script" + (new Date).getTime();
13418     form.innerHTML = "<a name='" + id + "'/>";
13419
13420     // Inject it into the root element, check its status, and remove it quickly
13421     var root = document.documentElement;
13422     root.insertBefore( form, root.firstChild );
13423
13424     // The workaround has to do additional checks after a getElementById
13425     // Which slows things down for other browsers (hence the branching)
13426     if ( !!document.getElementById( id ) ) {
13427         Expr.find.ID = function(match, context, isXML){
13428             if ( typeof context.getElementById !== "undefined" && !isXML ) {
13429                 var m = context.getElementById(match[1]);
13430                 return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
13431             }
13432         };
13433
13434         Expr.filter.ID = function(elem, match){
13435             var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
13436             return elem.nodeType === 1 && node && node.nodeValue === match;
13437         };
13438     }
13439
13440     root.removeChild( form );
13441     root = form = null; // release memory in IE
13442 })();
13443
13444 (function(){
13445     // Check to see if the browser returns only elements
13446     // when doing getElementsByTagName("*")
13447
13448     // Create a fake element
13449     var div = document.createElement("div");
13450     div.appendChild( document.createComment("") );
13451
13452     // Make sure no comments are found
13453     if ( div.getElementsByTagName("*").length > 0 ) {
13454         Expr.find.TAG = function(match, context){
13455             var results = context.getElementsByTagName(match[1]);
13456
13457             // Filter out possible comments
13458             if ( match[1] === "*" ) {
13459                 var tmp = [];
13460
13461                 for ( var i = 0; results[i]; i++ ) {
13462                     if ( results[i].nodeType === 1 ) {
13463                         tmp.push( results[i] );
13464                     }
13465                 }
13466
13467                 results = tmp;
13468             }
13469
13470             return results;
13471         };
13472     }
13473
13474     // Check to see if an attribute returns normalized href attributes
13475     div.innerHTML = "<a href='#'></a>";
13476     if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
13477             div.firstChild.getAttribute("href") !== "#" ) {
13478         Expr.attrHandle.href = function(elem){
13479             return elem.getAttribute("href", 2);
13480         };
13481     }
13482
13483     div = null; // release memory in IE
13484 })();
13485
13486 if ( document.querySelectorAll ) (function(){
13487     var oldSizzle = Sizzle, div = document.createElement("div");
13488     div.innerHTML = "<p class='TEST'></p>";
13489
13490     // Safari can't handle uppercase or unicode characters when
13491     // in quirks mode.
13492     if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
13493         return;
13494     }
13495
13496     Sizzle = function(query, context, extra, seed){
13497         context = context || document;
13498
13499         // Only use querySelectorAll on non-XML documents
13500         // (ID selectors don't work in non-HTML documents)
13501         if ( !seed && context.nodeType === 9 && !isXML(context) ) {
13502             try {
13503                 return makeArray( context.querySelectorAll(query), extra );
13504             } catch(e){}
13505         }
13506
13507         return oldSizzle(query, context, extra, seed);
13508     };
13509
13510     for ( var prop in oldSizzle ) {
13511         Sizzle[ prop ] = oldSizzle[ prop ];
13512     }
13513
13514     div = null; // release memory in IE
13515 })();
13516
13517 if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
13518     var div = document.createElement("div");
13519     div.innerHTML = "<div class='test e'></div><div class='test'></div>";
13520
13521     // Opera can't find a second classname (in 9.6)
13522     if ( div.getElementsByClassName("e").length === 0 )
13523         return;
13524
13525     // Safari caches class attributes, doesn't catch changes (in 3.2)
13526     div.lastChild.className = "e";
13527
13528     if ( div.getElementsByClassName("e").length === 1 )
13529         return;
13530
13531     Expr.order.splice(1, 0, "CLASS");
13532     Expr.find.CLASS = function(match, context, isXML) {
13533         if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
13534             return context.getElementsByClassName(match[1]);
13535         }
13536     };
13537
13538     div = null; // release memory in IE
13539 })();
13540
13541 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
13542     var sibDir = dir == "previousSibling" && !isXML;
13543     for ( var i = 0, l = checkSet.length; i < l; i++ ) {
13544         var elem = checkSet[i];
13545         if ( elem ) {
13546             if ( sibDir && elem.nodeType === 1 ){
13547                 elem.sizcache = doneName;
13548                 elem.sizset = i;
13549             }
13550             elem = elem[dir];
13551             var match = false;
13552
13553             while ( elem ) {
13554                 if ( elem.sizcache === doneName ) {
13555                     match = checkSet[elem.sizset];
13556                     break;
13557                 }
13558
13559                 if ( elem.nodeType === 1 && !isXML ){
13560                     elem.sizcache = doneName;
13561                     elem.sizset = i;
13562                 }
13563
13564                 if ( elem.nodeName === cur ) {
13565                     match = elem;
13566                     break;
13567                 }
13568
13569                 elem = elem[dir];
13570             }
13571
13572             checkSet[i] = match;
13573         }
13574     }
13575 }
13576
13577 function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
13578     var sibDir = dir == "previousSibling" && !isXML;
13579     for ( var i = 0, l = checkSet.length; i < l; i++ ) {
13580         var elem = checkSet[i];
13581         if ( elem ) {
13582             if ( sibDir && elem.nodeType === 1 ) {
13583                 elem.sizcache = doneName;
13584                 elem.sizset = i;
13585             }
13586             elem = elem[dir];
13587             var match = false;
13588
13589             while ( elem ) {
13590                 if ( elem.sizcache === doneName ) {
13591                     match = checkSet[elem.sizset];
13592                     break;
13593                 }
13594
13595                 if ( elem.nodeType === 1 ) {
13596                     if ( !isXML ) {
13597                         elem.sizcache = doneName;
13598                         elem.sizset = i;
13599                     }
13600                     if ( typeof cur !== "string" ) {
13601                         if ( elem === cur ) {
13602                             match = true;
13603                             break;
13604                         }
13605
13606                     } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
13607                         match = elem;
13608                         break;
13609                     }
13610                 }
13611
13612                 elem = elem[dir];
13613             }
13614
13615             checkSet[i] = match;
13616         }
13617     }
13618 }
13619
13620 var contains = document.compareDocumentPosition ?  function(a, b){
13621     return a.compareDocumentPosition(b) & 16;
13622 } : function(a, b){
13623     return a !== b && (a.contains ? a.contains(b) : true);
13624 };
13625
13626 var isXML = function(elem){
13627     return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
13628         !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
13629 };
13630
13631 var posProcess = function(selector, context){
13632     var tmpSet = [], later = "", match,
13633         root = context.nodeType ? [context] : context;
13634
13635     // Position selectors must be done after the filter
13636     // And so must :not(positional) so we move all PSEUDOs to the end
13637     while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
13638         later += match[0];
13639         selector = selector.replace( Expr.match.PSEUDO, "" );
13640     }
13641
13642     selector = Expr.relative[selector] ? selector + "*" : selector;
13643
13644     for ( var i = 0, l = root.length; i < l; i++ ) {
13645         Sizzle( selector, root[i], tmpSet );
13646     }
13647
13648     return Sizzle.filter( later, tmpSet );
13649 };
13650
13651 // EXPOSE
13652
13653 Firebug.Selector = Sizzle;
13654
13655 /**#@-*/
13656
13657 // ************************************************************************************************
13658 }});
13659
13660 /* See license.txt for terms of usage */
13661
13662 FBL.ns(function() { with (FBL) {
13663 // ************************************************************************************************
13664
13665 // ************************************************************************************************
13666 // Inspector Module
13667
13668 var ElementCache = Firebug.Lite.Cache.Element;
13669
13670 var inspectorTS, inspectorTimer, isInspecting;
13671
13672 Firebug.Inspector =
13673 {
13674     create: function()
13675     {
13676         offlineFragment = Env.browser.document.createDocumentFragment();
13677
13678         createBoxModelInspector();
13679         createOutlineInspector();
13680     },
13681
13682     destroy: function()
13683     {
13684         destroyBoxModelInspector();
13685         destroyOutlineInspector();
13686
13687         offlineFragment = null;
13688     },
13689
13690     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
13691     // Inspect functions
13692
13693     toggleInspect: function()
13694     {
13695         if (isInspecting)
13696         {
13697             this.stopInspecting();
13698         }
13699         else
13700         {
13701             Firebug.chrome.inspectButton.changeState("pressed");
13702             this.startInspecting();
13703         }
13704     },
13705
13706     startInspecting: function()
13707     {
13708         isInspecting = true;
13709
13710         Firebug.chrome.selectPanel("HTML");
13711
13712         createInspectorFrame();
13713
13714         var size = Firebug.browser.getWindowScrollSize();
13715
13716         fbInspectFrame.style.width = size.width + "px";
13717         fbInspectFrame.style.height = size.height + "px";
13718
13719         //addEvent(Firebug.browser.document.documentElement, "mousemove", Firebug.Inspector.onInspectingBody);
13720
13721         addEvent(fbInspectFrame, "mousemove", Firebug.Inspector.onInspecting);
13722         addEvent(fbInspectFrame, "mousedown", Firebug.Inspector.onInspectingClick);
13723     },
13724
13725     stopInspecting: function()
13726     {
13727         isInspecting = false;
13728
13729         if (outlineVisible) this.hideOutline();
13730         removeEvent(fbInspectFrame, "mousemove", Firebug.Inspector.onInspecting);
13731         removeEvent(fbInspectFrame, "mousedown", Firebug.Inspector.onInspectingClick);
13732
13733         destroyInspectorFrame();
13734
13735         Firebug.chrome.inspectButton.restore();
13736
13737         if (Firebug.chrome.type == "popup")
13738             Firebug.chrome.node.focus();
13739     },
13740
13741     onInspectingClick: function(e)
13742     {
13743         fbInspectFrame.style.display = "none";
13744         var targ = Firebug.browser.getElementFromPoint(e.clientX, e.clientY);
13745         fbInspectFrame.style.display = "block";
13746
13747         // Avoid inspecting the outline, and the FirebugUI
13748         var id = targ.id;
13749         if (id && /^fbOutline\w$/.test(id)) return;
13750         if (id == "FirebugUI") return;
13751
13752         // Avoid looking at text nodes in Opera
13753         while (targ.nodeType != 1) targ = targ.parentNode;
13754
13755         //Firebug.Console.log(targ);
13756         Firebug.Inspector.stopInspecting();
13757     },
13758
13759     onInspecting: function(e)
13760     {
13761         if (new Date().getTime() - lastInspecting > 30)
13762         {
13763             fbInspectFrame.style.display = "none";
13764             var targ = Firebug.browser.getElementFromPoint(e.clientX, e.clientY);
13765             fbInspectFrame.style.display = "block";
13766
13767             // Avoid inspecting the outline, and the FirebugUI
13768             var id = targ.id;
13769             if (id && /^fbOutline\w$/.test(id)) return;
13770             if (id == "FirebugUI") return;
13771
13772             // Avoid looking at text nodes in Opera
13773             while (targ.nodeType != 1) targ = targ.parentNode;
13774
13775             if (targ.nodeName.toLowerCase() == "body") return;
13776
13777             //Firebug.Console.log(e.clientX, e.clientY, targ);
13778             Firebug.Inspector.drawOutline(targ);
13779
13780             if (ElementCache(targ))
13781             {
13782                 var target = ""+ElementCache.key(targ);
13783                 var lazySelect = function()
13784                 {
13785                     inspectorTS = new Date().getTime();
13786
13787                     if (Firebug.HTML)
13788                         Firebug.HTML.selectTreeNode(""+ElementCache.key(targ));
13789                 };
13790
13791                 if (inspectorTimer)
13792                 {
13793                     clearTimeout(inspectorTimer);
13794                     inspectorTimer = null;
13795                 }
13796
13797                 if (new Date().getTime() - inspectorTS > 200)
13798                     setTimeout(lazySelect, 0);
13799                 else
13800                     inspectorTimer = setTimeout(lazySelect, 300);
13801             }
13802
13803             lastInspecting = new Date().getTime();
13804         }
13805     },
13806
13807     // TODO: xxxpedro remove this?
13808     onInspectingBody: function(e)
13809     {
13810         if (new Date().getTime() - lastInspecting > 30)
13811         {
13812             var targ = e.target;
13813
13814             // Avoid inspecting the outline, and the FirebugUI
13815             var id = targ.id;
13816             if (id && /^fbOutline\w$/.test(id)) return;
13817             if (id == "FirebugUI") return;
13818
13819             // Avoid looking at text nodes in Opera
13820             while (targ.nodeType != 1) targ = targ.parentNode;
13821
13822             if (targ.nodeName.toLowerCase() == "body") return;
13823
13824             //Firebug.Console.log(e.clientX, e.clientY, targ);
13825             Firebug.Inspector.drawOutline(targ);
13826
13827             if (ElementCache.has(targ))
13828                 FBL.Firebug.HTML.selectTreeNode(""+ElementCache.key(targ));
13829
13830             lastInspecting = new Date().getTime();
13831         }
13832     },
13833
13834     /**
13835      *
13836      *   llttttttrr
13837      *   llttttttrr
13838      *   ll      rr
13839      *   ll      rr
13840      *   llbbbbbbrr
13841      *   llbbbbbbrr
13842      */
13843     drawOutline: function(el)
13844     {
13845         var border = 2;
13846         var scrollbarSize = 17;
13847
13848         var windowSize = Firebug.browser.getWindowSize();
13849         var scrollSize = Firebug.browser.getWindowScrollSize();
13850         var scrollPosition = Firebug.browser.getWindowScrollPosition();
13851
13852         var box = Firebug.browser.getElementBox(el);
13853
13854         var top = box.top;
13855         var left = box.left;
13856         var height = box.height;
13857         var width = box.width;
13858
13859         var freeHorizontalSpace = scrollPosition.left + windowSize.width - left - width -
13860                 (!isIE && scrollSize.height > windowSize.height ? // is *vertical* scrollbar visible
13861                  scrollbarSize : 0);
13862
13863         var freeVerticalSpace = scrollPosition.top + windowSize.height - top - height -
13864                 (!isIE && scrollSize.width > windowSize.width ? // is *horizontal* scrollbar visible
13865                 scrollbarSize : 0);
13866
13867         var numVerticalBorders = freeVerticalSpace > 0 ? 2 : 1;
13868
13869         var o = outlineElements;
13870         var style;
13871
13872         style = o.fbOutlineT.style;
13873         style.top = top-border + "px";
13874         style.left = left + "px";
13875         style.height = border + "px";  // TODO: on initialize()
13876         style.width = width + "px";
13877
13878         style = o.fbOutlineL.style;
13879         style.top = top-border + "px";
13880         style.left = left-border + "px";
13881         style.height = height+ numVerticalBorders*border + "px";
13882         style.width = border + "px";  // TODO: on initialize()
13883
13884         style = o.fbOutlineB.style;
13885         if (freeVerticalSpace > 0)
13886         {
13887             style.top = top+height + "px";
13888             style.left = left + "px";
13889             style.width = width + "px";
13890             //style.height = border + "px"; // TODO: on initialize() or worst case?
13891         }
13892         else
13893         {
13894             style.top = -2*border + "px";
13895             style.left = -2*border + "px";
13896             style.width = border + "px";
13897             //style.height = border + "px";
13898         }
13899
13900         style = o.fbOutlineR.style;
13901         if (freeHorizontalSpace > 0)
13902         {
13903             style.top = top-border + "px";
13904             style.left = left+width + "px";
13905             style.height = height + numVerticalBorders*border + "px";
13906             style.width = (freeHorizontalSpace < border ? freeHorizontalSpace : border) + "px";
13907         }
13908         else
13909         {
13910             style.top = -2*border + "px";
13911             style.left = -2*border + "px";
13912             style.height = border + "px";
13913             style.width = border + "px";
13914         }
13915
13916         if (!outlineVisible) this.showOutline();
13917     },
13918
13919     hideOutline: function()
13920     {
13921         if (!outlineVisible) return;
13922
13923         for (var name in outline)
13924             offlineFragment.appendChild(outlineElements[name]);
13925
13926         outlineVisible = false;
13927     },
13928
13929     showOutline: function()
13930     {
13931         if (outlineVisible) return;
13932
13933         if (boxModelVisible) this.hideBoxModel();
13934
13935         for (var name in outline)
13936             Firebug.browser.document.getElementsByTagName("body")[0].appendChild(outlineElements[name]);
13937
13938         outlineVisible = true;
13939     },
13940
13941     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
13942     // Box Model
13943
13944     drawBoxModel: function(el)
13945     {
13946         // avoid error when the element is not attached a document
13947         if (!el || !el.parentNode)
13948             return;
13949
13950         var box = Firebug.browser.getElementBox(el);
13951
13952         var windowSize = Firebug.browser.getWindowSize();
13953         var scrollPosition = Firebug.browser.getWindowScrollPosition();
13954
13955         // element may be occluded by the chrome, when in frame mode
13956         var offsetHeight = Firebug.chrome.type == "frame" ? Firebug.context.persistedState.height : 0;
13957
13958         // if element box is not inside the viewport, don't draw the box model
13959         if (box.top > scrollPosition.top + windowSize.height - offsetHeight ||
13960             box.left > scrollPosition.left + windowSize.width ||
13961             scrollPosition.top > box.top + box.height ||
13962             scrollPosition.left > box.left + box.width )
13963             return;
13964
13965         var top = box.top;
13966         var left = box.left;
13967         var height = box.height;
13968         var width = box.width;
13969
13970         var margin = Firebug.browser.getMeasurementBox(el, "margin");
13971         var padding = Firebug.browser.getMeasurementBox(el, "padding");
13972         var border = Firebug.browser.getMeasurementBox(el, "border");
13973
13974         boxModelStyle.top = top - margin.top + "px";
13975         boxModelStyle.left = left - margin.left + "px";
13976         boxModelStyle.height = height + margin.top + margin.bottom + "px";
13977         boxModelStyle.width = width + margin.left + margin.right + "px";
13978
13979         boxBorderStyle.top = margin.top + "px";
13980         boxBorderStyle.left = margin.left + "px";
13981         boxBorderStyle.height = height + "px";
13982         boxBorderStyle.width = width + "px";
13983
13984         boxPaddingStyle.top = margin.top + border.top + "px";
13985         boxPaddingStyle.left = margin.left + border.left + "px";
13986         boxPaddingStyle.height = height - border.top - border.bottom + "px";
13987         boxPaddingStyle.width = width - border.left - border.right + "px";
13988
13989         boxContentStyle.top = margin.top + border.top + padding.top + "px";
13990         boxContentStyle.left = margin.left + border.left + padding.left + "px";
13991         boxContentStyle.height = height - border.top - padding.top - padding.bottom - border.bottom + "px";
13992         boxContentStyle.width = width - border.left - padding.left - padding.right - border.right + "px";
13993
13994         if (!boxModelVisible) this.showBoxModel();
13995     },
13996
13997     hideBoxModel: function()
13998     {
13999         if (!boxModelVisible) return;
14000
14001         offlineFragment.appendChild(boxModel);
14002         boxModelVisible = false;
14003     },
14004
14005     showBoxModel: function()
14006     {
14007         if (boxModelVisible) return;
14008
14009         if (outlineVisible) this.hideOutline();
14010
14011         Firebug.browser.document.getElementsByTagName("body")[0].appendChild(boxModel);
14012         boxModelVisible = true;
14013     }
14014
14015 };
14016
14017 // ************************************************************************************************
14018 // Inspector Internals
14019
14020
14021 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14022 // Shared variables
14023
14024
14025
14026 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14027 // Internal variables
14028
14029 var offlineFragment = null;
14030
14031 var boxModelVisible = false;
14032
14033 var boxModel, boxModelStyle,
14034     boxMargin, boxMarginStyle,
14035     boxBorder, boxBorderStyle,
14036     boxPadding, boxPaddingStyle,
14037     boxContent, boxContentStyle;
14038
14039 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14040
14041 var resetStyle = "margin:0; padding:0; border:0; position:absolute; overflow:hidden; display:block;";
14042 var offscreenStyle = resetStyle + "top:-1234px; left:-1234px;";
14043
14044 var inspectStyle = resetStyle + "z-index: 2147483500;";
14045 var inspectFrameStyle = resetStyle + "z-index: 2147483550; top:0; left:0; background:url(" +
14046                         Env.Location.skinDir + "pixel_transparent.gif);";
14047
14048 //if (Env.Options.enableTrace) inspectFrameStyle = resetStyle + "z-index: 2147483550; top: 0; left: 0; background: #ff0; opacity: 0.05; _filter: alpha(opacity=5);";
14049
14050 var inspectModelOpacity = isIE ? "filter:alpha(opacity=80);" : "opacity:0.8;";
14051 var inspectModelStyle = inspectStyle + inspectModelOpacity;
14052 var inspectMarginStyle = inspectStyle + "background: #EDFF64; height:100%; width:100%;";
14053 var inspectBorderStyle = inspectStyle + "background: #666;";
14054 var inspectPaddingStyle = inspectStyle + "background: SlateBlue;";
14055 var inspectContentStyle = inspectStyle + "background: SkyBlue;";
14056
14057
14058 var outlineStyle = {
14059     fbHorizontalLine: "background: #3875D7;height: 2px;",
14060     fbVerticalLine: "background: #3875D7;width: 2px;"
14061 };
14062
14063 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14064
14065 var lastInspecting = 0;
14066 var fbInspectFrame = null;
14067
14068
14069 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14070
14071 var outlineVisible = false;
14072 var outlineElements = {};
14073 var outline = {
14074   "fbOutlineT": "fbHorizontalLine",
14075   "fbOutlineL": "fbVerticalLine",
14076   "fbOutlineB": "fbHorizontalLine",
14077   "fbOutlineR": "fbVerticalLine"
14078 };
14079
14080
14081 var getInspectingTarget = function()
14082 {
14083
14084 };
14085
14086 // ************************************************************************************************
14087 // Section
14088
14089 var createInspectorFrame = function createInspectorFrame()
14090 {
14091     fbInspectFrame = createGlobalElement("div");
14092     fbInspectFrame.id = "fbInspectFrame";
14093     fbInspectFrame.firebugIgnore = true;
14094     fbInspectFrame.style.cssText = inspectFrameStyle;
14095     Firebug.browser.document.getElementsByTagName("body")[0].appendChild(fbInspectFrame);
14096 };
14097
14098 var destroyInspectorFrame = function destroyInspectorFrame()
14099 {
14100     if (fbInspectFrame)
14101     {
14102         Firebug.browser.document.getElementsByTagName("body")[0].removeChild(fbInspectFrame);
14103         fbInspectFrame = null;
14104     }
14105 };
14106
14107 var createOutlineInspector = function createOutlineInspector()
14108 {
14109     for (var name in outline)
14110     {
14111         var el = outlineElements[name] = createGlobalElement("div");
14112         el.id = name;
14113         el.firebugIgnore = true;
14114         el.style.cssText = inspectStyle + outlineStyle[outline[name]];
14115         offlineFragment.appendChild(el);
14116     }
14117 };
14118
14119 var destroyOutlineInspector = function destroyOutlineInspector()
14120 {
14121     for (var name in outline)
14122     {
14123         var el = outlineElements[name];
14124         el.parentNode.removeChild(el);
14125     }
14126 };
14127
14128 var createBoxModelInspector = function createBoxModelInspector()
14129 {
14130     boxModel = createGlobalElement("div");
14131     boxModel.id = "fbBoxModel";
14132     boxModel.firebugIgnore = true;
14133     boxModelStyle = boxModel.style;
14134     boxModelStyle.cssText = inspectModelStyle;
14135
14136     boxMargin = createGlobalElement("div");
14137     boxMargin.id = "fbBoxMargin";
14138     boxMarginStyle = boxMargin.style;
14139     boxMarginStyle.cssText = inspectMarginStyle;
14140     boxModel.appendChild(boxMargin);
14141
14142     boxBorder = createGlobalElement("div");
14143     boxBorder.id = "fbBoxBorder";
14144     boxBorderStyle = boxBorder.style;
14145     boxBorderStyle.cssText = inspectBorderStyle;
14146     boxModel.appendChild(boxBorder);
14147
14148     boxPadding = createGlobalElement("div");
14149     boxPadding.id = "fbBoxPadding";
14150     boxPaddingStyle = boxPadding.style;
14151     boxPaddingStyle.cssText = inspectPaddingStyle;
14152     boxModel.appendChild(boxPadding);
14153
14154     boxContent = createGlobalElement("div");
14155     boxContent.id = "fbBoxContent";
14156     boxContentStyle = boxContent.style;
14157     boxContentStyle.cssText = inspectContentStyle;
14158     boxModel.appendChild(boxContent);
14159
14160     offlineFragment.appendChild(boxModel);
14161 };
14162
14163 var destroyBoxModelInspector = function destroyBoxModelInspector()
14164 {
14165     boxModel.parentNode.removeChild(boxModel);
14166 };
14167
14168 // ************************************************************************************************
14169 // Section
14170
14171
14172
14173
14174 // ************************************************************************************************
14175 }});
14176
14177 // Problems in IE
14178 // FIXED - eval return
14179 // FIXED - addEventListener problem in IE
14180 // FIXED doc.createRange?
14181 //
14182 // class reserved word
14183 // test all honza examples in IE6 and IE7
14184
14185
14186 /* See license.txt for terms of usage */
14187
14188 ( /** @scope s_domplate */ function() {
14189
14190 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14191
14192 /** @class */
14193 FBL.DomplateTag = function DomplateTag(tagName)
14194 {
14195     this.tagName = tagName;
14196 };
14197
14198 /**
14199  * @class
14200  * @extends FBL.DomplateTag
14201  */
14202 FBL.DomplateEmbed = function DomplateEmbed()
14203 {
14204 };
14205
14206 /**
14207  * @class
14208  * @extends FBL.DomplateTag
14209  */
14210 FBL.DomplateLoop = function DomplateLoop()
14211 {
14212 };
14213
14214 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14215
14216 var DomplateTag = FBL.DomplateTag;
14217 var DomplateEmbed = FBL.DomplateEmbed;
14218 var DomplateLoop = FBL.DomplateLoop;
14219
14220 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14221
14222 var womb = null;
14223
14224 FBL.domplate = function()
14225 {
14226     var lastSubject;
14227     for (var i = 0; i < arguments.length; ++i)
14228         lastSubject = lastSubject ? copyObject(lastSubject, arguments[i]) : arguments[i];
14229
14230     for (var name in lastSubject)
14231     {
14232         var val = lastSubject[name];
14233         if (isTag(val))
14234             val.tag.subject = lastSubject;
14235     }
14236
14237     return lastSubject;
14238 };
14239
14240 var domplate = FBL.domplate;
14241
14242 FBL.domplate.context = function(context, fn)
14243 {
14244     var lastContext = domplate.lastContext;
14245     domplate.topContext = context;
14246     fn.apply(context);
14247     domplate.topContext = lastContext;
14248 };
14249
14250 FBL.TAG = function()
14251 {
14252     var embed = new DomplateEmbed();
14253     return embed.merge(arguments);
14254 };
14255
14256 FBL.FOR = function()
14257 {
14258     var loop = new DomplateLoop();
14259     return loop.merge(arguments);
14260 };
14261
14262 FBL.DomplateTag.prototype =
14263 {
14264     merge: function(args, oldTag)
14265     {
14266         if (oldTag)
14267             this.tagName = oldTag.tagName;
14268
14269         this.context = oldTag ? oldTag.context : null;
14270         this.subject = oldTag ? oldTag.subject : null;
14271         this.attrs = oldTag ? copyObject(oldTag.attrs) : {};
14272         this.classes = oldTag ? copyObject(oldTag.classes) : {};
14273         this.props = oldTag ? copyObject(oldTag.props) : null;
14274         this.listeners = oldTag ? copyArray(oldTag.listeners) : null;
14275         this.children = oldTag ? copyArray(oldTag.children) : [];
14276         this.vars = oldTag ? copyArray(oldTag.vars) : [];
14277
14278         var attrs = args.length ? args[0] : null;
14279         var hasAttrs = typeof(attrs) == "object" && !isTag(attrs);
14280
14281         this.children = [];
14282
14283         if (domplate.topContext)
14284             this.context = domplate.topContext;
14285
14286         if (args.length)
14287             parseChildren(args, hasAttrs ? 1 : 0, this.vars, this.children);
14288
14289         if (hasAttrs)
14290             this.parseAttrs(attrs);
14291
14292         return creator(this, DomplateTag);
14293     },
14294
14295     parseAttrs: function(args)
14296     {
14297         for (var name in args)
14298         {
14299             var val = parseValue(args[name]);
14300             readPartNames(val, this.vars);
14301
14302             if (name.indexOf("on") == 0)
14303             {
14304                 var eventName = name.substr(2);
14305                 if (!this.listeners)
14306                     this.listeners = [];
14307                 this.listeners.push(eventName, val);
14308             }
14309             else if (name.indexOf("_") == 0)
14310             {
14311                 var propName = name.substr(1);
14312                 if (!this.props)
14313                     this.props = {};
14314                 this.props[propName] = val;
14315             }
14316             else if (name.indexOf("$") == 0)
14317             {
14318                 var className = name.substr(1);
14319                 if (!this.classes)
14320                     this.classes = {};
14321                 this.classes[className] = val;
14322             }
14323             else
14324             {
14325                 if (name == "class" && this.attrs.hasOwnProperty(name) )
14326                     this.attrs[name] += " " + val;
14327                 else
14328                     this.attrs[name] = val;
14329             }
14330         }
14331     },
14332
14333     compile: function()
14334     {
14335         if (this.renderMarkup)
14336             return;
14337
14338         this.compileMarkup();
14339         this.compileDOM();
14340
14341         //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate renderMarkup: ", this.renderMarkup);
14342         //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate renderDOM:", this.renderDOM);
14343         //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate domArgs:", this.domArgs);
14344     },
14345
14346     compileMarkup: function()
14347     {
14348         this.markupArgs = [];
14349         var topBlock = [], topOuts = [], blocks = [], info = {args: this.markupArgs, argIndex: 0};
14350
14351         this.generateMarkup(topBlock, topOuts, blocks, info);
14352         this.addCode(topBlock, topOuts, blocks);
14353
14354         var fnBlock = ['r=(function (__code__, __context__, __in__, __out__'];
14355         for (var i = 0; i < info.argIndex; ++i)
14356             fnBlock.push(', s', i);
14357         fnBlock.push(') {');
14358
14359         if (this.subject)
14360             fnBlock.push('with (this) {');
14361         if (this.context)
14362             fnBlock.push('with (__context__) {');
14363         fnBlock.push('with (__in__) {');
14364
14365         fnBlock.push.apply(fnBlock, blocks);
14366
14367         if (this.subject)
14368             fnBlock.push('}');
14369         if (this.context)
14370             fnBlock.push('}');
14371
14372         fnBlock.push('}})');
14373
14374         function __link__(tag, code, outputs, args)
14375         {
14376             if (!tag || !tag.tag)
14377                 return;
14378
14379             tag.tag.compile();
14380
14381             var tagOutputs = [];
14382             var markupArgs = [code, tag.tag.context, args, tagOutputs];
14383             markupArgs.push.apply(markupArgs, tag.tag.markupArgs);
14384             tag.tag.renderMarkup.apply(tag.tag.subject, markupArgs);
14385
14386             outputs.push(tag);
14387             outputs.push(tagOutputs);
14388         }
14389
14390         function __escape__(value)
14391         {
14392             function replaceChars(ch)
14393             {
14394                 switch (ch)
14395                 {
14396                     case "<":
14397                         return "&lt;";
14398                     case ">":
14399                         return "&gt;";
14400                     case "&":
14401                         return "&amp;";
14402                     case "'":
14403                         return "&#39;";
14404                     case '"':
14405                         return "&quot;";
14406                 }
14407                 return "?";
14408             };
14409             return String(value).replace(/[<>&"']/g, replaceChars);
14410         }
14411
14412         function __loop__(iter, outputs, fn)
14413         {
14414             var iterOuts = [];
14415             outputs.push(iterOuts);
14416
14417             if (iter instanceof Array)
14418                 iter = new ArrayIterator(iter);
14419
14420             try
14421             {
14422                 while (1)
14423                 {
14424                     var value = iter.next();
14425                     var itemOuts = [0,0];
14426                     iterOuts.push(itemOuts);
14427                     fn.apply(this, [value, itemOuts]);
14428                 }
14429             }
14430             catch (exc)
14431             {
14432                 if (exc != StopIteration)
14433                     throw exc;
14434             }
14435         }
14436
14437         var js = fnBlock.join("");
14438         var r = null;
14439         eval(js);
14440         this.renderMarkup = r;
14441     },
14442
14443     getVarNames: function(args)
14444     {
14445         if (this.vars)
14446             args.push.apply(args, this.vars);
14447
14448         for (var i = 0; i < this.children.length; ++i)
14449         {
14450             var child = this.children[i];
14451             if (isTag(child))
14452                 child.tag.getVarNames(args);
14453             else if (child instanceof Parts)
14454             {
14455                 for (var i = 0; i < child.parts.length; ++i)
14456                 {
14457                     if (child.parts[i] instanceof Variable)
14458                     {
14459                         var name = child.parts[i].name;
14460                         var names = name.split(".");
14461                         args.push(names[0]);
14462                     }
14463                 }
14464             }
14465         }
14466     },
14467
14468     generateMarkup: function(topBlock, topOuts, blocks, info)
14469     {
14470         topBlock.push(',"<', this.tagName, '"');
14471
14472         for (var name in this.attrs)
14473         {
14474             if (name != "class")
14475             {
14476                 var val = this.attrs[name];
14477                 topBlock.push(', " ', name, '=\\""');
14478                 addParts(val, ',', topBlock, info, true);
14479                 topBlock.push(', "\\""');
14480             }
14481         }
14482
14483         if (this.listeners)
14484         {
14485             for (var i = 0; i < this.listeners.length; i += 2)
14486                 readPartNames(this.listeners[i+1], topOuts);
14487         }
14488
14489         if (this.props)
14490         {
14491             for (var name in this.props)
14492                 readPartNames(this.props[name], topOuts);
14493         }
14494
14495         if ( this.attrs.hasOwnProperty("class") || this.classes)
14496         {
14497             topBlock.push(', " class=\\""');
14498             if (this.attrs.hasOwnProperty("class"))
14499                 addParts(this.attrs["class"], ',', topBlock, info, true);
14500               topBlock.push(', " "');
14501             for (var name in this.classes)
14502             {
14503                 topBlock.push(', (');
14504                 addParts(this.classes[name], '', topBlock, info);
14505                 topBlock.push(' ? "', name, '" + " " : "")');
14506             }
14507             topBlock.push(', "\\""');
14508         }
14509         topBlock.push(',">"');
14510
14511         this.generateChildMarkup(topBlock, topOuts, blocks, info);
14512         topBlock.push(',"</', this.tagName, '>"');
14513     },
14514
14515     generateChildMarkup: function(topBlock, topOuts, blocks, info)
14516     {
14517         for (var i = 0; i < this.children.length; ++i)
14518         {
14519             var child = this.children[i];
14520             if (isTag(child))
14521                 child.tag.generateMarkup(topBlock, topOuts, blocks, info);
14522             else
14523                 addParts(child, ',', topBlock, info, true);
14524         }
14525     },
14526
14527     addCode: function(topBlock, topOuts, blocks)
14528     {
14529         if (topBlock.length)
14530             blocks.push('__code__.push(""', topBlock.join(""), ');');
14531         if (topOuts.length)
14532             blocks.push('__out__.push(', topOuts.join(","), ');');
14533         topBlock.splice(0, topBlock.length);
14534         topOuts.splice(0, topOuts.length);
14535     },
14536
14537     addLocals: function(blocks)
14538     {
14539         var varNames = [];
14540         this.getVarNames(varNames);
14541
14542         var map = {};
14543         for (var i = 0; i < varNames.length; ++i)
14544         {
14545             var name = varNames[i];
14546             if ( map.hasOwnProperty(name) )
14547                 continue;
14548
14549             map[name] = 1;
14550             var names = name.split(".");
14551             blocks.push('var ', names[0] + ' = ' + '__in__.' + names[0] + ';');
14552         }
14553     },
14554
14555     compileDOM: function()
14556     {
14557         var path = [];
14558         var blocks = [];
14559         this.domArgs = [];
14560         path.embedIndex = 0;
14561         path.loopIndex = 0;
14562         path.staticIndex = 0;
14563         path.renderIndex = 0;
14564         var nodeCount = this.generateDOM(path, blocks, this.domArgs);
14565
14566         var fnBlock = ['r=(function (root, context, o'];
14567
14568         for (var i = 0; i < path.staticIndex; ++i)
14569             fnBlock.push(', ', 's'+i);
14570
14571         for (var i = 0; i < path.renderIndex; ++i)
14572             fnBlock.push(', ', 'd'+i);
14573
14574         fnBlock.push(') {');
14575         for (var i = 0; i < path.loopIndex; ++i)
14576             fnBlock.push('var l', i, ' = 0;');
14577         for (var i = 0; i < path.embedIndex; ++i)
14578             fnBlock.push('var e', i, ' = 0;');
14579
14580         if (this.subject)
14581             fnBlock.push('with (this) {');
14582         if (this.context)
14583             fnBlock.push('with (context) {');
14584
14585         fnBlock.push(blocks.join(""));
14586
14587         if (this.subject)
14588             fnBlock.push('}');
14589         if (this.context)
14590             fnBlock.push('}');
14591
14592         fnBlock.push('return ', nodeCount, ';');
14593         fnBlock.push('})');
14594
14595         function __bind__(object, fn)
14596         {
14597             return function(event) { return fn.apply(object, [event]); };
14598         }
14599
14600         function __link__(node, tag, args)
14601         {
14602             if (!tag || !tag.tag)
14603                 return;
14604
14605             tag.tag.compile();
14606
14607             var domArgs = [node, tag.tag.context, 0];
14608             domArgs.push.apply(domArgs, tag.tag.domArgs);
14609             domArgs.push.apply(domArgs, args);
14610             //if (FBTrace.DBG_DOM) FBTrace.dumpProperties("domplate__link__ domArgs:", domArgs);
14611             return tag.tag.renderDOM.apply(tag.tag.subject, domArgs);
14612         }
14613
14614         var self = this;
14615         function __loop__(iter, fn)
14616         {
14617             var nodeCount = 0;
14618             for (var i = 0; i < iter.length; ++i)
14619             {
14620                 iter[i][0] = i;
14621                 iter[i][1] = nodeCount;
14622                 nodeCount += fn.apply(this, iter[i]);
14623                 //if (FBTrace.DBG_DOM) FBTrace.sysout("nodeCount", nodeCount);
14624             }
14625             return nodeCount;
14626         }
14627
14628         function __path__(parent, offset)
14629         {
14630             //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate __path__ offset: "+ offset+"\n");
14631             var root = parent;
14632
14633             for (var i = 2; i < arguments.length; ++i)
14634             {
14635                 var index = arguments[i];
14636                 if (i == 3)
14637                     index += offset;
14638
14639                 if (index == -1)
14640                     parent = parent.parentNode;
14641                 else
14642                     parent = parent.childNodes[index];
14643             }
14644
14645             //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate: "+arguments[2]+", root: "+ root+", parent: "+ parent+"\n");
14646             return parent;
14647         }
14648
14649         var js = fnBlock.join("");
14650         //if (FBTrace.DBG_DOM) FBTrace.sysout(js.replace(/(\;|\{)/g, "$1\n"));
14651         var r = null;
14652         eval(js);
14653         this.renderDOM = r;
14654     },
14655
14656     generateDOM: function(path, blocks, args)
14657     {
14658         if (this.listeners || this.props)
14659             this.generateNodePath(path, blocks);
14660
14661         if (this.listeners)
14662         {
14663             for (var i = 0; i < this.listeners.length; i += 2)
14664             {
14665                 var val = this.listeners[i+1];
14666                 var arg = generateArg(val, path, args);
14667                 //blocks.push('node.addEventListener("', this.listeners[i], '", __bind__(this, ', arg, '), false);');
14668                 blocks.push('addEvent(node, "', this.listeners[i], '", __bind__(this, ', arg, '), false);');
14669             }
14670         }
14671
14672         if (this.props)
14673         {
14674             for (var name in this.props)
14675             {
14676                 var val = this.props[name];
14677                 var arg = generateArg(val, path, args);
14678                 blocks.push('node.', name, ' = ', arg, ';');
14679             }
14680         }
14681
14682         this.generateChildDOM(path, blocks, args);
14683         return 1;
14684     },
14685
14686     generateNodePath: function(path, blocks)
14687     {
14688         blocks.push("var node = __path__(root, o");
14689         for (var i = 0; i < path.length; ++i)
14690             blocks.push(",", path[i]);
14691         blocks.push(");");
14692     },
14693
14694     generateChildDOM: function(path, blocks, args)
14695     {
14696         path.push(0);
14697         for (var i = 0; i < this.children.length; ++i)
14698         {
14699             var child = this.children[i];
14700             if (isTag(child))
14701                 path[path.length-1] += '+' + child.tag.generateDOM(path, blocks, args);
14702             else
14703                 path[path.length-1] += '+1';
14704         }
14705         path.pop();
14706     }
14707 };
14708
14709 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14710
14711 FBL.DomplateEmbed.prototype = copyObject(FBL.DomplateTag.prototype,
14712 /** @lends FBL.DomplateEmbed.prototype */
14713 {
14714     merge: function(args, oldTag)
14715     {
14716         this.value = oldTag ? oldTag.value : parseValue(args[0]);
14717         this.attrs = oldTag ? oldTag.attrs : {};
14718         this.vars = oldTag ? copyArray(oldTag.vars) : [];
14719
14720         var attrs = args[1];
14721         for (var name in attrs)
14722         {
14723             var val = parseValue(attrs[name]);
14724             this.attrs[name] = val;
14725             readPartNames(val, this.vars);
14726         }
14727
14728         return creator(this, DomplateEmbed);
14729     },
14730
14731     getVarNames: function(names)
14732     {
14733         if (this.value instanceof Parts)
14734             names.push(this.value.parts[0].name);
14735
14736         if (this.vars)
14737             names.push.apply(names, this.vars);
14738     },
14739
14740     generateMarkup: function(topBlock, topOuts, blocks, info)
14741     {
14742         this.addCode(topBlock, topOuts, blocks);
14743
14744         blocks.push('__link__(');
14745         addParts(this.value, '', blocks, info);
14746         blocks.push(', __code__, __out__, {');
14747
14748         var lastName = null;
14749         for (var name in this.attrs)
14750         {
14751             if (lastName)
14752                 blocks.push(',');
14753             lastName = name;
14754
14755             var val = this.attrs[name];
14756             blocks.push('"', name, '":');
14757             addParts(val, '', blocks, info);
14758         }
14759
14760         blocks.push('});');
14761         //this.generateChildMarkup(topBlock, topOuts, blocks, info);
14762     },
14763
14764     generateDOM: function(path, blocks, args)
14765     {
14766         var embedName = 'e'+path.embedIndex++;
14767
14768         this.generateNodePath(path, blocks);
14769
14770         var valueName = 'd' + path.renderIndex++;
14771         var argsName = 'd' + path.renderIndex++;
14772         blocks.push(embedName + ' = __link__(node, ', valueName, ', ', argsName, ');');
14773
14774         return embedName;
14775     }
14776 });
14777
14778 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14779
14780 FBL.DomplateLoop.prototype = copyObject(FBL.DomplateTag.prototype,
14781 /** @lends FBL.DomplateLoop.prototype */
14782 {
14783     merge: function(args, oldTag)
14784     {
14785         this.varName = oldTag ? oldTag.varName : args[0];
14786         this.iter = oldTag ? oldTag.iter : parseValue(args[1]);
14787         this.vars = [];
14788
14789         this.children = oldTag ? copyArray(oldTag.children) : [];
14790
14791         var offset = Math.min(args.length, 2);
14792         parseChildren(args, offset, this.vars, this.children);
14793
14794         return creator(this, DomplateLoop);
14795     },
14796
14797     getVarNames: function(names)
14798     {
14799         if (this.iter instanceof Parts)
14800             names.push(this.iter.parts[0].name);
14801
14802         DomplateTag.prototype.getVarNames.apply(this, [names]);
14803     },
14804
14805     generateMarkup: function(topBlock, topOuts, blocks, info)
14806     {
14807         this.addCode(topBlock, topOuts, blocks);
14808
14809         var iterName;
14810         if (this.iter instanceof Parts)
14811         {
14812             var part = this.iter.parts[0];
14813             iterName = part.name;
14814
14815             if (part.format)
14816             {
14817                 for (var i = 0; i < part.format.length; ++i)
14818                     iterName = part.format[i] + "(" + iterName + ")";
14819             }
14820         }
14821         else
14822             iterName = this.iter;
14823
14824         blocks.push('__loop__.apply(this, [', iterName, ', __out__, function(', this.varName, ', __out__) {');
14825         this.generateChildMarkup(topBlock, topOuts, blocks, info);
14826         this.addCode(topBlock, topOuts, blocks);
14827         blocks.push('}]);');
14828     },
14829
14830     generateDOM: function(path, blocks, args)
14831     {
14832         var iterName = 'd'+path.renderIndex++;
14833         var counterName = 'i'+path.loopIndex;
14834         var loopName = 'l'+path.loopIndex++;
14835
14836         if (!path.length)
14837             path.push(-1, 0);
14838
14839         var preIndex = path.renderIndex;
14840         path.renderIndex = 0;
14841
14842         var nodeCount = 0;
14843
14844         var subBlocks = [];
14845         var basePath = path[path.length-1];
14846         for (var i = 0; i < this.children.length; ++i)
14847         {
14848             path[path.length-1] = basePath+'+'+loopName+'+'+nodeCount;
14849
14850             var child = this.children[i];
14851             if (isTag(child))
14852                 nodeCount += '+' + child.tag.generateDOM(path, subBlocks, args);
14853             else
14854                 nodeCount += '+1';
14855         }
14856
14857         path[path.length-1] = basePath+'+'+loopName;
14858
14859         blocks.push(loopName,' = __loop__.apply(this, [', iterName, ', function(', counterName,',',loopName);
14860         for (var i = 0; i < path.renderIndex; ++i)
14861             blocks.push(',d'+i);
14862         blocks.push(') {');
14863         blocks.push(subBlocks.join(""));
14864         blocks.push('return ', nodeCount, ';');
14865         blocks.push('}]);');
14866
14867         path.renderIndex = preIndex;
14868
14869         return loopName;
14870     }
14871 });
14872
14873 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14874
14875 /** @class */
14876 function Variable(name, format)
14877 {
14878     this.name = name;
14879     this.format = format;
14880 }
14881
14882 /** @class */
14883 function Parts(parts)
14884 {
14885     this.parts = parts;
14886 }
14887
14888 // ************************************************************************************************
14889
14890 function parseParts(str)
14891 {
14892     var re = /\$([_A-Za-z][_A-Za-z0-9.|]*)/g;
14893     var index = 0;
14894     var parts = [];
14895
14896     var m;
14897     while (m = re.exec(str))
14898     {
14899         var pre = str.substr(index, (re.lastIndex-m[0].length)-index);
14900         if (pre)
14901             parts.push(pre);
14902
14903         var expr = m[1].split("|");
14904         parts.push(new Variable(expr[0], expr.slice(1)));
14905         index = re.lastIndex;
14906     }
14907
14908     if (!index)
14909         return str;
14910
14911     var post = str.substr(index);
14912     if (post)
14913         parts.push(post);
14914
14915     return new Parts(parts);
14916 }
14917
14918 function parseValue(val)
14919 {
14920     return typeof(val) == 'string' ? parseParts(val) : val;
14921 }
14922
14923 function parseChildren(args, offset, vars, children)
14924 {
14925     for (var i = offset; i < args.length; ++i)
14926     {
14927         var val = parseValue(args[i]);
14928         children.push(val);
14929         readPartNames(val, vars);
14930     }
14931 }
14932
14933 function readPartNames(val, vars)
14934 {
14935     if (val instanceof Parts)
14936     {
14937         for (var i = 0; i < val.parts.length; ++i)
14938         {
14939             var part = val.parts[i];
14940             if (part instanceof Variable)
14941                 vars.push(part.name);
14942         }
14943     }
14944 }
14945
14946 function generateArg(val, path, args)
14947 {
14948     if (val instanceof Parts)
14949     {
14950         var vals = [];
14951         for (var i = 0; i < val.parts.length; ++i)
14952         {
14953             var part = val.parts[i];
14954             if (part instanceof Variable)
14955             {
14956                 var varName = 'd'+path.renderIndex++;
14957                 if (part.format)
14958                 {
14959                     for (var j = 0; j < part.format.length; ++j)
14960                         varName = part.format[j] + '(' + varName + ')';
14961                 }
14962
14963                 vals.push(varName);
14964             }
14965             else
14966                 vals.push('"'+part.replace(/"/g, '\\"')+'"');
14967         }
14968
14969         return vals.join('+');
14970     }
14971     else
14972     {
14973         args.push(val);
14974         return 's' + path.staticIndex++;
14975     }
14976 }
14977
14978 function addParts(val, delim, block, info, escapeIt)
14979 {
14980     var vals = [];
14981     if (val instanceof Parts)
14982     {
14983         for (var i = 0; i < val.parts.length; ++i)
14984         {
14985             var part = val.parts[i];
14986             if (part instanceof Variable)
14987             {
14988                 var partName = part.name;
14989                 if (part.format)
14990                 {
14991                     for (var j = 0; j < part.format.length; ++j)
14992                         partName = part.format[j] + "(" + partName + ")";
14993                 }
14994
14995                 if (escapeIt)
14996                     vals.push("__escape__(" + partName + ")");
14997                 else
14998                     vals.push(partName);
14999             }
15000             else
15001                 vals.push('"'+ part + '"');
15002         }
15003     }
15004     else if (isTag(val))
15005     {
15006         info.args.push(val);
15007         vals.push('s'+info.argIndex++);
15008     }
15009     else
15010         vals.push('"'+ val + '"');
15011
15012     var parts = vals.join(delim);
15013     if (parts)
15014         block.push(delim, parts);
15015 }
15016
15017 function isTag(obj)
15018 {
15019     return (typeof(obj) == "function" || obj instanceof Function) && !!obj.tag;
15020 }
15021
15022 function creator(tag, cons)
15023 {
15024     var fn = new Function(
15025         "var tag = arguments.callee.tag;" +
15026         "var cons = arguments.callee.cons;" +
15027         "var newTag = new cons();" +
15028         "return newTag.merge(arguments, tag);");
15029
15030     fn.tag = tag;
15031     fn.cons = cons;
15032     extend(fn, Renderer);
15033
15034     return fn;
15035 }
15036
15037 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15038
15039 function copyArray(oldArray)
15040 {
15041     var ary = [];
15042     if (oldArray)
15043         for (var i = 0; i < oldArray.length; ++i)
15044             ary.push(oldArray[i]);
15045    return ary;
15046 }
15047
15048 function copyObject(l, r)
15049 {
15050     var m = {};
15051     extend(m, l);
15052     extend(m, r);
15053     return m;
15054 }
15055
15056 function extend(l, r)
15057 {
15058     for (var n in r)
15059         l[n] = r[n];
15060 }
15061
15062 function addEvent(object, name, handler)
15063 {
15064     if (document.all)
15065         object.attachEvent("on"+name, handler);
15066     else
15067         object.addEventListener(name, handler, false);
15068 }
15069
15070 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15071
15072 /** @class */
15073 function ArrayIterator(array)
15074 {
15075     var index = -1;
15076
15077     this.next = function()
15078     {
15079         if (++index >= array.length)
15080             throw StopIteration;
15081
15082         return array[index];
15083     };
15084 }
15085
15086 /** @class */
15087 function StopIteration() {}
15088
15089 FBL.$break = function()
15090 {
15091     throw StopIteration;
15092 };
15093
15094 // ************************************************************************************************
15095
15096 /** @namespace */
15097 var Renderer =
15098 {
15099     renderHTML: function(args, outputs, self)
15100     {
15101         var code = [];
15102         var markupArgs = [code, this.tag.context, args, outputs];
15103         markupArgs.push.apply(markupArgs, this.tag.markupArgs);
15104         this.tag.renderMarkup.apply(self ? self : this.tag.subject, markupArgs);
15105         return code.join("");
15106     },
15107
15108     insertRows: function(args, before, self)
15109     {
15110         this.tag.compile();
15111
15112         var outputs = [];
15113         var html = this.renderHTML(args, outputs, self);
15114
15115         var doc = before.ownerDocument;
15116         var div = doc.createElement("div");
15117         div.innerHTML = "<table><tbody>"+html+"</tbody></table>";
15118
15119         var tbody = div.firstChild.firstChild;
15120         var parent = before.tagName == "TR" ? before.parentNode : before;
15121         var after = before.tagName == "TR" ? before.nextSibling : null;
15122
15123         var firstRow = tbody.firstChild, lastRow;
15124         while (tbody.firstChild)
15125         {
15126             lastRow = tbody.firstChild;
15127             if (after)
15128                 parent.insertBefore(lastRow, after);
15129             else
15130                 parent.appendChild(lastRow);
15131         }
15132
15133         var offset = 0;
15134         if (before.tagName == "TR")
15135         {
15136             var node = firstRow.parentNode.firstChild;
15137             for (; node && node != firstRow; node = node.nextSibling)
15138                 ++offset;
15139         }
15140
15141         var domArgs = [firstRow, this.tag.context, offset];
15142         domArgs.push.apply(domArgs, this.tag.domArgs);
15143         domArgs.push.apply(domArgs, outputs);
15144
15145         this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
15146         return [firstRow, lastRow];
15147     },
15148
15149     insertBefore: function(args, before, self)
15150     {
15151         return this.insertNode(args, before.ownerDocument, before, false, self);
15152     },
15153
15154     insertAfter: function(args, after, self)
15155     {
15156         return this.insertNode(args, after.ownerDocument, after, true, self);
15157     },
15158
15159     insertNode: function(args, doc, element, isAfter, self)
15160     {
15161         if (!args)
15162             args = {};
15163
15164         this.tag.compile();
15165
15166         var outputs = [];
15167         var html = this.renderHTML(args, outputs, self);
15168
15169         //if (FBTrace.DBG_DOM)
15170         //    FBTrace.sysout("domplate.insertNode html: "+html+"\n");
15171
15172         var doc = element.ownerDocument;
15173         if (!womb || womb.ownerDocument != doc)
15174             womb = doc.createElement("div");
15175
15176         womb.innerHTML = html;
15177
15178         var root = womb.firstChild;
15179         if (isAfter)
15180         {
15181             while (womb.firstChild)
15182                 if (element.nextSibling)
15183                     element.parentNode.insertBefore(womb.firstChild, element.nextSibling);
15184                 else
15185                     element.parentNode.appendChild(womb.firstChild);
15186         }
15187         else
15188         {
15189             while (womb.lastChild)
15190                 element.parentNode.insertBefore(womb.lastChild, element);
15191         }
15192
15193         var domArgs = [root, this.tag.context, 0];
15194         domArgs.push.apply(domArgs, this.tag.domArgs);
15195         domArgs.push.apply(domArgs, outputs);
15196
15197         //if (FBTrace.DBG_DOM)
15198         //    FBTrace.sysout("domplate.insertNode domArgs:", domArgs);
15199         this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
15200
15201         return root;
15202     },
15203     /**/
15204
15205     /*
15206     insertAfter: function(args, before, self)
15207     {
15208         this.tag.compile();
15209
15210         var outputs = [];
15211         var html = this.renderHTML(args, outputs, self);
15212
15213         var doc = before.ownerDocument;
15214         if (!womb || womb.ownerDocument != doc)
15215             womb = doc.createElement("div");
15216
15217         womb.innerHTML = html;
15218
15219         var root = womb.firstChild;
15220         while (womb.firstChild)
15221             if (before.nextSibling)
15222                 before.parentNode.insertBefore(womb.firstChild, before.nextSibling);
15223             else
15224                 before.parentNode.appendChild(womb.firstChild);
15225
15226         var domArgs = [root, this.tag.context, 0];
15227         domArgs.push.apply(domArgs, this.tag.domArgs);
15228         domArgs.push.apply(domArgs, outputs);
15229
15230         this.tag.renderDOM.apply(self ? self : (this.tag.subject ? this.tag.subject : null),
15231             domArgs);
15232
15233         return root;
15234     },
15235     /**/
15236
15237     replace: function(args, parent, self)
15238     {
15239         this.tag.compile();
15240
15241         var outputs = [];
15242         var html = this.renderHTML(args, outputs, self);
15243
15244         var root;
15245         if (parent.nodeType == 1)
15246         {
15247             parent.innerHTML = html;
15248             root = parent.firstChild;
15249         }
15250         else
15251         {
15252             if (!parent || parent.nodeType != 9)
15253                 parent = document;
15254
15255             if (!womb || womb.ownerDocument != parent)
15256                 womb = parent.createElement("div");
15257             womb.innerHTML = html;
15258
15259             root = womb.firstChild;
15260             //womb.removeChild(root);
15261         }
15262
15263         var domArgs = [root, this.tag.context, 0];
15264         domArgs.push.apply(domArgs, this.tag.domArgs);
15265         domArgs.push.apply(domArgs, outputs);
15266         this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
15267
15268         return root;
15269     },
15270
15271     append: function(args, parent, self)
15272     {
15273         this.tag.compile();
15274
15275         var outputs = [];
15276         var html = this.renderHTML(args, outputs, self);
15277         //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate.append html: "+html+"\n");
15278
15279         if (!womb || womb.ownerDocument != parent.ownerDocument)
15280             womb = parent.ownerDocument.createElement("div");
15281         womb.innerHTML = html;
15282
15283         // TODO: xxxpedro domplate port to Firebug
15284         var root = womb.firstChild;
15285         while (womb.firstChild)
15286             parent.appendChild(womb.firstChild);
15287
15288         // clearing element reference to avoid reference error in IE8 when switching contexts
15289         womb = null;
15290
15291         var domArgs = [root, this.tag.context, 0];
15292         domArgs.push.apply(domArgs, this.tag.domArgs);
15293         domArgs.push.apply(domArgs, outputs);
15294
15295         //if (FBTrace.DBG_DOM) FBTrace.dumpProperties("domplate append domArgs:", domArgs);
15296         this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
15297
15298         return root;
15299     }
15300 };
15301
15302 // ************************************************************************************************
15303
15304 function defineTags()
15305 {
15306     for (var i = 0; i < arguments.length; ++i)
15307     {
15308         var tagName = arguments[i];
15309         var fn = new Function("var newTag = new arguments.callee.DomplateTag('"+tagName+"'); return newTag.merge(arguments);");
15310         fn.DomplateTag = DomplateTag;
15311
15312         var fnName = tagName.toUpperCase();
15313         FBL[fnName] = fn;
15314     }
15315 }
15316
15317 defineTags(
15318     "a", "button", "br", "canvas", "code", "col", "colgroup", "div", "fieldset", "form", "h1", "h2", "h3", "hr",
15319      "img", "input", "label", "legend", "li", "ol", "optgroup", "option", "p", "pre", "select",
15320     "span", "strong", "table", "tbody", "td", "textarea", "tfoot", "th", "thead", "tr", "tt", "ul", "iframe"
15321 );
15322
15323 })();
15324
15325
15326 /* See license.txt for terms of usage */
15327
15328 var FirebugReps = FBL.ns(function() { with (FBL) {
15329
15330
15331 // ************************************************************************************************
15332 // Common Tags
15333
15334 var OBJECTBOX = this.OBJECTBOX =
15335     SPAN({"class": "objectBox objectBox-$className"});
15336
15337 var OBJECTBLOCK = this.OBJECTBLOCK =
15338     DIV({"class": "objectBox objectBox-$className"});
15339
15340 var OBJECTLINK = this.OBJECTLINK = isIE6 ? // IE6 object link representation
15341     A({
15342         "class": "objectLink objectLink-$className a11yFocus",
15343         href: "javascript:void(0)",
15344         // workaround to show XPath (a better approach would use the tooltip on mouseover,
15345         // so the XPath information would be calculated dynamically, but we need to create
15346         // a tooltip class/wrapper around Menu or InfoTip)
15347         title: "$object|FBL.getElementXPath",
15348         _repObject: "$object"
15349     })
15350     : // Other browsers
15351     A({
15352         "class": "objectLink objectLink-$className a11yFocus",
15353         // workaround to show XPath (a better approach would use the tooltip on mouseover,
15354         // so the XPath information would be calculated dynamically, but we need to create
15355         // a tooltip class/wrapper around Menu or InfoTip)
15356         title: "$object|FBL.getElementXPath",
15357         _repObject: "$object"
15358     });
15359
15360
15361 // ************************************************************************************************
15362
15363 this.Undefined = domplate(Firebug.Rep,
15364 {
15365     tag: OBJECTBOX("undefined"),
15366
15367     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15368
15369     className: "undefined",
15370
15371     supportsObject: function(object, type)
15372     {
15373         return type == "undefined";
15374     }
15375 });
15376
15377 // ************************************************************************************************
15378
15379 this.Null = domplate(Firebug.Rep,
15380 {
15381     tag: OBJECTBOX("null"),
15382
15383     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15384
15385     className: "null",
15386
15387     supportsObject: function(object, type)
15388     {
15389         return object == null;
15390     }
15391 });
15392
15393 // ************************************************************************************************
15394
15395 this.Nada = domplate(Firebug.Rep,
15396 {
15397     tag: SPAN(""),
15398
15399     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15400
15401     className: "nada"
15402 });
15403
15404 // ************************************************************************************************
15405
15406 this.Number = domplate(Firebug.Rep,
15407 {
15408     tag: OBJECTBOX("$object"),
15409
15410     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15411
15412     className: "number",
15413
15414     supportsObject: function(object, type)
15415     {
15416         return type == "boolean" || type == "number";
15417     }
15418 });
15419
15420 // ************************************************************************************************
15421
15422 this.String = domplate(Firebug.Rep,
15423 {
15424     tag: OBJECTBOX("&quot;$object&quot;"),
15425
15426     shortTag: OBJECTBOX("&quot;$object|cropString&quot;"),
15427
15428     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15429
15430     className: "string",
15431
15432     supportsObject: function(object, type)
15433     {
15434         return type == "string";
15435     }
15436 });
15437
15438 // ************************************************************************************************
15439
15440 this.Text = domplate(Firebug.Rep,
15441 {
15442     tag: OBJECTBOX("$object"),
15443
15444     shortTag: OBJECTBOX("$object|cropString"),
15445
15446     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15447
15448     className: "text"
15449 });
15450
15451 // ************************************************************************************************
15452
15453 this.Caption = domplate(Firebug.Rep,
15454 {
15455     tag: SPAN({"class": "caption"}, "$object")
15456 });
15457
15458 // ************************************************************************************************
15459
15460 this.Warning = domplate(Firebug.Rep,
15461 {
15462     tag: DIV({"class": "warning focusRow", role : 'listitem'}, "$object|STR")
15463 });
15464
15465 // ************************************************************************************************
15466
15467 this.Func = domplate(Firebug.Rep,
15468 {
15469     tag:
15470         OBJECTLINK("$object|summarizeFunction"),
15471
15472     summarizeFunction: function(fn)
15473     {
15474         var fnRegex = /function ([^(]+\([^)]*\)) \{/;
15475         var fnText = safeToString(fn);
15476
15477         var m = fnRegex.exec(fnText);
15478         return m ? m[1] : "function()";
15479     },
15480
15481     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15482
15483     copySource: function(fn)
15484     {
15485         copyToClipboard(safeToString(fn));
15486     },
15487
15488     monitor: function(fn, script, monitored)
15489     {
15490         if (monitored)
15491             Firebug.Debugger.unmonitorScript(fn, script, "monitor");
15492         else
15493             Firebug.Debugger.monitorScript(fn, script, "monitor");
15494     },
15495
15496     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15497
15498     className: "function",
15499
15500     supportsObject: function(object, type)
15501     {
15502         return isFunction(object);
15503     },
15504
15505     inspectObject: function(fn, context)
15506     {
15507         var sourceLink = findSourceForFunction(fn, context);
15508         if (sourceLink)
15509             Firebug.chrome.select(sourceLink);
15510         if (FBTrace.DBG_FUNCTION_NAME)
15511             FBTrace.sysout("reps.function.inspectObject selected sourceLink is ", sourceLink);
15512     },
15513
15514     getTooltip: function(fn, context)
15515     {
15516         var script = findScriptForFunctionInContext(context, fn);
15517         if (script)
15518             return $STRF("Line", [normalizeURL(script.fileName), script.baseLineNumber]);
15519         else
15520             if (fn.toString)
15521                 return fn.toString();
15522     },
15523
15524     getTitle: function(fn, context)
15525     {
15526         var name = fn.name ? fn.name : "function";
15527         return name + "()";
15528     },
15529
15530     getContextMenuItems: function(fn, target, context, script)
15531     {
15532         if (!script)
15533             script = findScriptForFunctionInContext(context, fn);
15534         if (!script)
15535             return;
15536
15537         var scriptInfo = getSourceFileAndLineByScript(context, script);
15538         var monitored = scriptInfo ? fbs.isMonitored(scriptInfo.sourceFile.href, scriptInfo.lineNo) : false;
15539
15540         var name = script ? getFunctionName(script, context) : fn.name;
15541         return [
15542             {label: "CopySource", command: bindFixed(this.copySource, this, fn) },
15543             "-",
15544             {label: $STRF("ShowCallsInConsole", [name]), nol10n: true,
15545              type: "checkbox", checked: monitored,
15546              command: bindFixed(this.monitor, this, fn, script, monitored) }
15547         ];
15548     }
15549 });
15550
15551 // ************************************************************************************************
15552 /*
15553 this.jsdScript = domplate(Firebug.Rep,
15554 {
15555     copySource: function(script)
15556     {
15557         var fn = script.functionObject.getWrappedValue();
15558         return FirebugReps.Func.copySource(fn);
15559     },
15560
15561     monitor: function(fn, script, monitored)
15562     {
15563         fn = script.functionObject.getWrappedValue();
15564         return FirebugReps.Func.monitor(fn, script, monitored);
15565     },
15566
15567     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15568
15569     className: "jsdScript",
15570     inspectable: false,
15571
15572     supportsObject: function(object, type)
15573     {
15574         return object instanceof jsdIScript;
15575     },
15576
15577     inspectObject: function(script, context)
15578     {
15579         var sourceLink = getSourceLinkForScript(script, context);
15580         if (sourceLink)
15581             Firebug.chrome.select(sourceLink);
15582     },
15583
15584     getRealObject: function(script, context)
15585     {
15586         return script;
15587     },
15588
15589     getTooltip: function(script)
15590     {
15591         return $STRF("jsdIScript", [script.tag]);
15592     },
15593
15594     getTitle: function(script, context)
15595     {
15596         var fn = script.functionObject.getWrappedValue();
15597         return FirebugReps.Func.getTitle(fn, context);
15598     },
15599
15600     getContextMenuItems: function(script, target, context)
15601     {
15602         var fn = script.functionObject.getWrappedValue();
15603
15604         var scriptInfo = getSourceFileAndLineByScript(context, script);
15605            var monitored = scriptInfo ? fbs.isMonitored(scriptInfo.sourceFile.href, scriptInfo.lineNo) : false;
15606
15607         var name = getFunctionName(script, context);
15608
15609         return [
15610             {label: "CopySource", command: bindFixed(this.copySource, this, script) },
15611             "-",
15612             {label: $STRF("ShowCallsInConsole", [name]), nol10n: true,
15613              type: "checkbox", checked: monitored,
15614              command: bindFixed(this.monitor, this, fn, script, monitored) }
15615         ];
15616     }
15617 });
15618 /**/
15619 //************************************************************************************************
15620
15621 this.Obj = domplate(Firebug.Rep,
15622 {
15623     tag:
15624         OBJECTLINK(
15625             SPAN({"class": "objectTitle"}, "$object|getTitle "),
15626
15627             SPAN({"class": "objectProps"},
15628                 SPAN({"class": "objectLeftBrace", role: "presentation"}, "{"),
15629                 FOR("prop", "$object|propIterator",
15630                     SPAN({"class": "objectPropName", role: "presentation"}, "$prop.name"),
15631                     SPAN({"class": "objectEqual", role: "presentation"}, "$prop.equal"),
15632                     TAG("$prop.tag", {object: "$prop.object"}),
15633                     SPAN({"class": "objectComma", role: "presentation"}, "$prop.delim")
15634                 ),
15635                 SPAN({"class": "objectRightBrace"}, "}")
15636             )
15637         ),
15638
15639     propNumberTag:
15640         SPAN({"class": "objectProp-number"}, "$object"),
15641
15642     propStringTag:
15643         SPAN({"class": "objectProp-string"}, "&quot;$object&quot;"),
15644
15645     propObjectTag:
15646         SPAN({"class": "objectProp-object"}, "$object"),
15647
15648     propIterator: function (object)
15649     {
15650         ///Firebug.ObjectShortIteratorMax;
15651         var maxLength = 55; // default max length for long representation
15652
15653         if (!object)
15654             return [];
15655
15656         var props = [];
15657         var length = 0;
15658
15659         var numProperties = 0;
15660         var numPropertiesShown = 0;
15661         var maxLengthReached = false;
15662
15663         var lib = this;
15664
15665         var propRepsMap =
15666         {
15667             "boolean": this.propNumberTag,
15668             "number": this.propNumberTag,
15669             "string": this.propStringTag,
15670             "object": this.propObjectTag
15671         };
15672
15673         try
15674         {
15675             var title = Firebug.Rep.getTitle(object);
15676             length += title.length;
15677
15678             for (var name in object)
15679             {
15680                 var value;
15681                 try
15682                 {
15683                     value = object[name];
15684                 }
15685                 catch (exc)
15686                 {
15687                     continue;
15688                 }
15689
15690                 var type = typeof(value);
15691                 if (type == "boolean" ||
15692                     type == "number" ||
15693                     (type == "string" && value) ||
15694                     (type == "object" && value && value.toString))
15695                 {
15696                     var tag = propRepsMap[type];
15697
15698                     var value = (type == "object") ?
15699                         Firebug.getRep(value).getTitle(value) :
15700                         value + "";
15701
15702                     length += name.length + value.length + 4;
15703
15704                     if (length <= maxLength)
15705                     {
15706                         props.push({
15707                             tag: tag,
15708                             name: name,
15709                             object: value,
15710                             equal: "=",
15711                             delim: ", "
15712                         });
15713
15714                         numPropertiesShown++;
15715                     }
15716                     else
15717                         maxLengthReached = true;
15718
15719                 }
15720
15721                 numProperties++;
15722
15723                 if (maxLengthReached && numProperties > numPropertiesShown)
15724                     break;
15725             }
15726
15727             if (numProperties > numPropertiesShown)
15728             {
15729                 props.push({
15730                     object: "...", //xxxHonza localization
15731                     tag: FirebugReps.Caption.tag,
15732                     name: "",
15733                     equal:"",
15734                     delim:""
15735                 });
15736             }
15737             else if (props.length > 0)
15738             {
15739                 props[props.length-1].delim = '';
15740             }
15741         }
15742         catch (exc)
15743         {
15744             // Sometimes we get exceptions when trying to read from certain objects, like
15745             // StorageList, but don't let that gum up the works
15746             // XXXjjb also History.previous fails because object is a web-page object which does not have
15747             // permission to read the history
15748         }
15749         return props;
15750     },
15751
15752     fb_1_6_propIterator: function (object, max)
15753     {
15754         max = max || 3;
15755         if (!object)
15756             return [];
15757
15758         var props = [];
15759         var len = 0, count = 0;
15760
15761         try
15762         {
15763             for (var name in object)
15764             {
15765                 var value;
15766                 try
15767                 {
15768                     value = object[name];
15769                 }
15770                 catch (exc)
15771                 {
15772                     continue;
15773                 }
15774
15775                 var t = typeof(value);
15776                 if (t == "boolean" || t == "number" || (t == "string" && value)
15777                     || (t == "object" && value && value.toString))
15778                 {
15779                     var rep = Firebug.getRep(value);
15780                     var tag = rep.shortTag || rep.tag;
15781                     if (t == "object")
15782                     {
15783                         value = rep.getTitle(value);
15784                         tag = rep.titleTag;
15785                     }
15786                     count++;
15787                     if (count <= max)
15788                         props.push({tag: tag, name: name, object: value, equal: "=", delim: ", "});
15789                     else
15790                         break;
15791                 }
15792             }
15793             if (count > max)
15794             {
15795                 props[Math.max(1,max-1)] = {
15796                     object: "more...", //xxxHonza localization
15797                     tag: FirebugReps.Caption.tag,
15798                     name: "",
15799                     equal:"",
15800                     delim:""
15801                 };
15802             }
15803             else if (props.length > 0)
15804             {
15805                 props[props.length-1].delim = '';
15806             }
15807         }
15808         catch (exc)
15809         {
15810             // Sometimes we get exceptions when trying to read from certain objects, like
15811             // StorageList, but don't let that gum up the works
15812             // XXXjjb also History.previous fails because object is a web-page object which does not have
15813             // permission to read the history
15814         }
15815         return props;
15816     },
15817
15818     /*
15819     propIterator: function (object)
15820     {
15821         if (!object)
15822             return [];
15823
15824         var props = [];
15825         var len = 0;
15826
15827         try
15828         {
15829             for (var name in object)
15830             {
15831                 var val;
15832                 try
15833                 {
15834                     val = object[name];
15835                 }
15836                 catch (exc)
15837                 {
15838                     continue;
15839                 }
15840
15841                 var t = typeof val;
15842                 if (t == "boolean" || t == "number" || (t == "string" && val)
15843                     || (t == "object" && !isFunction(val) && val && val.toString))
15844                 {
15845                     var title = (t == "object")
15846                         ? Firebug.getRep(val).getTitle(val)
15847                         : val+"";
15848
15849                     len += name.length + title.length + 1;
15850                     if (len < 50)
15851                         props.push({name: name, value: title});
15852                     else
15853                         break;
15854                 }
15855             }
15856         }
15857         catch (exc)
15858         {
15859             // Sometimes we get exceptions when trying to read from certain objects, like
15860             // StorageList, but don't let that gum up the works
15861             // XXXjjb also History.previous fails because object is a web-page object which does not have
15862             // permission to read the history
15863         }
15864
15865         return props;
15866     },
15867     /**/
15868
15869     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15870
15871     className: "object",
15872
15873     supportsObject: function(object, type)
15874     {
15875         return true;
15876     }
15877 });
15878
15879
15880 // ************************************************************************************************
15881
15882 this.Arr = domplate(Firebug.Rep,
15883 {
15884     tag:
15885         OBJECTBOX({_repObject: "$object"},
15886             SPAN({"class": "arrayLeftBracket", role : "presentation"}, "["),
15887             FOR("item", "$object|arrayIterator",
15888                 TAG("$item.tag", {object: "$item.object"}),
15889                 SPAN({"class": "arrayComma", role : "presentation"}, "$item.delim")
15890             ),
15891             SPAN({"class": "arrayRightBracket", role : "presentation"}, "]")
15892         ),
15893
15894     shortTag:
15895         OBJECTBOX({_repObject: "$object"},
15896             SPAN({"class": "arrayLeftBracket", role : "presentation"}, "["),
15897             FOR("item", "$object|shortArrayIterator",
15898                 TAG("$item.tag", {object: "$item.object"}),
15899                 SPAN({"class": "arrayComma", role : "presentation"}, "$item.delim")
15900             ),
15901             // TODO: xxxpedro - confirm this on Firebug
15902             //FOR("prop", "$object|shortPropIterator",
15903             //        " $prop.name=",
15904             //        SPAN({"class": "objectPropValue"}, "$prop.value|cropString")
15905             //),
15906             SPAN({"class": "arrayRightBracket"}, "]")
15907         ),
15908
15909     arrayIterator: function(array)
15910     {
15911         var items = [];
15912         for (var i = 0; i < array.length; ++i)
15913         {
15914             var value = array[i];
15915             var rep = Firebug.getRep(value);
15916             var tag = rep.shortTag ? rep.shortTag : rep.tag;
15917             var delim = (i == array.length-1 ? "" : ", ");
15918
15919             items.push({object: value, tag: tag, delim: delim});
15920         }
15921
15922         return items;
15923     },
15924
15925     shortArrayIterator: function(array)
15926     {
15927         var items = [];
15928         for (var i = 0; i < array.length && i < 3; ++i)
15929         {
15930             var value = array[i];
15931             var rep = Firebug.getRep(value);
15932             var tag = rep.shortTag ? rep.shortTag : rep.tag;
15933             var delim = (i == array.length-1 ? "" : ", ");
15934
15935             items.push({object: value, tag: tag, delim: delim});
15936         }
15937
15938         if (array.length > 3)
15939             items.push({object: (array.length-3) + " more...", tag: FirebugReps.Caption.tag, delim: ""});
15940
15941         return items;
15942     },
15943
15944     shortPropIterator:    this.Obj.propIterator,
15945
15946     getItemIndex: function(child)
15947     {
15948         var arrayIndex = 0;
15949         for (child = child.previousSibling; child; child = child.previousSibling)
15950         {
15951             if (child.repObject)
15952                 ++arrayIndex;
15953         }
15954         return arrayIndex;
15955     },
15956
15957     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15958
15959     className: "array",
15960
15961     supportsObject: function(object)
15962     {
15963         return this.isArray(object);
15964     },
15965
15966     // http://code.google.com/p/fbug/issues/detail?id=874
15967     // BEGIN Yahoo BSD Source (modified here)  YAHOO.lang.isArray, YUI 2.2.2 June 2007
15968     isArray: function(obj) {
15969         try {
15970             if (!obj)
15971                 return false;
15972             else if (isIE && !isFunction(obj) && typeof obj == "object" && isFinite(obj.length) && obj.nodeType != 8)
15973                 return true;
15974             else if (isFinite(obj.length) && isFunction(obj.splice))
15975                 return true;
15976             else if (isFinite(obj.length) && isFunction(obj.callee)) // arguments
15977                 return true;
15978             else if (instanceOf(obj, "HTMLCollection"))
15979                 return true;
15980             else if (instanceOf(obj, "NodeList"))
15981                 return true;
15982             else
15983                 return false;
15984         }
15985         catch(exc)
15986         {
15987             if (FBTrace.DBG_ERRORS)
15988             {
15989                 FBTrace.sysout("isArray FAILS:", exc);  /* Something weird: without the try/catch, OOM, with no exception?? */
15990                 FBTrace.sysout("isArray Fails on obj", obj);
15991             }
15992         }
15993
15994         return false;
15995     },
15996     // END Yahoo BSD SOURCE See license below.
15997
15998     getTitle: function(object, context)
15999     {
16000         return "[" + object.length + "]";
16001     }
16002 });
16003
16004 // ************************************************************************************************
16005
16006 this.Property = domplate(Firebug.Rep,
16007 {
16008     supportsObject: function(object)
16009     {
16010         return object instanceof Property;
16011     },
16012
16013     getRealObject: function(prop, context)
16014     {
16015         return prop.object[prop.name];
16016     },
16017
16018     getTitle: function(prop, context)
16019     {
16020         return prop.name;
16021     }
16022 });
16023
16024 // ************************************************************************************************
16025
16026 this.NetFile = domplate(this.Obj,
16027 {
16028     supportsObject: function(object)
16029     {
16030         return object instanceof Firebug.NetFile;
16031     },
16032
16033     browseObject: function(file, context)
16034     {
16035         openNewTab(file.href);
16036         return true;
16037     },
16038
16039     getRealObject: function(file, context)
16040     {
16041         return null;
16042     }
16043 });
16044
16045 // ************************************************************************************************
16046
16047 this.Except = domplate(Firebug.Rep,
16048 {
16049     tag:
16050         OBJECTBOX({_repObject: "$object"}, "$object.message"),
16051
16052     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16053
16054     className: "exception",
16055
16056     supportsObject: function(object)
16057     {
16058         return object instanceof ErrorCopy;
16059     }
16060 });
16061
16062
16063 // ************************************************************************************************
16064
16065 this.Element = domplate(Firebug.Rep,
16066 {
16067     tag:
16068         OBJECTLINK(
16069             "&lt;",
16070             SPAN({"class": "nodeTag"}, "$object.nodeName|toLowerCase"),
16071             FOR("attr", "$object|attrIterator",
16072                 "&nbsp;$attr.nodeName=&quot;", SPAN({"class": "nodeValue"}, "$attr.nodeValue"), "&quot;"
16073             ),
16074             "&gt;"
16075          ),
16076
16077     shortTag:
16078         OBJECTLINK(
16079             SPAN({"class": "$object|getVisible"},
16080                 SPAN({"class": "selectorTag"}, "$object|getSelectorTag"),
16081                 SPAN({"class": "selectorId"}, "$object|getSelectorId"),
16082                 SPAN({"class": "selectorClass"}, "$object|getSelectorClass"),
16083                 SPAN({"class": "selectorValue"}, "$object|getValue")
16084             )
16085          ),
16086
16087      getVisible: function(elt)
16088      {
16089          return isVisible(elt) ? "" : "selectorHidden";
16090      },
16091
16092      getSelectorTag: function(elt)
16093      {
16094          return elt.nodeName.toLowerCase();
16095      },
16096
16097      getSelectorId: function(elt)
16098      {
16099          return elt.id ? "#" + elt.id : "";
16100      },
16101
16102      getSelectorClass: function(elt)
16103      {
16104          return elt.className ? "." + elt.className.split(" ")[0] : "";
16105      },
16106
16107      getValue: function(elt)
16108      {
16109          // TODO: xxxpedro
16110          return "";
16111          var value;
16112          if (elt instanceof HTMLImageElement)
16113              value = getFileName(elt.src);
16114          else if (elt instanceof HTMLAnchorElement)
16115              value = getFileName(elt.href);
16116          else if (elt instanceof HTMLInputElement)
16117              value = elt.value;
16118          else if (elt instanceof HTMLFormElement)
16119              value = getFileName(elt.action);
16120          else if (elt instanceof HTMLScriptElement)
16121              value = getFileName(elt.src);
16122
16123          return value ? " " + cropString(value, 20) : "";
16124      },
16125
16126      attrIterator: function(elt)
16127      {
16128          var attrs = [];
16129          var idAttr, classAttr;
16130          if (elt.attributes)
16131          {
16132              for (var i = 0; i < elt.attributes.length; ++i)
16133              {
16134                  var attr = elt.attributes[i];
16135
16136                  // we must check if the attribute is specified otherwise IE will show them
16137                  if (!attr.specified || attr.nodeName && attr.nodeName.indexOf("firebug-") != -1)
16138                     continue;
16139                  else if (attr.nodeName == "id")
16140                     idAttr = attr;
16141                  else if (attr.nodeName == "class")
16142                     classAttr = attr;
16143                  else if (attr.nodeName == "style")
16144                     attrs.push({
16145                         nodeName: attr.nodeName,
16146                         nodeValue: attr.nodeValue ||
16147                         // IE won't recognize the attr.nodeValue of <style> nodes ...
16148                         // and will return CSS property names in upper case, so we need to convert them
16149                         elt.style.cssText.replace(/([^\s]+)\s*:/g,
16150                                 function(m,g){return g.toLowerCase()+":"})
16151                     });
16152                  else
16153                     attrs.push(attr);
16154              }
16155          }
16156          if (classAttr)
16157             attrs.splice(0, 0, classAttr);
16158          if (idAttr)
16159             attrs.splice(0, 0, idAttr);
16160
16161          return attrs;
16162      },
16163
16164      shortAttrIterator: function(elt)
16165      {
16166          var attrs = [];
16167          if (elt.attributes)
16168          {
16169              for (var i = 0; i < elt.attributes.length; ++i)
16170              {
16171                  var attr = elt.attributes[i];
16172                  if (attr.nodeName == "id" || attr.nodeName == "class")
16173                      attrs.push(attr);
16174              }
16175          }
16176
16177          return attrs;
16178      },
16179
16180      getHidden: function(elt)
16181      {
16182          return isVisible(elt) ? "" : "nodeHidden";
16183      },
16184
16185      getXPath: function(elt)
16186      {
16187          return getElementTreeXPath(elt);
16188      },
16189
16190      // TODO: xxxpedro remove this?
16191      getNodeText: function(element)
16192      {
16193          var text = element.textContent;
16194          if (Firebug.showFullTextNodes)
16195             return text;
16196         else
16197             return cropString(text, 50);
16198      },
16199      /**/
16200
16201      getNodeTextGroups: function(element)
16202      {
16203          var text =  element.textContent;
16204          if (!Firebug.showFullTextNodes)
16205          {
16206              text=cropString(text,50);
16207          }
16208
16209          var escapeGroups=[];
16210
16211          if (Firebug.showTextNodesWithWhitespace)
16212              escapeGroups.push({
16213                 'group': 'whitespace',
16214                 'class': 'nodeWhiteSpace',
16215                 'extra': {
16216                     '\t': '_Tab',
16217                     '\n': '_Para',
16218                     ' ' : '_Space'
16219                 }
16220              });
16221          if (Firebug.showTextNodesWithEntities)
16222              escapeGroups.push({
16223                  'group':'text',
16224                  'class':'nodeTextEntity',
16225                  'extra':{}
16226              });
16227
16228          if (escapeGroups.length)
16229              return escapeGroupsForEntities(text, escapeGroups);
16230          else
16231              return [{str:text,'class':'',extra:''}];
16232      },
16233
16234     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16235
16236     copyHTML: function(elt)
16237     {
16238         var html = getElementXML(elt);
16239         copyToClipboard(html);
16240     },
16241
16242     copyInnerHTML: function(elt)
16243     {
16244         copyToClipboard(elt.innerHTML);
16245     },
16246
16247     copyXPath: function(elt)
16248     {
16249         var xpath = getElementXPath(elt);
16250         copyToClipboard(xpath);
16251     },
16252
16253     persistor: function(context, xpath)
16254     {
16255         var elts = xpath
16256             ? getElementsByXPath(context.window.document, xpath)
16257             : null;
16258
16259         return elts && elts.length ? elts[0] : null;
16260     },
16261
16262     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16263
16264     className: "element",
16265
16266     supportsObject: function(object)
16267     {
16268         //return object instanceof Element || object.nodeType == 1 && typeof object.nodeName == "string";
16269         return instanceOf(object, "Element");
16270     },
16271
16272     browseObject: function(elt, context)
16273     {
16274         var tag = elt.nodeName.toLowerCase();
16275         if (tag == "script")
16276             openNewTab(elt.src);
16277         else if (tag == "link")
16278             openNewTab(elt.href);
16279         else if (tag == "a")
16280             openNewTab(elt.href);
16281         else if (tag == "img")
16282             openNewTab(elt.src);
16283
16284         return true;
16285     },
16286
16287     persistObject: function(elt, context)
16288     {
16289         var xpath = getElementXPath(elt);
16290
16291         return bind(this.persistor, top, xpath);
16292     },
16293
16294     getTitle: function(element, context)
16295     {
16296         return getElementCSSSelector(element);
16297     },
16298
16299     getTooltip: function(elt)
16300     {
16301         return this.getXPath(elt);
16302     },
16303
16304     getContextMenuItems: function(elt, target, context)
16305     {
16306         var monitored = areEventsMonitored(elt, null, context);
16307
16308         return [
16309             {label: "CopyHTML", command: bindFixed(this.copyHTML, this, elt) },
16310             {label: "CopyInnerHTML", command: bindFixed(this.copyInnerHTML, this, elt) },
16311             {label: "CopyXPath", command: bindFixed(this.copyXPath, this, elt) },
16312             "-",
16313             {label: "ShowEventsInConsole", type: "checkbox", checked: monitored,
16314              command: bindFixed(toggleMonitorEvents, FBL, elt, null, monitored, context) },
16315             "-",
16316             {label: "ScrollIntoView", command: bindFixed(elt.scrollIntoView, elt) }
16317         ];
16318     }
16319 });
16320
16321 // ************************************************************************************************
16322
16323 this.TextNode = domplate(Firebug.Rep,
16324 {
16325     tag:
16326         OBJECTLINK(
16327             "&lt;",
16328             SPAN({"class": "nodeTag"}, "TextNode"),
16329             "&nbsp;textContent=&quot;", SPAN({"class": "nodeValue"}, "$object.textContent|cropString"), "&quot;",
16330             "&gt;"
16331             ),
16332
16333     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16334
16335     className: "textNode",
16336
16337     supportsObject: function(object)
16338     {
16339         return object instanceof Text;
16340     }
16341 });
16342
16343 // ************************************************************************************************
16344
16345 this.Document = domplate(Firebug.Rep,
16346 {
16347     tag:
16348         OBJECTLINK("Document ", SPAN({"class": "objectPropValue"}, "$object|getLocation")),
16349
16350     getLocation: function(doc)
16351     {
16352         return doc.location ? getFileName(doc.location.href) : "";
16353     },
16354
16355     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16356
16357     className: "object",
16358
16359     supportsObject: function(object)
16360     {
16361         //return object instanceof Document || object instanceof XMLDocument;
16362         return instanceOf(object, "Document");
16363     },
16364
16365     browseObject: function(doc, context)
16366     {
16367         openNewTab(doc.location.href);
16368         return true;
16369     },
16370
16371     persistObject: function(doc, context)
16372     {
16373         return this.persistor;
16374     },
16375
16376     persistor: function(context)
16377     {
16378         return context.window.document;
16379     },
16380
16381     getTitle: function(win, context)
16382     {
16383         return "document";
16384     },
16385
16386     getTooltip: function(doc)
16387     {
16388         return doc.location.href;
16389     }
16390 });
16391
16392 // ************************************************************************************************
16393
16394 this.StyleSheet = domplate(Firebug.Rep,
16395 {
16396     tag:
16397         OBJECTLINK("StyleSheet ", SPAN({"class": "objectPropValue"}, "$object|getLocation")),
16398
16399     getLocation: function(styleSheet)
16400     {
16401         return getFileName(styleSheet.href);
16402     },
16403
16404     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16405
16406     copyURL: function(styleSheet)
16407     {
16408         copyToClipboard(styleSheet.href);
16409     },
16410
16411     openInTab: function(styleSheet)
16412     {
16413         openNewTab(styleSheet.href);
16414     },
16415
16416     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16417
16418     className: "object",
16419
16420     supportsObject: function(object)
16421     {
16422         //return object instanceof CSSStyleSheet;
16423         return instanceOf(object, "CSSStyleSheet");
16424     },
16425
16426     browseObject: function(styleSheet, context)
16427     {
16428         openNewTab(styleSheet.href);
16429         return true;
16430     },
16431
16432     persistObject: function(styleSheet, context)
16433     {
16434         return bind(this.persistor, top, styleSheet.href);
16435     },
16436
16437     getTooltip: function(styleSheet)
16438     {
16439         return styleSheet.href;
16440     },
16441
16442     getContextMenuItems: function(styleSheet, target, context)
16443     {
16444         return [
16445             {label: "CopyLocation", command: bindFixed(this.copyURL, this, styleSheet) },
16446             "-",
16447             {label: "OpenInTab", command: bindFixed(this.openInTab, this, styleSheet) }
16448         ];
16449     },
16450
16451     persistor: function(context, href)
16452     {
16453         return getStyleSheetByHref(href, context);
16454     }
16455 });
16456
16457 // ************************************************************************************************
16458
16459 this.Window = domplate(Firebug.Rep,
16460 {
16461     tag:
16462         OBJECTLINK("Window ", SPAN({"class": "objectPropValue"}, "$object|getLocation")),
16463
16464     getLocation: function(win)
16465     {
16466         try
16467         {
16468             return (win && win.location && !win.closed) ? getFileName(win.location.href) : "";
16469         }
16470         catch (exc)
16471         {
16472             if (FBTrace.DBG_ERRORS)
16473                 FBTrace.sysout("reps.Window window closed?");
16474         }
16475     },
16476
16477     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16478
16479     className: "object",
16480
16481     supportsObject: function(object)
16482     {
16483         return instanceOf(object, "Window");
16484     },
16485
16486     browseObject: function(win, context)
16487     {
16488         openNewTab(win.location.href);
16489         return true;
16490     },
16491
16492     persistObject: function(win, context)
16493     {
16494         return this.persistor;
16495     },
16496
16497     persistor: function(context)
16498     {
16499         return context.window;
16500     },
16501
16502     getTitle: function(win, context)
16503     {
16504         return "window";
16505     },
16506
16507     getTooltip: function(win)
16508     {
16509         if (win && !win.closed)
16510             return win.location.href;
16511     }
16512 });
16513
16514 // ************************************************************************************************
16515
16516 this.Event = domplate(Firebug.Rep,
16517 {
16518     tag: TAG("$copyEventTag", {object: "$object|copyEvent"}),
16519
16520     copyEventTag:
16521         OBJECTLINK("$object|summarizeEvent"),
16522
16523     summarizeEvent: function(event)
16524     {
16525         var info = [event.type, ' '];
16526
16527         var eventFamily = getEventFamily(event.type);
16528         if (eventFamily == "mouse")
16529             info.push("clientX=", event.clientX, ", clientY=", event.clientY);
16530         else if (eventFamily == "key")
16531             info.push("charCode=", event.charCode, ", keyCode=", event.keyCode);
16532
16533         return info.join("");
16534     },
16535
16536     copyEvent: function(event)
16537     {
16538         return new EventCopy(event);
16539     },
16540
16541     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16542
16543     className: "object",
16544
16545     supportsObject: function(object)
16546     {
16547         //return object instanceof Event || object instanceof EventCopy;
16548         return instanceOf(object, "Event") || instanceOf(object, "EventCopy");
16549     },
16550
16551     getTitle: function(event, context)
16552     {
16553         return "Event " + event.type;
16554     }
16555 });
16556
16557 // ************************************************************************************************
16558
16559 this.SourceLink = domplate(Firebug.Rep,
16560 {
16561     tag:
16562         OBJECTLINK({$collapsed: "$object|hideSourceLink"}, "$object|getSourceLinkTitle"),
16563
16564     hideSourceLink: function(sourceLink)
16565     {
16566         return sourceLink ? sourceLink.href.indexOf("XPCSafeJSObjectWrapper") != -1 : true;
16567     },
16568
16569     getSourceLinkTitle: function(sourceLink)
16570     {
16571         if (!sourceLink)
16572             return "";
16573
16574         try
16575         {
16576             var fileName = getFileName(sourceLink.href);
16577             fileName = decodeURIComponent(fileName);
16578             fileName = cropString(fileName, 17);
16579         }
16580         catch(exc)
16581         {
16582             if (FBTrace.DBG_ERRORS)
16583                 FBTrace.sysout("reps.getSourceLinkTitle decodeURIComponent fails for \'"+fileName+"\': "+exc, exc);
16584         }
16585
16586         return typeof sourceLink.line == "number" ?
16587                 fileName + " (line " + sourceLink.line + ")" :
16588                 fileName;
16589
16590         // TODO: xxxpedro
16591         //return $STRF("Line", [fileName, sourceLink.line]);
16592     },
16593
16594     copyLink: function(sourceLink)
16595     {
16596         copyToClipboard(sourceLink.href);
16597     },
16598
16599     openInTab: function(sourceLink)
16600     {
16601         openNewTab(sourceLink.href);
16602     },
16603
16604     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16605
16606     className: "sourceLink",
16607
16608     supportsObject: function(object)
16609     {
16610         return object instanceof SourceLink;
16611     },
16612
16613     getTooltip: function(sourceLink)
16614     {
16615         return decodeURI(sourceLink.href);
16616     },
16617
16618     inspectObject: function(sourceLink, context)
16619     {
16620         if (sourceLink.type == "js")
16621         {
16622             var scriptFile = getSourceFileByHref(sourceLink.href, context);
16623             if (scriptFile)
16624                 return Firebug.chrome.select(sourceLink);
16625         }
16626         else if (sourceLink.type == "css")
16627         {
16628             // If an object is defined, treat it as the highest priority for
16629             // inspect actions
16630             if (sourceLink.object) {
16631                 Firebug.chrome.select(sourceLink.object);
16632                 return;
16633             }
16634
16635             var stylesheet = getStyleSheetByHref(sourceLink.href, context);
16636             if (stylesheet)
16637             {
16638                 var ownerNode = stylesheet.ownerNode;
16639                 if (ownerNode)
16640                 {
16641                     Firebug.chrome.select(sourceLink, "html");
16642                     return;
16643                 }
16644
16645                 var panel = context.getPanel("stylesheet");
16646                 if (panel && panel.getRuleByLine(stylesheet, sourceLink.line))
16647                     return Firebug.chrome.select(sourceLink);
16648             }
16649         }
16650
16651         // Fallback is to just open the view-source window on the file
16652         viewSource(sourceLink.href, sourceLink.line);
16653     },
16654
16655     browseObject: function(sourceLink, context)
16656     {
16657         openNewTab(sourceLink.href);
16658         return true;
16659     },
16660
16661     getContextMenuItems: function(sourceLink, target, context)
16662     {
16663         return [
16664             {label: "CopyLocation", command: bindFixed(this.copyLink, this, sourceLink) },
16665             "-",
16666             {label: "OpenInTab", command: bindFixed(this.openInTab, this, sourceLink) }
16667         ];
16668     }
16669 });
16670
16671 // ************************************************************************************************
16672
16673 this.SourceFile = domplate(this.SourceLink,
16674 {
16675     tag:
16676         OBJECTLINK({$collapsed: "$object|hideSourceLink"}, "$object|getSourceLinkTitle"),
16677
16678     persistor: function(context, href)
16679     {
16680         return getSourceFileByHref(href, context);
16681     },
16682
16683     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16684
16685     className: "sourceFile",
16686
16687     supportsObject: function(object)
16688     {
16689         return object instanceof SourceFile;
16690     },
16691
16692     persistObject: function(sourceFile)
16693     {
16694         return bind(this.persistor, top, sourceFile.href);
16695     },
16696
16697     browseObject: function(sourceLink, context)
16698     {
16699     },
16700
16701     getTooltip: function(sourceFile)
16702     {
16703         return sourceFile.href;
16704     }
16705 });
16706
16707 // ************************************************************************************************
16708
16709 this.StackFrame = domplate(Firebug.Rep,  // XXXjjb Since the repObject is fn the stack does not have correct line numbers
16710 {
16711     tag:
16712         OBJECTBLOCK(
16713             A({"class": "objectLink objectLink-function focusRow a11yFocus", _repObject: "$object.fn"}, "$object|getCallName"),
16714             " ( ",
16715             FOR("arg", "$object|argIterator",
16716                 TAG("$arg.tag", {object: "$arg.value"}),
16717                 SPAN({"class": "arrayComma"}, "$arg.delim")
16718             ),
16719             " )",
16720             SPAN({"class": "objectLink-sourceLink objectLink"}, "$object|getSourceLinkTitle")
16721         ),
16722
16723     getCallName: function(frame)
16724     {
16725         //TODO: xxxpedro reps StackFrame
16726         return frame.name || "anonymous";
16727
16728         //return getFunctionName(frame.script, frame.context);
16729     },
16730
16731     getSourceLinkTitle: function(frame)
16732     {
16733         //TODO: xxxpedro reps StackFrame
16734         var fileName = cropString(getFileName(frame.href), 20);
16735         return fileName + (frame.lineNo ? " (line " + frame.lineNo + ")" : "");
16736
16737         var fileName = cropString(getFileName(frame.href), 17);
16738         return $STRF("Line", [fileName, frame.lineNo]);
16739     },
16740
16741     argIterator: function(frame)
16742     {
16743         if (!frame.args)
16744             return [];
16745
16746         var items = [];
16747
16748         for (var i = 0; i < frame.args.length; ++i)
16749         {
16750             var arg = frame.args[i];
16751
16752             if (!arg)
16753                 break;
16754
16755             var rep = Firebug.getRep(arg.value);
16756             var tag = rep.shortTag ? rep.shortTag : rep.tag;
16757
16758             var delim = (i == frame.args.length-1 ? "" : ", ");
16759
16760             items.push({name: arg.name, value: arg.value, tag: tag, delim: delim});
16761         }
16762
16763         return items;
16764     },
16765
16766     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16767
16768     className: "stackFrame",
16769
16770     supportsObject: function(object)
16771     {
16772         return object instanceof StackFrame;
16773     },
16774
16775     inspectObject: function(stackFrame, context)
16776     {
16777         var sourceLink = new SourceLink(stackFrame.href, stackFrame.lineNo, "js");
16778         Firebug.chrome.select(sourceLink);
16779     },
16780
16781     getTooltip: function(stackFrame, context)
16782     {
16783         return $STRF("Line", [stackFrame.href, stackFrame.lineNo]);
16784     }
16785
16786 });
16787
16788 // ************************************************************************************************
16789
16790 this.StackTrace = domplate(Firebug.Rep,
16791 {
16792     tag:
16793         FOR("frame", "$object.frames focusRow",
16794             TAG(this.StackFrame.tag, {object: "$frame"})
16795         ),
16796
16797     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16798
16799     className: "stackTrace",
16800
16801     supportsObject: function(object)
16802     {
16803         return object instanceof StackTrace;
16804     }
16805 });
16806
16807 // ************************************************************************************************
16808
16809 this.jsdStackFrame = domplate(Firebug.Rep,
16810 {
16811     inspectable: false,
16812
16813     supportsObject: function(object)
16814     {
16815         return (object instanceof jsdIStackFrame) && (object.isValid);
16816     },
16817
16818     getTitle: function(frame, context)
16819     {
16820         if (!frame.isValid) return "(invalid frame)"; // XXXjjb avoid frame.script == null
16821         return getFunctionName(frame.script, context);
16822     },
16823
16824     getTooltip: function(frame, context)
16825     {
16826         if (!frame.isValid) return "(invalid frame)";  // XXXjjb avoid frame.script == null
16827         var sourceInfo = FBL.getSourceFileAndLineByScript(context, frame.script, frame);
16828         if (sourceInfo)
16829             return $STRF("Line", [sourceInfo.sourceFile.href, sourceInfo.lineNo]);
16830         else
16831             return $STRF("Line", [frame.script.fileName, frame.line]);
16832     },
16833
16834     getContextMenuItems: function(frame, target, context)
16835     {
16836         var fn = frame.script.functionObject.getWrappedValue();
16837         return FirebugReps.Func.getContextMenuItems(fn, target, context, frame.script);
16838     }
16839 });
16840
16841 // ************************************************************************************************
16842
16843 this.ErrorMessage = domplate(Firebug.Rep,
16844 {
16845     tag:
16846         OBJECTBOX({
16847                 $hasTwisty: "$object|hasStackTrace",
16848                 $hasBreakSwitch: "$object|hasBreakSwitch",
16849                 $breakForError: "$object|hasErrorBreak",
16850                 _repObject: "$object",
16851                 _stackTrace: "$object|getLastErrorStackTrace",
16852                 onclick: "$onToggleError"},
16853
16854             DIV({"class": "errorTitle a11yFocus", role : 'checkbox', 'aria-checked' : 'false'},
16855                 "$object.message|getMessage"
16856             ),
16857             DIV({"class": "errorTrace"}),
16858             DIV({"class": "errorSourceBox errorSource-$object|getSourceType"},
16859                 IMG({"class": "errorBreak a11yFocus", src:"blank.gif", role : 'checkbox', 'aria-checked':'false', title: "Break on this error"}),
16860                 A({"class": "errorSource a11yFocus"}, "$object|getLine")
16861             ),
16862             TAG(this.SourceLink.tag, {object: "$object|getSourceLink"})
16863         ),
16864
16865     getLastErrorStackTrace: function(error)
16866     {
16867         return error.trace;
16868     },
16869
16870     hasStackTrace: function(error)
16871     {
16872         var url = error.href.toString();
16873         var fromCommandLine = (url.indexOf("XPCSafeJSObjectWrapper") != -1);
16874         return !fromCommandLine && error.trace;
16875     },
16876
16877     hasBreakSwitch: function(error)
16878     {
16879         return error.href && error.lineNo > 0;
16880     },
16881
16882     hasErrorBreak: function(error)
16883     {
16884         return fbs.hasErrorBreakpoint(error.href, error.lineNo);
16885     },
16886
16887     getMessage: function(message)
16888     {
16889         var re = /\[Exception... "(.*?)" nsresult:/;
16890         var m = re.exec(message);
16891         return m ? m[1] : message;
16892     },
16893
16894     getLine: function(error)
16895     {
16896         if (error.category == "js")
16897         {
16898             if (error.source)
16899                 return cropString(error.source, 80);
16900             else if (error.href && error.href.indexOf("XPCSafeJSObjectWrapper") == -1)
16901                 return cropString(error.getSourceLine(), 80);
16902         }
16903     },
16904
16905     getSourceLink: function(error)
16906     {
16907         var ext = error.category == "css" ? "css" : "js";
16908         return error.lineNo ? new SourceLink(error.href, error.lineNo, ext) : null;
16909     },
16910
16911     getSourceType: function(error)
16912     {
16913         // Errors occurring inside of HTML event handlers look like "foo.html (line 1)"
16914         // so let's try to skip those
16915         if (error.source)
16916             return "syntax";
16917         else if (error.lineNo == 1 && getFileExtension(error.href) != "js")
16918             return "none";
16919         else if (error.category == "css")
16920             return "none";
16921         else if (!error.href || !error.lineNo)
16922             return "none";
16923         else
16924             return "exec";
16925     },
16926
16927     onToggleError: function(event)
16928     {
16929         var target = event.currentTarget;
16930         if (hasClass(event.target, "errorBreak"))
16931         {
16932             this.breakOnThisError(target.repObject);
16933         }
16934         else if (hasClass(event.target, "errorSource"))
16935         {
16936             var panel = Firebug.getElementPanel(event.target);
16937             this.inspectObject(target.repObject, panel.context);
16938         }
16939         else if (hasClass(event.target, "errorTitle"))
16940         {
16941             var traceBox = target.childNodes[1];
16942             toggleClass(target, "opened");
16943             event.target.setAttribute('aria-checked', hasClass(target, "opened"));
16944             if (hasClass(target, "opened"))
16945             {
16946                 if (target.stackTrace)
16947                     var node = FirebugReps.StackTrace.tag.append({object: target.stackTrace}, traceBox);
16948                 if (Firebug.A11yModel.enabled)
16949                 {
16950                     var panel = Firebug.getElementPanel(event.target);
16951                     dispatch([Firebug.A11yModel], "onLogRowContentCreated", [panel , traceBox]);
16952                 }
16953             }
16954             else
16955                 clearNode(traceBox);
16956         }
16957     },
16958
16959     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16960
16961     copyError: function(error)
16962     {
16963         var message = [
16964             this.getMessage(error.message),
16965             error.href,
16966             "Line " +  error.lineNo
16967         ];
16968         copyToClipboard(message.join("\n"));
16969     },
16970
16971     breakOnThisError: function(error)
16972     {
16973         if (this.hasErrorBreak(error))
16974             Firebug.Debugger.clearErrorBreakpoint(error.href, error.lineNo);
16975         else
16976             Firebug.Debugger.setErrorBreakpoint(error.href, error.lineNo);
16977     },
16978
16979     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16980
16981     className: "errorMessage",
16982     inspectable: false,
16983
16984     supportsObject: function(object)
16985     {
16986         return object instanceof ErrorMessage;
16987     },
16988
16989     inspectObject: function(error, context)
16990     {
16991         var sourceLink = this.getSourceLink(error);
16992         FirebugReps.SourceLink.inspectObject(sourceLink, context);
16993     },
16994
16995     getContextMenuItems: function(error, target, context)
16996     {
16997         var breakOnThisError = this.hasErrorBreak(error);
16998
16999         var items = [
17000             {label: "CopyError", command: bindFixed(this.copyError, this, error) }
17001         ];
17002
17003         if (error.category == "css")
17004         {
17005             items.push(
17006                 "-",
17007                 {label: "BreakOnThisError", type: "checkbox", checked: breakOnThisError,
17008                  command: bindFixed(this.breakOnThisError, this, error) },
17009
17010                 optionMenu("BreakOnAllErrors", "breakOnErrors")
17011             );
17012         }
17013
17014         return items;
17015     }
17016 });
17017
17018 // ************************************************************************************************
17019
17020 this.Assert = domplate(Firebug.Rep,
17021 {
17022     tag:
17023         DIV(
17024             DIV({"class": "errorTitle"}),
17025             DIV({"class": "assertDescription"})
17026         ),
17027
17028     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17029
17030     className: "assert",
17031
17032     inspectObject: function(error, context)
17033     {
17034         var sourceLink = this.getSourceLink(error);
17035         Firebug.chrome.select(sourceLink);
17036     },
17037
17038     getContextMenuItems: function(error, target, context)
17039     {
17040         var breakOnThisError = this.hasErrorBreak(error);
17041
17042         return [
17043             {label: "CopyError", command: bindFixed(this.copyError, this, error) },
17044             "-",
17045             {label: "BreakOnThisError", type: "checkbox", checked: breakOnThisError,
17046              command: bindFixed(this.breakOnThisError, this, error) },
17047             {label: "BreakOnAllErrors", type: "checkbox", checked: Firebug.breakOnErrors,
17048              command: bindFixed(this.breakOnAllErrors, this, error) }
17049         ];
17050     }
17051 });
17052
17053 // ************************************************************************************************
17054
17055 this.SourceText = domplate(Firebug.Rep,
17056 {
17057     tag:
17058         DIV(
17059             FOR("line", "$object|lineIterator",
17060                 DIV({"class": "sourceRow", role : "presentation"},
17061                     SPAN({"class": "sourceLine", role : "presentation"}, "$line.lineNo"),
17062                     SPAN({"class": "sourceRowText", role : "presentation"}, "$line.text")
17063                 )
17064             )
17065         ),
17066
17067     lineIterator: function(sourceText)
17068     {
17069         var maxLineNoChars = (sourceText.lines.length + "").length;
17070         var list = [];
17071
17072         for (var i = 0; i < sourceText.lines.length; ++i)
17073         {
17074             // Make sure all line numbers are the same width (with a fixed-width font)
17075             var lineNo = (i+1) + "";
17076             while (lineNo.length < maxLineNoChars)
17077                 lineNo = " " + lineNo;
17078
17079             list.push({lineNo: lineNo, text: sourceText.lines[i]});
17080         }
17081
17082         return list;
17083     },
17084
17085     getHTML: function(sourceText)
17086     {
17087         return getSourceLineRange(sourceText, 1, sourceText.lines.length);
17088     }
17089 });
17090
17091 //************************************************************************************************
17092 this.nsIDOMHistory = domplate(Firebug.Rep,
17093 {
17094     tag:OBJECTBOX({onclick: "$showHistory"},
17095             OBJECTLINK("$object|summarizeHistory")
17096         ),
17097
17098     className: "nsIDOMHistory",
17099
17100     summarizeHistory: function(history)
17101     {
17102         try
17103         {
17104             var items = history.length;
17105             return items + " history entries";
17106         }
17107         catch(exc)
17108         {
17109             return "object does not support history (nsIDOMHistory)";
17110         }
17111     },
17112
17113     showHistory: function(history)
17114     {
17115         try
17116         {
17117             var items = history.length;  // if this throws, then unsupported
17118             Firebug.chrome.select(history);
17119         }
17120         catch (exc)
17121         {
17122         }
17123     },
17124
17125     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17126
17127     supportsObject: function(object, type)
17128     {
17129         return (object instanceof Ci.nsIDOMHistory);
17130     }
17131 });
17132
17133 // ************************************************************************************************
17134 this.ApplicationCache = domplate(Firebug.Rep,
17135 {
17136     tag:OBJECTBOX({onclick: "$showApplicationCache"},
17137             OBJECTLINK("$object|summarizeCache")
17138         ),
17139
17140     summarizeCache: function(applicationCache)
17141     {
17142         try
17143         {
17144             return applicationCache.length + " items in offline cache";
17145         }
17146         catch(exc)
17147         {
17148             return "https://bugzilla.mozilla.org/show_bug.cgi?id=422264";
17149         }
17150     },
17151
17152     showApplicationCache: function(event)
17153     {
17154         openNewTab("https://bugzilla.mozilla.org/show_bug.cgi?id=422264");
17155     },
17156
17157     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17158
17159     className: "applicationCache",
17160
17161     supportsObject: function(object, type)
17162     {
17163         if (Ci.nsIDOMOfflineResourceList)
17164             return (object instanceof Ci.nsIDOMOfflineResourceList);
17165     }
17166
17167 });
17168
17169 this.Storage = domplate(Firebug.Rep,
17170 {
17171     tag: OBJECTBOX({onclick: "$show"}, OBJECTLINK("$object|summarize")),
17172
17173     summarize: function(storage)
17174     {
17175         return storage.length +" items in Storage";
17176     },
17177     show: function(storage)
17178     {
17179         openNewTab("http://dev.w3.org/html5/webstorage/#storage-0");
17180     },
17181     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17182
17183     className: "Storage",
17184
17185     supportsObject: function(object, type)
17186     {
17187         return (object instanceof Storage);
17188     }
17189
17190 });
17191
17192 // ************************************************************************************************
17193 Firebug.registerRep(
17194     //this.nsIDOMHistory, // make this early to avoid exceptions
17195     this.Undefined,
17196     this.Null,
17197     this.Number,
17198     this.String,
17199     this.Window,
17200     //this.ApplicationCache, // must come before Arr (array) else exceptions.
17201     //this.ErrorMessage,
17202     this.Element,
17203     //this.TextNode,
17204     this.Document,
17205     this.StyleSheet,
17206     this.Event,
17207     //this.SourceLink,
17208     //this.SourceFile,
17209     //this.StackTrace,
17210     //this.StackFrame,
17211     //this.jsdStackFrame,
17212     //this.jsdScript,
17213     //this.NetFile,
17214     this.Property,
17215     this.Except,
17216     this.Arr
17217 );
17218
17219 Firebug.setDefaultReps(this.Func, this.Obj);
17220
17221 }});
17222
17223 // ************************************************************************************************
17224 /*
17225  * The following is http://developer.yahoo.com/yui/license.txt and applies to only code labeled "Yahoo BSD Source"
17226  * in only this file reps.js.  John J. Barton June 2007.
17227  *
17228 Software License Agreement (BSD License)
17229
17230 Copyright (c) 2006, Yahoo! Inc.
17231 All rights reserved.
17232
17233 Redistribution and use of this software in source and binary forms, with or without modification, are
17234 permitted provided that the following conditions are met:
17235
17236 * Redistributions of source code must retain the above
17237   copyright notice, this list of conditions and the
17238   following disclaimer.
17239
17240 * Redistributions in binary form must reproduce the above
17241   copyright notice, this list of conditions and the
17242   following disclaimer in the documentation and/or other
17243   materials provided with the distribution.
17244
17245 * Neither the name of Yahoo! Inc. nor the names of its
17246   contributors may be used to endorse or promote products
17247   derived from this software without specific prior
17248   written permission of Yahoo! Inc.
17249
17250 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
17251 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
17252 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17253 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
17254 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
17255 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
17256 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
17257 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17258  * /
17259  */
17260
17261
17262 /* See license.txt for terms of usage */
17263
17264 FBL.ns(function() { with (FBL) {
17265
17266 // ************************************************************************************************
17267 // Constants
17268
17269 var saveTimeout = 400;
17270 var pageAmount = 10;
17271
17272 // ************************************************************************************************
17273 // Globals
17274
17275 var currentTarget = null;
17276 var currentGroup = null;
17277 var currentPanel = null;
17278 var currentEditor = null;
17279
17280 var defaultEditor = null;
17281
17282 var originalClassName = null;
17283
17284 var originalValue = null;
17285 var defaultValue = null;
17286 var previousValue = null;
17287
17288 var invalidEditor = false;
17289 var ignoreNextInput = false;
17290
17291 // ************************************************************************************************
17292
17293 Firebug.Editor = extend(Firebug.Module,
17294 {
17295     supportsStopEvent: true,
17296
17297     dispatchName: "editor",
17298     tabCharacter: "    ",
17299
17300     startEditing: function(target, value, editor)
17301     {
17302         this.stopEditing();
17303
17304         if (hasClass(target, "insertBefore") || hasClass(target, "insertAfter"))
17305             return;
17306
17307         var panel = Firebug.getElementPanel(target);
17308         if (!panel.editable)
17309             return;
17310
17311         if (FBTrace.DBG_EDITOR)
17312             FBTrace.sysout("editor.startEditing " + value, target);
17313
17314         defaultValue = target.getAttribute("defaultValue");
17315         if (value == undefined)
17316         {
17317             var textContent = isIE ? "innerText" : "textContent";
17318             value = target[textContent];
17319             if (value == defaultValue)
17320                 value = "";
17321         }
17322
17323         originalValue = previousValue = value;
17324
17325         invalidEditor = false;
17326         currentTarget = target;
17327         currentPanel = panel;
17328         currentGroup = getAncestorByClass(target, "editGroup");
17329
17330         currentPanel.editing = true;
17331
17332         var panelEditor = currentPanel.getEditor(target, value);
17333         currentEditor = editor ? editor : panelEditor;
17334         if (!currentEditor)
17335             currentEditor = getDefaultEditor(currentPanel);
17336
17337         var inlineParent = getInlineParent(target);
17338         var targetSize = getOffsetSize(inlineParent);
17339
17340         setClass(panel.panelNode, "editing");
17341         setClass(target, "editing");
17342         if (currentGroup)
17343             setClass(currentGroup, "editing");
17344
17345         currentEditor.show(target, currentPanel, value, targetSize);
17346         //dispatch(this.fbListeners, "onBeginEditing", [currentPanel, currentEditor, target, value]);
17347         currentEditor.beginEditing(target, value);
17348         if (FBTrace.DBG_EDITOR)
17349             FBTrace.sysout("Editor start panel "+currentPanel.name);
17350         this.attachListeners(currentEditor, panel.context);
17351     },
17352
17353     stopEditing: function(cancel)
17354     {
17355         if (!currentTarget)
17356             return;
17357
17358         if (FBTrace.DBG_EDITOR)
17359             FBTrace.sysout("editor.stopEditing cancel:" + cancel+" saveTimeout: "+this.saveTimeout);
17360
17361         clearTimeout(this.saveTimeout);
17362         delete this.saveTimeout;
17363
17364         this.detachListeners(currentEditor, currentPanel.context);
17365
17366         removeClass(currentPanel.panelNode, "editing");
17367         removeClass(currentTarget, "editing");
17368         if (currentGroup)
17369             removeClass(currentGroup, "editing");
17370
17371         var value = currentEditor.getValue();
17372         if (value == defaultValue)
17373             value = "";
17374
17375         var removeGroup = currentEditor.endEditing(currentTarget, value, cancel);
17376
17377         try
17378         {
17379             if (cancel)
17380             {
17381                 //dispatch([Firebug.A11yModel], 'onInlineEditorClose', [currentPanel, currentTarget, removeGroup && !originalValue]);
17382                 if (value != originalValue)
17383                     this.saveEditAndNotifyListeners(currentTarget, originalValue, previousValue);
17384
17385                 if (removeGroup && !originalValue && currentGroup)
17386                     currentGroup.parentNode.removeChild(currentGroup);
17387             }
17388             else if (!value)
17389             {
17390                 this.saveEditAndNotifyListeners(currentTarget, null, previousValue);
17391
17392                 if (removeGroup && currentGroup)
17393                     currentGroup.parentNode.removeChild(currentGroup);
17394             }
17395             else
17396                 this.save(value);
17397         }
17398         catch (exc)
17399         {
17400             //throw exc.message;
17401             //ERROR(exc);
17402         }
17403
17404         currentEditor.hide();
17405         currentPanel.editing = false;
17406
17407         //dispatch(this.fbListeners, "onStopEdit", [currentPanel, currentEditor, currentTarget]);
17408         //if (FBTrace.DBG_EDITOR)
17409         //    FBTrace.sysout("Editor stop panel "+currentPanel.name);
17410
17411         currentTarget = null;
17412         currentGroup = null;
17413         currentPanel = null;
17414         currentEditor = null;
17415         originalValue = null;
17416         invalidEditor = false;
17417
17418         return value;
17419     },
17420
17421     cancelEditing: function()
17422     {
17423         return this.stopEditing(true);
17424     },
17425
17426     update: function(saveNow)
17427     {
17428         if (this.saveTimeout)
17429             clearTimeout(this.saveTimeout);
17430
17431         invalidEditor = true;
17432
17433         currentEditor.layout();
17434
17435         if (saveNow)
17436             this.save();
17437         else
17438         {
17439             var context = currentPanel.context;
17440             this.saveTimeout = context.setTimeout(bindFixed(this.save, this), saveTimeout);
17441             if (FBTrace.DBG_EDITOR)
17442                 FBTrace.sysout("editor.update saveTimeout: "+this.saveTimeout);
17443         }
17444     },
17445
17446     save: function(value)
17447     {
17448         if (!invalidEditor)
17449             return;
17450
17451         if (value == undefined)
17452             value = currentEditor.getValue();
17453         if (FBTrace.DBG_EDITOR)
17454             FBTrace.sysout("editor.save saveTimeout: "+this.saveTimeout+" currentPanel: "+(currentPanel?currentPanel.name:"null"));
17455         try
17456         {
17457             this.saveEditAndNotifyListeners(currentTarget, value, previousValue);
17458
17459             previousValue = value;
17460             invalidEditor = false;
17461         }
17462         catch (exc)
17463         {
17464             if (FBTrace.DBG_ERRORS)
17465                 FBTrace.sysout("editor.save FAILS "+exc, exc);
17466         }
17467     },
17468
17469     saveEditAndNotifyListeners: function(currentTarget, value, previousValue)
17470     {
17471         currentEditor.saveEdit(currentTarget, value, previousValue);
17472         //dispatch(this.fbListeners, "onSaveEdit", [currentPanel, currentEditor, currentTarget, value, previousValue]);
17473     },
17474
17475     setEditTarget: function(element)
17476     {
17477         if (!element)
17478         {
17479             dispatch([Firebug.A11yModel], 'onInlineEditorClose', [currentPanel, currentTarget, true]);
17480             this.stopEditing();
17481         }
17482         else if (hasClass(element, "insertBefore"))
17483             this.insertRow(element, "before");
17484         else if (hasClass(element, "insertAfter"))
17485             this.insertRow(element, "after");
17486         else
17487             this.startEditing(element);
17488     },
17489
17490     tabNextEditor: function()
17491     {
17492         if (!currentTarget)
17493             return;
17494
17495         var value = currentEditor.getValue();
17496         var nextEditable = currentTarget;
17497         do
17498         {
17499             nextEditable = !value && currentGroup
17500                 ? getNextOutsider(nextEditable, currentGroup)
17501                 : getNextByClass(nextEditable, "editable");
17502         }
17503         while (nextEditable && !nextEditable.offsetHeight);
17504
17505         this.setEditTarget(nextEditable);
17506     },
17507
17508     tabPreviousEditor: function()
17509     {
17510         if (!currentTarget)
17511             return;
17512
17513         var value = currentEditor.getValue();
17514         var prevEditable = currentTarget;
17515         do
17516         {
17517             prevEditable = !value && currentGroup
17518                 ? getPreviousOutsider(prevEditable, currentGroup)
17519                 : getPreviousByClass(prevEditable, "editable");
17520         }
17521         while (prevEditable && !prevEditable.offsetHeight);
17522
17523         this.setEditTarget(prevEditable);
17524     },
17525
17526     insertRow: function(relative, insertWhere)
17527     {
17528         var group =
17529             relative || getAncestorByClass(currentTarget, "editGroup") || currentTarget;
17530         var value = this.stopEditing();
17531
17532         currentPanel = Firebug.getElementPanel(group);
17533
17534         currentEditor = currentPanel.getEditor(group, value);
17535         if (!currentEditor)
17536             currentEditor = getDefaultEditor(currentPanel);
17537
17538         currentGroup = currentEditor.insertNewRow(group, insertWhere);
17539         if (!currentGroup)
17540             return;
17541
17542         var editable = hasClass(currentGroup, "editable")
17543             ? currentGroup
17544             : getNextByClass(currentGroup, "editable");
17545
17546         if (editable)
17547             this.setEditTarget(editable);
17548     },
17549
17550     insertRowForObject: function(relative)
17551     {
17552         var container = getAncestorByClass(relative, "insertInto");
17553         if (container)
17554         {
17555             relative = getChildByClass(container, "insertBefore");
17556             if (relative)
17557                 this.insertRow(relative, "before");
17558         }
17559     },
17560
17561     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17562
17563     attachListeners: function(editor, context)
17564     {
17565         var win = isIE ?
17566                 currentTarget.ownerDocument.parentWindow :
17567                 currentTarget.ownerDocument.defaultView;
17568
17569         addEvent(win, "resize", this.onResize);
17570         addEvent(win, "blur", this.onBlur);
17571
17572         var chrome = Firebug.chrome;
17573
17574         this.listeners = [
17575             chrome.keyCodeListen("ESCAPE", null, bind(this.cancelEditing, this))
17576         ];
17577
17578         if (editor.arrowCompletion)
17579         {
17580             this.listeners.push(
17581                 chrome.keyCodeListen("UP", null, bindFixed(editor.completeValue, editor, -1)),
17582                 chrome.keyCodeListen("DOWN", null, bindFixed(editor.completeValue, editor, 1)),
17583                 chrome.keyCodeListen("PAGE_UP", null, bindFixed(editor.completeValue, editor, -pageAmount)),
17584                 chrome.keyCodeListen("PAGE_DOWN", null, bindFixed(editor.completeValue, editor, pageAmount))
17585             );
17586         }
17587
17588         if (currentEditor.tabNavigation)
17589         {
17590             this.listeners.push(
17591                 chrome.keyCodeListen("RETURN", null, bind(this.tabNextEditor, this)),
17592                 chrome.keyCodeListen("RETURN", isControl, bind(this.insertRow, this, null, "after")),
17593                 chrome.keyCodeListen("TAB", null, bind(this.tabNextEditor, this)),
17594                 chrome.keyCodeListen("TAB", isShift, bind(this.tabPreviousEditor, this))
17595             );
17596         }
17597         else if (currentEditor.multiLine)
17598         {
17599             this.listeners.push(
17600                 chrome.keyCodeListen("TAB", null, insertTab)
17601             );
17602         }
17603         else
17604         {
17605             this.listeners.push(
17606                 chrome.keyCodeListen("RETURN", null, bindFixed(this.stopEditing, this))
17607             );
17608
17609             if (currentEditor.tabCompletion)
17610             {
17611                 this.listeners.push(
17612                     chrome.keyCodeListen("TAB", null, bind(editor.completeValue, editor, 1)),
17613                     chrome.keyCodeListen("TAB", isShift, bind(editor.completeValue, editor, -1))
17614                 );
17615             }
17616         }
17617     },
17618
17619     detachListeners: function(editor, context)
17620     {
17621         if (!this.listeners)
17622             return;
17623
17624         var win = isIE ?
17625                 currentTarget.ownerDocument.parentWindow :
17626                 currentTarget.ownerDocument.defaultView;
17627
17628         removeEvent(win, "resize", this.onResize);
17629         removeEvent(win, "blur", this.onBlur);
17630
17631         var chrome = Firebug.chrome;
17632         if (chrome)
17633         {
17634             for (var i = 0; i < this.listeners.length; ++i)
17635                 chrome.keyIgnore(this.listeners[i]);
17636         }
17637
17638         delete this.listeners;
17639     },
17640
17641     onResize: function(event)
17642     {
17643         currentEditor.layout(true);
17644     },
17645
17646     onBlur: function(event)
17647     {
17648         if (currentEditor.enterOnBlur && isAncestor(event.target, currentEditor.box))
17649             this.stopEditing();
17650     },
17651
17652     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17653     // extends Module
17654
17655     initialize: function()
17656     {
17657         Firebug.Module.initialize.apply(this, arguments);
17658
17659         this.onResize = bindFixed(this.onResize, this);
17660         this.onBlur = bind(this.onBlur, this);
17661     },
17662
17663     disable: function()
17664     {
17665         this.stopEditing();
17666     },
17667
17668     showContext: function(browser, context)
17669     {
17670         this.stopEditing();
17671     },
17672
17673     showPanel: function(browser, panel)
17674     {
17675         this.stopEditing();
17676     }
17677 });
17678
17679 // ************************************************************************************************
17680 // BaseEditor
17681
17682 Firebug.BaseEditor = extend(Firebug.MeasureBox,
17683 {
17684     getValue: function()
17685     {
17686     },
17687
17688     setValue: function(value)
17689     {
17690     },
17691
17692     show: function(target, panel, value, textSize, targetSize)
17693     {
17694     },
17695
17696     hide: function()
17697     {
17698     },
17699
17700     layout: function(forceAll)
17701     {
17702     },
17703
17704     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17705     // Support for context menus within inline editors.
17706
17707     getContextMenuItems: function(target)
17708     {
17709         var items = [];
17710         items.push({label: "Cut", commandID: "cmd_cut"});
17711         items.push({label: "Copy", commandID: "cmd_copy"});
17712         items.push({label: "Paste", commandID: "cmd_paste"});
17713         return items;
17714     },
17715
17716     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17717     // Editor Module listeners will get "onBeginEditing" just before this call
17718
17719     beginEditing: function(target, value)
17720     {
17721     },
17722
17723     // Editor Module listeners will get "onSaveEdit" just after this call
17724     saveEdit: function(target, value, previousValue)
17725     {
17726     },
17727
17728     endEditing: function(target, value, cancel)
17729     {
17730         // Remove empty groups by default
17731         return true;
17732     },
17733
17734     insertNewRow: function(target, insertWhere)
17735     {
17736     }
17737 });
17738
17739 // ************************************************************************************************
17740 // InlineEditor
17741
17742 // basic inline editor attributes
17743 var inlineEditorAttributes = {
17744     "class": "textEditorInner",
17745
17746     type: "text",
17747     spellcheck: "false",
17748
17749     onkeypress: "$onKeyPress",
17750
17751     onoverflow: "$onOverflow",
17752     oncontextmenu: "$onContextMenu"
17753 };
17754
17755 // IE does not support the oninput event, so we're using the onkeydown to signalize
17756 // the relevant keyboard events, and the onpropertychange to actually handle the
17757 // input event, which should happen after the onkeydown event is fired and after the
17758 // value of the input is updated, but before the onkeyup and before the input (with the
17759 // new value) is rendered
17760 if (isIE)
17761 {
17762     inlineEditorAttributes.onpropertychange = "$onInput";
17763     inlineEditorAttributes.onkeydown = "$onKeyDown";
17764 }
17765 // for other browsers we use the oninput event
17766 else
17767 {
17768     inlineEditorAttributes.oninput = "$onInput";
17769 }
17770
17771 Firebug.InlineEditor = function(doc)
17772 {
17773     this.initializeInline(doc);
17774 };
17775
17776 Firebug.InlineEditor.prototype = domplate(Firebug.BaseEditor,
17777 {
17778     enterOnBlur: true,
17779     outerMargin: 8,
17780     shadowExpand: 7,
17781
17782     tag:
17783         DIV({"class": "inlineEditor"},
17784             DIV({"class": "textEditorTop1"},
17785                 DIV({"class": "textEditorTop2"})
17786             ),
17787             DIV({"class": "textEditorInner1"},
17788                 DIV({"class": "textEditorInner2"},
17789                     INPUT(
17790                         inlineEditorAttributes
17791                     )
17792                 )
17793             ),
17794             DIV({"class": "textEditorBottom1"},
17795                 DIV({"class": "textEditorBottom2"})
17796             )
17797         ),
17798
17799     inputTag :
17800         INPUT({"class": "textEditorInner", type: "text",
17801             /*oninput: "$onInput",*/ onkeypress: "$onKeyPress", onoverflow: "$onOverflow"}
17802         ),
17803
17804     expanderTag:
17805         IMG({"class": "inlineExpander", src: "blank.gif"}),
17806
17807     initialize: function()
17808     {
17809         this.fixedWidth = false;
17810         this.completeAsYouType = true;
17811         this.tabNavigation = true;
17812         this.multiLine = false;
17813         this.tabCompletion = false;
17814         this.arrowCompletion = true;
17815         this.noWrap = true;
17816         this.numeric = false;
17817     },
17818
17819     destroy: function()
17820     {
17821         this.destroyInput();
17822     },
17823
17824     initializeInline: function(doc)
17825     {
17826         if (FBTrace.DBG_EDITOR)
17827             FBTrace.sysout("Firebug.InlineEditor initializeInline()");
17828
17829         //this.box = this.tag.replace({}, doc, this);
17830         this.box = this.tag.append({}, doc.body, this);
17831
17832         //this.input = this.box.childNodes[1].firstChild.firstChild;  // XXXjjb childNode[1] required
17833         this.input = this.box.getElementsByTagName("input")[0];
17834
17835         if (isIElt8)
17836         {
17837             this.input.style.top = "-8px";
17838         }
17839
17840         this.expander = this.expanderTag.replace({}, doc, this);
17841         this.initialize();
17842     },
17843
17844     destroyInput: function()
17845     {
17846         // XXXjoe Need to remove input/keypress handlers to avoid leaks
17847     },
17848
17849     getValue: function()
17850     {
17851         return this.input.value;
17852     },
17853
17854     setValue: function(value)
17855     {
17856         // It's only a one-line editor, so new lines shouldn't be allowed
17857         return this.input.value = stripNewLines(value);
17858     },
17859
17860     show: function(target, panel, value, targetSize)
17861     {
17862         //dispatch([Firebug.A11yModel], "onInlineEditorShow", [panel, this]);
17863         this.target = target;
17864         this.panel = panel;
17865
17866         this.targetSize = targetSize;
17867
17868         // TODO: xxxpedro editor
17869         //this.targetOffset = getClientOffset(target);
17870
17871         // Some browsers (IE, Google Chrome and Safari) will have problem trying to get the
17872         // offset values of invisible elements, or empty elements. So, in order to get the
17873         // correct values, we temporary inject a character in the innerHTML of the empty element,
17874         // then we get the offset values, and next, we restore the original innerHTML value.
17875         var innerHTML = target.innerHTML;
17876         var isEmptyElement = !innerHTML;
17877         if (isEmptyElement)
17878             target.innerHTML = ".";
17879
17880         // Get the position of the target element (that is about to be edited)
17881         this.targetOffset =
17882         {
17883             x: target.offsetLeft,
17884             y: target.offsetTop
17885         };
17886
17887         // Restore the original innerHTML value of the empty element
17888         if (isEmptyElement)
17889             target.innerHTML = innerHTML;
17890
17891         this.originalClassName = this.box.className;
17892
17893         var classNames = target.className.split(" ");
17894         for (var i = 0; i < classNames.length; ++i)
17895             setClass(this.box, "editor-" + classNames[i]);
17896
17897         // Make the editor match the target's font style
17898         copyTextStyles(target, this.box);
17899
17900         this.setValue(value);
17901
17902         if (this.fixedWidth)
17903             this.updateLayout(true);
17904         else
17905         {
17906             this.startMeasuring(target);
17907             this.textSize = this.measureInputText(value);
17908
17909             // Correct the height of the box to make the funky CSS drop-shadow line up
17910             var parent = this.input.parentNode;
17911             if (hasClass(parent, "textEditorInner2"))
17912             {
17913                 var yDiff = this.textSize.height - this.shadowExpand;
17914
17915                 // IE6 height offset
17916                 if (isIE6)
17917                     yDiff -= 2;
17918
17919                 parent.style.height = yDiff + "px";
17920                 parent.parentNode.style.height = yDiff + "px";
17921             }
17922
17923             this.updateLayout(true);
17924         }
17925
17926         this.getAutoCompleter().reset();
17927
17928         if (isIElt8)
17929             panel.panelNode.appendChild(this.box);
17930         else
17931             target.offsetParent.appendChild(this.box);
17932
17933         //console.log(target);
17934         //this.input.select(); // it's called bellow, with setTimeout
17935
17936         if (isIE)
17937         {
17938             // reset input style
17939             this.input.style.fontFamily = "Monospace";
17940             this.input.style.fontSize = "11px";
17941         }
17942
17943         // Insert the "expander" to cover the target element with white space
17944         if (!this.fixedWidth)
17945         {
17946             copyBoxStyles(target, this.expander);
17947
17948             target.parentNode.replaceChild(this.expander, target);
17949             collapse(target, true);
17950             this.expander.parentNode.insertBefore(target, this.expander);
17951         }
17952
17953         //TODO: xxxpedro
17954         //scrollIntoCenterView(this.box, null, true);
17955
17956         // Display the editor after change its size and position to avoid flickering
17957         this.box.style.display = "block";
17958
17959         // we need to call input.focus() and input.select() with a timeout,
17960         // otherwise it won't work on all browsers due to timing issues
17961         var self = this;
17962         setTimeout(function(){
17963             self.input.focus();
17964             self.input.select();
17965         },0);
17966     },
17967
17968     hide: function()
17969     {
17970         this.box.className = this.originalClassName;
17971
17972         if (!this.fixedWidth)
17973         {
17974             this.stopMeasuring();
17975
17976             collapse(this.target, false);
17977
17978             if (this.expander.parentNode)
17979                 this.expander.parentNode.removeChild(this.expander);
17980         }
17981
17982         if (this.box.parentNode)
17983         {
17984             ///setSelectionRange(this.input, 0, 0);
17985             this.input.blur();
17986
17987             this.box.parentNode.removeChild(this.box);
17988         }
17989
17990         delete this.target;
17991         delete this.panel;
17992     },
17993
17994     layout: function(forceAll)
17995     {
17996         if (!this.fixedWidth)
17997             this.textSize = this.measureInputText(this.input.value);
17998
17999         if (forceAll)
18000             this.targetOffset = getClientOffset(this.expander);
18001
18002         this.updateLayout(false, forceAll);
18003     },
18004
18005     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18006
18007     beginEditing: function(target, value)
18008     {
18009     },
18010
18011     saveEdit: function(target, value, previousValue)
18012     {
18013     },
18014
18015     endEditing: function(target, value, cancel)
18016     {
18017         // Remove empty groups by default
18018         return true;
18019     },
18020
18021     insertNewRow: function(target, insertWhere)
18022     {
18023     },
18024
18025     advanceToNext: function(target, charCode)
18026     {
18027         return false;
18028     },
18029
18030     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18031
18032     getAutoCompleteRange: function(value, offset)
18033     {
18034     },
18035
18036     getAutoCompleteList: function(preExpr, expr, postExpr)
18037     {
18038     },
18039
18040     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18041
18042     getAutoCompleter: function()
18043     {
18044         if (!this.autoCompleter)
18045         {
18046             this.autoCompleter = new Firebug.AutoCompleter(null,
18047                 bind(this.getAutoCompleteRange, this), bind(this.getAutoCompleteList, this),
18048                 true, false);
18049         }
18050
18051         return this.autoCompleter;
18052     },
18053
18054     completeValue: function(amt)
18055     {
18056         //console.log("completeValue");
18057
18058         var selectRangeCallback = this.getAutoCompleter().complete(currentPanel.context, this.input, true, amt < 0);
18059
18060         if (selectRangeCallback)
18061         {
18062             Firebug.Editor.update(true);
18063
18064             // We need to select the editor text after calling update in Safari/Chrome,
18065             // otherwise the text won't be selected
18066             if (isSafari)
18067                 setTimeout(selectRangeCallback,0);
18068             else
18069                 selectRangeCallback();
18070         }
18071         else
18072             this.incrementValue(amt);
18073     },
18074
18075     incrementValue: function(amt)
18076     {
18077         var value = this.input.value;
18078
18079         // TODO: xxxpedro editor
18080         if (isIE)
18081             var start = getInputSelectionStart(this.input), end = start;
18082         else
18083             var start = this.input.selectionStart, end = this.input.selectionEnd;
18084
18085         //debugger;
18086         var range = this.getAutoCompleteRange(value, start);
18087         if (!range || range.type != "int")
18088             range = {start: 0, end: value.length-1};
18089
18090         var expr = value.substr(range.start, range.end-range.start+1);
18091         preExpr = value.substr(0, range.start);
18092         postExpr = value.substr(range.end+1);
18093
18094         // See if the value is an integer, and if so increment it
18095         var intValue = parseInt(expr);
18096         if (!!intValue || intValue == 0)
18097         {
18098             var m = /\d+/.exec(expr);
18099             var digitPost = expr.substr(m.index+m[0].length);
18100
18101             var completion = intValue-amt;
18102             this.input.value = preExpr + completion + digitPost + postExpr;
18103
18104             setSelectionRange(this.input, start, end);
18105
18106             Firebug.Editor.update(true);
18107
18108             return true;
18109         }
18110         else
18111             return false;
18112     },
18113
18114     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18115
18116     onKeyPress: function(event)
18117     {
18118         //console.log("onKeyPress", event);
18119         if (event.keyCode == 27 && !this.completeAsYouType)
18120         {
18121             var reverted = this.getAutoCompleter().revert(this.input);
18122             if (reverted)
18123                 cancelEvent(event);
18124         }
18125         else if (event.charCode && this.advanceToNext(this.target, event.charCode))
18126         {
18127             Firebug.Editor.tabNextEditor();
18128             cancelEvent(event);
18129         }
18130         else
18131         {
18132             if (this.numeric && event.charCode && (event.charCode < 48 || event.charCode > 57)
18133                 && event.charCode != 45 && event.charCode != 46)
18134                 FBL.cancelEvent(event);
18135             else
18136             {
18137                 // If the user backspaces, don't autocomplete after the upcoming input event
18138                 this.ignoreNextInput = event.keyCode == 8;
18139             }
18140         }
18141     },
18142
18143     onOverflow: function()
18144     {
18145         this.updateLayout(false, false, 3);
18146     },
18147
18148     onKeyDown: function(event)
18149     {
18150         //console.log("onKeyDown", event.keyCode);
18151         if (event.keyCode > 46 || event.keyCode == 32 || event.keyCode == 8)
18152         {
18153             this.keyDownPressed = true;
18154         }
18155     },
18156
18157     onInput: function(event)
18158     {
18159         //debugger;
18160
18161         // skip not relevant onpropertychange calls on IE
18162         if (isIE)
18163         {
18164             if (event.propertyName != "value" || !isVisible(this.input) || !this.keyDownPressed)
18165                 return;
18166
18167             this.keyDownPressed = false;
18168         }
18169
18170         //console.log("onInput", event);
18171         //console.trace();
18172
18173         var selectRangeCallback;
18174
18175         if (this.ignoreNextInput)
18176         {
18177             this.ignoreNextInput = false;
18178             this.getAutoCompleter().reset();
18179         }
18180         else if (this.completeAsYouType)
18181             selectRangeCallback = this.getAutoCompleter().complete(currentPanel.context, this.input, false);
18182         else
18183             this.getAutoCompleter().reset();
18184
18185         Firebug.Editor.update();
18186
18187         if (selectRangeCallback)
18188         {
18189             // We need to select the editor text after calling update in Safari/Chrome,
18190             // otherwise the text won't be selected
18191             if (isSafari)
18192                 setTimeout(selectRangeCallback,0);
18193             else
18194                 selectRangeCallback();
18195         }
18196     },
18197
18198     onContextMenu: function(event)
18199     {
18200         cancelEvent(event);
18201
18202         var popup = $("fbInlineEditorPopup");
18203         FBL.eraseNode(popup);
18204
18205         var target = event.target || event.srcElement;
18206         var menu = this.getContextMenuItems(target);
18207         if (menu)
18208         {
18209             for (var i = 0; i < menu.length; ++i)
18210                 FBL.createMenuItem(popup, menu[i]);
18211         }
18212
18213         if (!popup.firstChild)
18214             return false;
18215
18216         popup.openPopupAtScreen(event.screenX, event.screenY, true);
18217         return true;
18218     },
18219
18220     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18221
18222     updateLayout: function(initial, forceAll, extraWidth)
18223     {
18224         if (this.fixedWidth)
18225         {
18226             this.box.style.left = (this.targetOffset.x) + "px";
18227             this.box.style.top = (this.targetOffset.y) + "px";
18228
18229             var w = this.target.offsetWidth;
18230             var h = this.target.offsetHeight;
18231             this.input.style.width = w + "px";
18232             this.input.style.height = (h-3) + "px";
18233         }
18234         else
18235         {
18236             if (initial || forceAll)
18237             {
18238                 this.box.style.left = this.targetOffset.x + "px";
18239                 this.box.style.top = this.targetOffset.y + "px";
18240             }
18241
18242             var approxTextWidth = this.textSize.width;
18243             var maxWidth = (currentPanel.panelNode.scrollWidth - this.targetOffset.x)
18244                 - this.outerMargin;
18245
18246             var wrapped = initial
18247                 ? this.noWrap && this.targetSize.height > this.textSize.height+3
18248                 : this.noWrap && approxTextWidth > maxWidth;
18249
18250             if (wrapped)
18251             {
18252                 var style = isIE ?
18253                         this.target.currentStyle :
18254                         this.target.ownerDocument.defaultView.getComputedStyle(this.target, "");
18255
18256                 targetMargin = parseInt(style.marginLeft) + parseInt(style.marginRight);
18257
18258                 // Make the width fit the remaining x-space from the offset to the far right
18259                 approxTextWidth = maxWidth - targetMargin;
18260
18261                 this.input.style.width = "100%";
18262                 this.box.style.width = approxTextWidth + "px";
18263             }
18264             else
18265             {
18266                 // Make the input one character wider than the text value so that
18267                 // typing does not ever cause the textbox to scroll
18268                 var charWidth = this.measureInputText('m').width;
18269
18270                 // Sometimes we need to make the editor a little wider, specifically when
18271                 // an overflow happens, otherwise it will scroll off some text on the left
18272                 if (extraWidth)
18273                     charWidth *= extraWidth;
18274
18275                 var inputWidth = approxTextWidth + charWidth;
18276
18277                 if (initial)
18278                 {
18279                     if (isIE)
18280                     {
18281                         // TODO: xxxpedro
18282                         var xDiff = 13;
18283                         this.box.style.width = (inputWidth + xDiff) + "px";
18284                     }
18285                     else
18286                         this.box.style.width = "auto";
18287                 }
18288                 else
18289                 {
18290                     // TODO: xxxpedro
18291                     var xDiff = isIE ? 13: this.box.scrollWidth - this.input.offsetWidth;
18292                     this.box.style.width = (inputWidth + xDiff) + "px";
18293                 }
18294
18295                 this.input.style.width = inputWidth + "px";
18296             }
18297
18298             this.expander.style.width = approxTextWidth + "px";
18299             this.expander.style.height = Math.max(this.textSize.height-3,0) + "px";
18300         }
18301
18302         if (forceAll)
18303             scrollIntoCenterView(this.box, null, true);
18304     }
18305 });
18306
18307 // ************************************************************************************************
18308 // Autocompletion
18309
18310 Firebug.AutoCompleter = function(getExprOffset, getRange, evaluator, selectMode, caseSensitive)
18311 {
18312     var candidates = null;
18313     var originalValue = null;
18314     var originalOffset = -1;
18315     var lastExpr = null;
18316     var lastOffset = -1;
18317     var exprOffset = 0;
18318     var lastIndex = 0;
18319     var preParsed = null;
18320     var preExpr = null;
18321     var postExpr = null;
18322
18323     this.revert = function(textBox)
18324     {
18325         if (originalOffset != -1)
18326         {
18327             textBox.value = originalValue;
18328
18329             setSelectionRange(textBox, originalOffset, originalOffset);
18330
18331             this.reset();
18332             return true;
18333         }
18334         else
18335         {
18336             this.reset();
18337             return false;
18338         }
18339     };
18340
18341     this.reset = function()
18342     {
18343         candidates = null;
18344         originalValue = null;
18345         originalOffset = -1;
18346         lastExpr = null;
18347         lastOffset = 0;
18348         exprOffset = 0;
18349     };
18350
18351     this.complete = function(context, textBox, cycle, reverse)
18352     {
18353         //console.log("complete", context, textBox, cycle, reverse);
18354         // TODO: xxxpedro important port to firebug (variable leak)
18355         //var value = lastValue = textBox.value;
18356         var value = textBox.value;
18357
18358         //var offset = textBox.selectionStart;
18359         var offset = getInputSelectionStart(textBox);
18360
18361         // The result of selectionStart() in Safari/Chrome is 1 unit less than the result
18362         // in Firefox. Therefore, we need to manually adjust the value here.
18363         if (isSafari && !cycle && offset >= 0) offset++;
18364
18365         if (!selectMode && originalOffset != -1)
18366             offset = originalOffset;
18367
18368         if (!candidates || !cycle || offset != lastOffset)
18369         {
18370             originalOffset = offset;
18371             originalValue = value;
18372
18373             // Find the part of the string that will be parsed
18374             var parseStart = getExprOffset ? getExprOffset(value, offset, context) : 0;
18375             preParsed = value.substr(0, parseStart);
18376             var parsed = value.substr(parseStart);
18377
18378             // Find the part of the string that is being completed
18379             var range = getRange ? getRange(parsed, offset-parseStart, context) : null;
18380             if (!range)
18381                 range = {start: 0, end: parsed.length-1 };
18382
18383             var expr = parsed.substr(range.start, range.end-range.start+1);
18384             preExpr = parsed.substr(0, range.start);
18385             postExpr = parsed.substr(range.end+1);
18386             exprOffset = parseStart + range.start;
18387
18388             if (!cycle)
18389             {
18390                 if (!expr)
18391                     return;
18392                 else if (lastExpr && lastExpr.indexOf(expr) != 0)
18393                 {
18394                     candidates = null;
18395                 }
18396                 else if (lastExpr && lastExpr.length >= expr.length)
18397                 {
18398                     candidates = null;
18399                     lastExpr = expr;
18400                     return;
18401                 }
18402             }
18403
18404             lastExpr = expr;
18405             lastOffset = offset;
18406
18407             var searchExpr;
18408
18409             // Check if the cursor is at the very right edge of the expression, or
18410             // somewhere in the middle of it
18411             if (expr && offset != parseStart+range.end+1)
18412             {
18413                 if (cycle)
18414                 {
18415                     // We are in the middle of the expression, but we can
18416                     // complete by cycling to the next item in the values
18417                     // list after the expression
18418                     offset = range.start;
18419                     searchExpr = expr;
18420                     expr = "";
18421                 }
18422                 else
18423                 {
18424                     // We can't complete unless we are at the ridge edge
18425                     return;
18426                 }
18427             }
18428
18429             var values = evaluator(preExpr, expr, postExpr, context);
18430             if (!values)
18431                 return;
18432
18433             if (expr)
18434             {
18435                 // Filter the list of values to those which begin with expr. We
18436                 // will then go on to complete the first value in the resulting list
18437                 candidates = [];
18438
18439                 if (caseSensitive)
18440                 {
18441                     for (var i = 0; i < values.length; ++i)
18442                     {
18443                         var name = values[i];
18444                         if (name.indexOf && name.indexOf(expr) == 0)
18445                             candidates.push(name);
18446                     }
18447                 }
18448                 else
18449                 {
18450                     var lowerExpr = caseSensitive ? expr : expr.toLowerCase();
18451                     for (var i = 0; i < values.length; ++i)
18452                     {
18453                         var name = values[i];
18454                         if (name.indexOf && name.toLowerCase().indexOf(lowerExpr) == 0)
18455                             candidates.push(name);
18456                     }
18457                 }
18458
18459                 lastIndex = reverse ? candidates.length-1 : 0;
18460             }
18461             else if (searchExpr)
18462             {
18463                 var searchIndex = -1;
18464
18465                 // Find the first instance of searchExpr in the values list. We
18466                 // will then complete the string that is found
18467                 if (caseSensitive)
18468                 {
18469                     searchIndex = values.indexOf(expr);
18470                 }
18471                 else
18472                 {
18473                     var lowerExpr = searchExpr.toLowerCase();
18474                     for (var i = 0; i < values.length; ++i)
18475                     {
18476                         var name = values[i];
18477                         if (name && name.toLowerCase().indexOf(lowerExpr) == 0)
18478                         {
18479                             searchIndex = i;
18480                             break;
18481                         }
18482                     }
18483                 }
18484
18485                 // Nothing found, so there's nothing to complete to
18486                 if (searchIndex == -1)
18487                     return this.reset();
18488
18489                 expr = searchExpr;
18490                 candidates = cloneArray(values);
18491                 lastIndex = searchIndex;
18492             }
18493             else
18494             {
18495                 expr = "";
18496                 candidates = [];
18497                 for (var i = 0; i < values.length; ++i)
18498                 {
18499                     if (values[i].substr)
18500                         candidates.push(values[i]);
18501                 }
18502                 lastIndex = -1;
18503             }
18504         }
18505
18506         if (cycle)
18507         {
18508             expr = lastExpr;
18509             lastIndex += reverse ? -1 : 1;
18510         }
18511
18512         if (!candidates.length)
18513             return;
18514
18515         if (lastIndex >= candidates.length)
18516             lastIndex = 0;
18517         else if (lastIndex < 0)
18518             lastIndex = candidates.length-1;
18519
18520         var completion = candidates[lastIndex];
18521         var preCompletion = expr.substr(0, offset-exprOffset);
18522         var postCompletion = completion.substr(offset-exprOffset);
18523
18524         textBox.value = preParsed + preExpr + preCompletion + postCompletion + postExpr;
18525         var offsetEnd = preParsed.length + preExpr.length + completion.length;
18526
18527         // TODO: xxxpedro remove the following commented code, if the lib.setSelectionRange()
18528         // is working well.
18529         /*
18530         if (textBox.setSelectionRange)
18531         {
18532             // we must select the range with a timeout, otherwise the text won't
18533             // be properly selected (because after this function executes, the editor's
18534             // input will be resized to fit the whole text)
18535             setTimeout(function(){
18536                 if (selectMode)
18537                     textBox.setSelectionRange(offset, offsetEnd);
18538                 else
18539                     textBox.setSelectionRange(offsetEnd, offsetEnd);
18540             },0);
18541         }
18542         /**/
18543
18544         // we must select the range with a timeout, otherwise the text won't
18545         // be properly selected (because after this function executes, the editor's
18546         // input will be resized to fit the whole text)
18547         /*
18548         setTimeout(function(){
18549             if (selectMode)
18550                 setSelectionRange(textBox, offset, offsetEnd);
18551             else
18552                 setSelectionRange(textBox, offsetEnd, offsetEnd);
18553         },0);
18554
18555         return true;
18556         /**/
18557
18558         // The editor text should be selected only after calling the editor.update()
18559         // in Safari/Chrome, otherwise the text won't be selected. So, we're returning
18560         // a function to be called later (in the proper time for all browsers).
18561         //
18562         // TODO: xxxpedro see if we can move the editor.update() calls to here, and avoid
18563         // returning a closure. the complete() function seems to be called only twice in
18564         // editor.js. See if this function is called anywhere else (like css.js for example).
18565         return function(){
18566             //console.log("autocomplete ", textBox, offset, offsetEnd);
18567
18568             if (selectMode)
18569                 setSelectionRange(textBox, offset, offsetEnd);
18570             else
18571                 setSelectionRange(textBox, offsetEnd, offsetEnd);
18572         };
18573         /**/
18574     };
18575 };
18576
18577 // ************************************************************************************************
18578 // Local Helpers
18579
18580 var getDefaultEditor = function getDefaultEditor(panel)
18581 {
18582     if (!defaultEditor)
18583     {
18584         var doc = panel.document;
18585         defaultEditor = new Firebug.InlineEditor(doc);
18586     }
18587
18588     return defaultEditor;
18589 }
18590
18591 /**
18592  * An outsider is the first element matching the stepper element that
18593  * is not an child of group. Elements tagged with insertBefore or insertAfter
18594  * classes are also excluded from these results unless they are the sibling
18595  * of group, relative to group's parent editGroup. This allows for the proper insertion
18596  * rows when groups are nested.
18597  */
18598 var getOutsider = function getOutsider(element, group, stepper)
18599 {
18600     var parentGroup = getAncestorByClass(group.parentNode, "editGroup");
18601     var next;
18602     do
18603     {
18604         next = stepper(next || element);
18605     }
18606     while (isAncestor(next, group) || isGroupInsert(next, parentGroup));
18607
18608     return next;
18609 }
18610
18611 var isGroupInsert = function isGroupInsert(next, group)
18612 {
18613     return (!group || isAncestor(next, group))
18614         && (hasClass(next, "insertBefore") || hasClass(next, "insertAfter"));
18615 }
18616
18617 var getNextOutsider = function getNextOutsider(element, group)
18618 {
18619     return getOutsider(element, group, bind(getNextByClass, FBL, "editable"));
18620 }
18621
18622 var getPreviousOutsider = function getPreviousOutsider(element, group)
18623 {
18624     return getOutsider(element, group, bind(getPreviousByClass, FBL, "editable"));
18625 }
18626
18627 var getInlineParent = function getInlineParent(element)
18628 {
18629     var lastInline = element;
18630     for (; element; element = element.parentNode)
18631     {
18632         //var s = element.ownerDocument.defaultView.getComputedStyle(element, "");
18633         var s = isIE ?
18634                 element.currentStyle :
18635                 element.ownerDocument.defaultView.getComputedStyle(element, "");
18636
18637         if (s.display != "inline")
18638             return lastInline;
18639         else
18640             lastInline = element;
18641     }
18642     return null;
18643 }
18644
18645 var insertTab = function insertTab()
18646 {
18647     insertTextIntoElement(currentEditor.input, Firebug.Editor.tabCharacter);
18648 }
18649
18650 // ************************************************************************************************
18651
18652 Firebug.registerModule(Firebug.Editor);
18653
18654 // ************************************************************************************************
18655
18656 }});
18657
18658
18659 /* See license.txt for terms of usage */
18660
18661 FBL.ns(function() { with (FBL) {
18662 // ************************************************************************************************
18663
18664 if (Env.Options.disableXHRListener)
18665     return;
18666
18667 // ************************************************************************************************
18668 // XHRSpy
18669
18670 var XHRSpy = function()
18671 {
18672     this.requestHeaders = [];
18673     this.responseHeaders = [];
18674 };
18675
18676 XHRSpy.prototype =
18677 {
18678     method: null,
18679     url: null,
18680     async: null,
18681
18682     xhrRequest: null,
18683
18684     href: null,
18685
18686     loaded: false,
18687
18688     logRow: null,
18689
18690     responseText: null,
18691
18692     requestHeaders: null,
18693     responseHeaders: null,
18694
18695     sourceLink: null, // {href:"file.html", line: 22}
18696
18697     getURL: function()
18698     {
18699         return this.href;
18700     }
18701 };
18702
18703 // ************************************************************************************************
18704 // XMLHttpRequestWrapper
18705
18706 var XMLHttpRequestWrapper = function(activeXObject)
18707 {
18708     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18709     // XMLHttpRequestWrapper internal variables
18710
18711     var xhrRequest = typeof activeXObject != "undefined" ?
18712                 activeXObject :
18713                 new _XMLHttpRequest(),
18714
18715         spy = new XHRSpy(),
18716
18717         self = this,
18718
18719         reqType,
18720         reqUrl,
18721         reqStartTS;
18722
18723     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18724     // XMLHttpRequestWrapper internal methods
18725
18726     var updateSelfPropertiesIgnore = {
18727         abort: 1,
18728         channel: 1,
18729         getAllResponseHeaders: 1,
18730         getInterface: 1,
18731         getResponseHeader: 1,
18732         mozBackgroundRequest: 1,
18733         multipart: 1,
18734         onreadystatechange: 1,
18735         open: 1,
18736         send: 1,
18737         setRequestHeader: 1
18738     };
18739
18740     var updateSelfProperties = function()
18741     {
18742         if (supportsXHRIterator)
18743         {
18744             for (var propName in xhrRequest)
18745             {
18746                 if (propName in updateSelfPropertiesIgnore)
18747                     continue;
18748
18749                 try
18750                 {
18751                     var propValue = xhrRequest[propName];
18752
18753                     if (propValue && !isFunction(propValue))
18754                         self[propName] = propValue;
18755                 }
18756                 catch(E)
18757                 {
18758                     //console.log(propName, E.message);
18759                 }
18760             }
18761         }
18762         else
18763         {
18764             // will fail to read these xhrRequest properties if the request is not completed
18765             if (xhrRequest.readyState == 4)
18766             {
18767                 self.status = xhrRequest.status;
18768                 self.statusText = xhrRequest.statusText;
18769                 self.responseText = xhrRequest.responseText;
18770                 self.responseXML = xhrRequest.responseXML;
18771             }
18772         }
18773     };
18774
18775     var updateXHRPropertiesIgnore = {
18776         channel: 1,
18777         onreadystatechange: 1,
18778         readyState: 1,
18779         responseBody: 1,
18780         responseText: 1,
18781         responseXML: 1,
18782         status: 1,
18783         statusText: 1,
18784         upload: 1
18785     };
18786
18787     var updateXHRProperties = function()
18788     {
18789         for (var propName in self)
18790         {
18791             if (propName in updateXHRPropertiesIgnore)
18792                 continue;
18793
18794             try
18795             {
18796                 var propValue = self[propName];
18797
18798                 if (propValue && !xhrRequest[propName])
18799                 {
18800                     xhrRequest[propName] = propValue;
18801                 }
18802             }
18803             catch(E)
18804             {
18805                 //console.log(propName, E.message);
18806             }
18807         }
18808     };
18809
18810     var logXHR = function()
18811     {
18812         var row = Firebug.Console.log(spy, null, "spy", Firebug.Spy.XHR);
18813
18814         if (row)
18815         {
18816             setClass(row, "loading");
18817             spy.logRow = row;
18818         }
18819     };
18820
18821     var finishXHR = function()
18822     {
18823         var duration = new Date().getTime() - reqStartTS;
18824         var success = xhrRequest.status == 200;
18825
18826         var responseHeadersText = xhrRequest.getAllResponseHeaders();
18827         var responses = responseHeadersText ? responseHeadersText.split(/[\n\r]/) : [];
18828         var reHeader = /^(\S+):\s*(.*)/;
18829
18830         for (var i=0, l=responses.length; i<l; i++)
18831         {
18832             var text = responses[i];
18833             var match = text.match(reHeader);
18834
18835             if (match)
18836             {
18837                 var name = match[1];
18838                 var value = match[2];
18839
18840                 // update the spy mimeType property so we can detect when to show
18841                 // custom response viewers (such as HTML, XML or JSON viewer)
18842                 if (name == "Content-Type")
18843                     spy.mimeType = value;
18844
18845                 /*
18846                 if (name == "Last Modified")
18847                 {
18848                     if (!spy.cacheEntry)
18849                         spy.cacheEntry = [];
18850
18851                     spy.cacheEntry.push({
18852                        name: [name],
18853                        value: [value]
18854                     });
18855                 }
18856                 /**/
18857
18858                 spy.responseHeaders.push({
18859                    name: [name],
18860                    value: [value]
18861                 });
18862             }
18863         }
18864
18865         with({
18866             row: spy.logRow,
18867             status: xhrRequest.status == 0 ?
18868                         // if xhrRequest.status == 0 then accessing xhrRequest.statusText
18869                         // will cause an error, so we must handle this case (Issue 3504)
18870                         "" : xhrRequest.status + " " + xhrRequest.statusText,
18871             time: duration,
18872             success: success
18873         })
18874         {
18875             setTimeout(function(){
18876
18877                 spy.responseText = xhrRequest.responseText;
18878
18879                 // update row information to avoid "ethernal spinning gif" bug in IE
18880                 row = row || spy.logRow;
18881
18882                 // if chrome document is not loaded, there will be no row yet, so just ignore
18883                 if (!row) return;
18884
18885                 // update the XHR representation data
18886                 handleRequestStatus(success, status, time);
18887
18888             },200);
18889         }
18890
18891         spy.loaded = true;
18892         /*
18893         // commented because they are being updated by the updateSelfProperties() function
18894         self.status = xhrRequest.status;
18895         self.statusText = xhrRequest.statusText;
18896         self.responseText = xhrRequest.responseText;
18897         self.responseXML = xhrRequest.responseXML;
18898         /**/
18899         updateSelfProperties();
18900     };
18901
18902     var handleStateChange = function()
18903     {
18904         //Firebug.Console.log(["onreadystatechange", xhrRequest.readyState, xhrRequest.readyState == 4 && xhrRequest.status]);
18905
18906         self.readyState = xhrRequest.readyState;
18907
18908         if (xhrRequest.readyState == 4)
18909         {
18910             finishXHR();
18911
18912             xhrRequest.onreadystatechange = function(){};
18913         }
18914
18915         //Firebug.Console.log(spy.url + ": " + xhrRequest.readyState);
18916
18917         self.onreadystatechange();
18918     };
18919
18920     // update the XHR representation data
18921     var handleRequestStatus = function(success, status, time)
18922     {
18923         var row = spy.logRow;
18924         FBL.removeClass(row, "loading");
18925
18926         if (!success)
18927             FBL.setClass(row, "error");
18928
18929         var item = FBL.$$(".spyStatus", row)[0];
18930         item.innerHTML = status;
18931
18932         if (time)
18933         {
18934             var item = FBL.$$(".spyTime", row)[0];
18935             item.innerHTML = time + "ms";
18936         }
18937     };
18938
18939     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18940     // XMLHttpRequestWrapper public properties and handlers
18941
18942     this.readyState = 0;
18943
18944     this.onreadystatechange = function(){};
18945
18946     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18947     // XMLHttpRequestWrapper public methods
18948
18949     this.open = function(method, url, async, user, password)
18950     {
18951         //Firebug.Console.log("xhrRequest open");
18952
18953         updateSelfProperties();
18954
18955         if (spy.loaded)
18956             spy = new XHRSpy();
18957
18958         spy.method = method;
18959         spy.url = url;
18960         spy.async = async;
18961         spy.href = url;
18962         spy.xhrRequest = xhrRequest;
18963         spy.urlParams = parseURLParamsArray(url);
18964
18965         try
18966         {
18967             // xhrRequest.open.apply may not be available in IE
18968             if (supportsApply)
18969                 xhrRequest.open.apply(xhrRequest, arguments);
18970             else
18971                 xhrRequest.open(method, url, async, user, password);
18972         }
18973         catch(e)
18974         {
18975         }
18976
18977         xhrRequest.onreadystatechange = handleStateChange;
18978
18979     };
18980
18981     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18982
18983     this.send = function(data)
18984     {
18985         //Firebug.Console.log("xhrRequest send");
18986         spy.data = data;
18987
18988         reqStartTS = new Date().getTime();
18989
18990         updateXHRProperties();
18991
18992         try
18993         {
18994             xhrRequest.send(data);
18995         }
18996         catch(e)
18997         {
18998             // TODO: xxxpedro XHR throws or not?
18999             //throw e;
19000         }
19001         finally
19002         {
19003             logXHR();
19004
19005             if (!spy.async)
19006             {
19007                 self.readyState = xhrRequest.readyState;
19008
19009                 // sometimes an error happens when calling finishXHR()
19010                 // Issue 3422: Firebug Lite breaks Google Instant Search
19011                 try
19012                 {
19013                     finishXHR();
19014                 }
19015                 catch(E)
19016                 {
19017                 }
19018             }
19019         }
19020     };
19021
19022     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19023
19024     this.setRequestHeader = function(header, value)
19025     {
19026         spy.requestHeaders.push({name: [header], value: [value]});
19027         return xhrRequest.setRequestHeader(header, value);
19028     };
19029
19030
19031     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19032
19033     this.abort = function()
19034     {
19035         xhrRequest.abort();
19036         updateSelfProperties();
19037         handleRequestStatus(false, "Aborted");
19038     };
19039
19040     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19041
19042     this.getResponseHeader = function(header)
19043     {
19044         return xhrRequest.getResponseHeader(header);
19045     };
19046
19047     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19048
19049     this.getAllResponseHeaders = function()
19050     {
19051         return xhrRequest.getAllResponseHeaders();
19052     };
19053
19054     /**/
19055     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19056     // Clone XHR object
19057
19058     // xhrRequest.open.apply not available in IE and will throw an error in
19059     // IE6 by simply reading xhrRequest.open so we must sniff it
19060     var supportsApply = !isIE6 &&
19061             xhrRequest &&
19062             xhrRequest.open &&
19063             typeof xhrRequest.open.apply != "undefined";
19064
19065     var numberOfXHRProperties = 0;
19066     for (var propName in xhrRequest)
19067     {
19068         numberOfXHRProperties++;
19069
19070         if (propName in updateSelfPropertiesIgnore)
19071             continue;
19072
19073         try
19074         {
19075             var propValue = xhrRequest[propName];
19076
19077             if (isFunction(propValue))
19078             {
19079                 if (typeof self[propName] == "undefined")
19080                 {
19081                     this[propName] = (function(name, xhr){
19082
19083                         return supportsApply ?
19084                             // if the browser supports apply
19085                             function()
19086                             {
19087                                 return xhr[name].apply(xhr, arguments);
19088                             }
19089                             :
19090                             function(a,b,c,d,e)
19091                             {
19092                                 return xhr[name](a,b,c,d,e);
19093                             };
19094
19095                     })(propName, xhrRequest);
19096                 }
19097             }
19098             else
19099                 this[propName] = propValue;
19100         }
19101         catch(E)
19102         {
19103             //console.log(propName, E.message);
19104         }
19105     }
19106
19107     // IE6 does not support for (var prop in XHR)
19108     var supportsXHRIterator = numberOfXHRProperties > 0;
19109
19110     /**/
19111
19112     return this;
19113 };
19114
19115 // ************************************************************************************************
19116 // ActiveXObject Wrapper (IE6 only)
19117
19118 var _ActiveXObject;
19119 var isIE6 =  /msie 6/i.test(navigator.appVersion);
19120
19121 if (isIE6)
19122 {
19123     _ActiveXObject = window.ActiveXObject;
19124
19125     var xhrObjects = " MSXML2.XMLHTTP.5.0 MSXML2.XMLHTTP.4.0 MSXML2.XMLHTTP.3.0 MSXML2.XMLHTTP Microsoft.XMLHTTP ";
19126
19127     window.ActiveXObject = function(name)
19128     {
19129         var error = null;
19130
19131         try
19132         {
19133             var activeXObject = new _ActiveXObject(name);
19134         }
19135         catch(e)
19136         {
19137             error = e;
19138         }
19139         finally
19140         {
19141             if (!error)
19142             {
19143                 if (xhrObjects.indexOf(" " + name + " ") != -1)
19144                     return new XMLHttpRequestWrapper(activeXObject);
19145                 else
19146                     return activeXObject;
19147             }
19148             else
19149                 throw error.message;
19150         }
19151     };
19152 }
19153
19154 // ************************************************************************************************
19155
19156 // Register the XMLHttpRequestWrapper for non-IE6 browsers
19157 if (!isIE6)
19158 {
19159     var _XMLHttpRequest = XMLHttpRequest;
19160     window.XMLHttpRequest = function()
19161     {
19162         return new XMLHttpRequestWrapper();
19163     };
19164 }
19165
19166 //************************************************************************************************
19167
19168 FBL.getNativeXHRObject = function()
19169 {
19170     var xhrObj = false;
19171     try
19172     {
19173         xhrObj = new _XMLHttpRequest();
19174     }
19175     catch(e)
19176     {
19177         var progid = [
19178                 "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0",
19179                 "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"
19180             ];
19181
19182         for ( var i=0; i < progid.length; ++i ) {
19183             try
19184             {
19185                 xhrObj = new _ActiveXObject(progid[i]);
19186             }
19187             catch(e)
19188             {
19189                 continue;
19190             }
19191             break;
19192         }
19193     }
19194     finally
19195     {
19196         return xhrObj;
19197     }
19198 };
19199
19200 // ************************************************************************************************
19201 }});
19202
19203
19204 /* See license.txt for terms of usage */
19205
19206 FBL.ns(function() { with (FBL) {
19207 // ************************************************************************************************
19208
19209 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19210
19211 var reIgnore = /about:|javascript:|resource:|chrome:|jar:/;
19212 var layoutInterval = 300;
19213 var indentWidth = 18;
19214
19215 var cacheSession = null;
19216 var contexts = new Array();
19217 var panelName = "net";
19218 var maxQueueRequests = 500;
19219 //var panelBar1 = $("fbPanelBar1"); // chrome not available at startup
19220 var activeRequests = [];
19221
19222 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19223
19224 var mimeExtensionMap =
19225 {
19226     "txt": "text/plain",
19227     "html": "text/html",
19228     "htm": "text/html",
19229     "xhtml": "text/html",
19230     "xml": "text/xml",
19231     "css": "text/css",
19232     "js": "application/x-javascript",
19233     "jss": "application/x-javascript",
19234     "jpg": "image/jpg",
19235     "jpeg": "image/jpeg",
19236     "gif": "image/gif",
19237     "png": "image/png",
19238     "bmp": "image/bmp",
19239     "swf": "application/x-shockwave-flash",
19240     "flv": "video/x-flv"
19241 };
19242
19243 var fileCategories =
19244 {
19245     "undefined": 1,
19246     "html": 1,
19247     "css": 1,
19248     "js": 1,
19249     "xhr": 1,
19250     "image": 1,
19251     "flash": 1,
19252     "txt": 1,
19253     "bin": 1
19254 };
19255
19256 var textFileCategories =
19257 {
19258     "txt": 1,
19259     "html": 1,
19260     "xhr": 1,
19261     "css": 1,
19262     "js": 1
19263 };
19264
19265 var binaryFileCategories =
19266 {
19267     "bin": 1,
19268     "flash": 1
19269 };
19270
19271 var mimeCategoryMap =
19272 {
19273     "text/plain": "txt",
19274     "application/octet-stream": "bin",
19275     "text/html": "html",
19276     "text/xml": "html",
19277     "text/css": "css",
19278     "application/x-javascript": "js",
19279     "text/javascript": "js",
19280     "application/javascript" : "js",
19281     "image/jpeg": "image",
19282     "image/jpg": "image",
19283     "image/gif": "image",
19284     "image/png": "image",
19285     "image/bmp": "image",
19286     "application/x-shockwave-flash": "flash",
19287     "video/x-flv": "flash"
19288 };
19289
19290 var binaryCategoryMap =
19291 {
19292     "image": 1,
19293     "flash" : 1
19294 };
19295
19296 // ************************************************************************************************
19297
19298 /**
19299  * @module Represents a module object for the Net panel. This object is derived
19300  * from <code>Firebug.ActivableModule</code> in order to support activation (enable/disable).
19301  * This allows to avoid (performance) expensive features if the functionality is not necessary
19302  * for the user.
19303  */
19304 Firebug.NetMonitor = extend(Firebug.ActivableModule,
19305 {
19306     dispatchName: "netMonitor",
19307
19308     clear: function(context)
19309     {
19310         // The user pressed a Clear button so, remove content of the panel...
19311         var panel = context.getPanel(panelName, true);
19312         if (panel)
19313             panel.clear();
19314     },
19315
19316     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19317     // extends Module
19318
19319     initialize: function()
19320     {
19321         return;
19322
19323         this.panelName = panelName;
19324
19325         Firebug.ActivableModule.initialize.apply(this, arguments);
19326
19327         if (Firebug.TraceModule)
19328             Firebug.TraceModule.addListener(this.TraceListener);
19329
19330         // HTTP observer must be registered now (and not in monitorContext, since if a
19331         // page is opened in a new tab the top document request would be missed otherwise.
19332         NetHttpObserver.registerObserver();
19333         NetHttpActivityObserver.registerObserver();
19334
19335         Firebug.Debugger.addListener(this.DebuggerListener);
19336     },
19337
19338     shutdown: function()
19339     {
19340         return;
19341
19342         prefs.removeObserver(Firebug.prefDomain, this, false);
19343         if (Firebug.TraceModule)
19344             Firebug.TraceModule.removeListener(this.TraceListener);
19345
19346         NetHttpObserver.unregisterObserver();
19347         NetHttpActivityObserver.unregisterObserver();
19348
19349         Firebug.Debugger.removeListener(this.DebuggerListener);
19350     }
19351 });
19352
19353
19354 /**
19355  * @domplate Represents a template that is used to reneder detailed info about a request.
19356  * This template is rendered when a request is expanded.
19357  */
19358 Firebug.NetMonitor.NetInfoBody = domplate(Firebug.Rep, new Firebug.Listener(),
19359 {
19360     tag:
19361         DIV({"class": "netInfoBody", _repObject: "$file"},
19362             TAG("$infoTabs", {file: "$file"}),
19363             TAG("$infoBodies", {file: "$file"})
19364         ),
19365
19366     infoTabs:
19367         DIV({"class": "netInfoTabs focusRow subFocusRow", "role": "tablist"},
19368             A({"class": "netInfoParamsTab netInfoTab a11yFocus", onclick: "$onClickTab", "role": "tab",
19369                 view: "Params",
19370                 $collapsed: "$file|hideParams"},
19371                 $STR("URLParameters")
19372             ),
19373             A({"class": "netInfoHeadersTab netInfoTab a11yFocus", onclick: "$onClickTab", "role": "tab",
19374                 view: "Headers"},
19375                 $STR("Headers")
19376             ),
19377             A({"class": "netInfoPostTab netInfoTab a11yFocus", onclick: "$onClickTab", "role": "tab",
19378                 view: "Post",
19379                 $collapsed: "$file|hidePost"},
19380                 $STR("Post")
19381             ),
19382             A({"class": "netInfoPutTab netInfoTab a11yFocus", onclick: "$onClickTab", "role": "tab",
19383                 view: "Put",
19384                 $collapsed: "$file|hidePut"},
19385                 $STR("Put")
19386             ),
19387             A({"class": "netInfoResponseTab netInfoTab a11yFocus", onclick: "$onClickTab", "role": "tab",
19388                 view: "Response",
19389                 $collapsed: "$file|hideResponse"},
19390                 $STR("Response")
19391             ),
19392             A({"class": "netInfoCacheTab netInfoTab a11yFocus", onclick: "$onClickTab", "role": "tab",
19393                view: "Cache",
19394                $collapsed: "$file|hideCache"},
19395                $STR("Cache")
19396             ),
19397             A({"class": "netInfoHtmlTab netInfoTab a11yFocus", onclick: "$onClickTab", "role": "tab",
19398                view: "Html",
19399                $collapsed: "$file|hideHtml"},
19400                $STR("HTML")
19401             )
19402         ),
19403
19404     infoBodies:
19405         DIV({"class": "netInfoBodies outerFocusRow"},
19406             TABLE({"class": "netInfoParamsText netInfoText netInfoParamsTable", "role": "tabpanel",
19407                     cellpadding: 0, cellspacing: 0}, TBODY()),
19408             DIV({"class": "netInfoHeadersText netInfoText", "role": "tabpanel"}),
19409             DIV({"class": "netInfoPostText netInfoText", "role": "tabpanel"}),
19410             DIV({"class": "netInfoPutText netInfoText", "role": "tabpanel"}),
19411             PRE({"class": "netInfoResponseText netInfoText", "role": "tabpanel"}),
19412             DIV({"class": "netInfoCacheText netInfoText", "role": "tabpanel"},
19413                 TABLE({"class": "netInfoCacheTable", cellpadding: 0, cellspacing: 0, "role": "presentation"},
19414                     TBODY({"role": "list", "aria-label": $STR("Cache")})
19415                 )
19416             ),
19417             DIV({"class": "netInfoHtmlText netInfoText", "role": "tabpanel"},
19418                 IFRAME({"class": "netInfoHtmlPreview", "role": "document"})
19419             )
19420         ),
19421
19422     headerDataTag:
19423         FOR("param", "$headers",
19424             TR({"role": "listitem"},
19425                 TD({"class": "netInfoParamName", "role": "presentation"},
19426                     TAG("$param|getNameTag", {param: "$param"})
19427                 ),
19428                 TD({"class": "netInfoParamValue", "role": "list", "aria-label": "$param.name"},
19429                     FOR("line", "$param|getParamValueIterator",
19430                         CODE({"class": "focusRow subFocusRow", "role": "listitem"}, "$line")
19431                     )
19432                 )
19433             )
19434         ),
19435
19436     customTab:
19437         A({"class": "netInfo$tabId\\Tab netInfoTab", onclick: "$onClickTab", view: "$tabId", "role": "tab"},
19438             "$tabTitle"
19439         ),
19440
19441     customBody:
19442         DIV({"class": "netInfo$tabId\\Text netInfoText", "role": "tabpanel"}),
19443
19444     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19445
19446     nameTag:
19447         SPAN("$param|getParamName"),
19448
19449     nameWithTooltipTag:
19450         SPAN({title: "$param.name"}, "$param|getParamName"),
19451
19452     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19453
19454     getNameTag: function(param)
19455     {
19456         return (this.getParamName(param) == param.name) ? this.nameTag : this.nameWithTooltipTag;
19457     },
19458
19459     getParamName: function(param)
19460     {
19461         var limit = 25;
19462         var name = param.name;
19463         if (name.length > limit)
19464             name = name.substr(0, limit) + "...";
19465         return name;
19466     },
19467
19468     getParamTitle: function(param)
19469     {
19470         var limit = 25;
19471         var name = param.name;
19472         if (name.length > limit)
19473             return name;
19474         return "";
19475     },
19476
19477     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19478
19479     hideParams: function(file)
19480     {
19481         return !file.urlParams || !file.urlParams.length;
19482     },
19483
19484     hidePost: function(file)
19485     {
19486         return file.method.toUpperCase() != "POST";
19487     },
19488
19489     hidePut: function(file)
19490     {
19491         return file.method.toUpperCase() != "PUT";
19492     },
19493
19494     hideResponse: function(file)
19495     {
19496         return false;
19497         //return file.category in binaryFileCategories;
19498     },
19499
19500     hideCache: function(file)
19501     {
19502         return true;
19503         //xxxHonza: I don't see any reason why not to display the cache also info for images.
19504         return !file.cacheEntry; // || file.category=="image";
19505     },
19506
19507     hideHtml: function(file)
19508     {
19509         return (file.mimeType != "text/html") && (file.mimeType != "application/xhtml+xml");
19510     },
19511
19512     onClickTab: function(event)
19513     {
19514         this.selectTab(event.currentTarget || event.srcElement);
19515     },
19516
19517     getParamValueIterator: function(param)
19518     {
19519         // TODO: xxxpedro console2
19520         return param.value;
19521
19522         // This value is inserted into CODE element and so, make sure the HTML isn't escaped (1210).
19523         // This is why the second parameter is true.
19524         // The CODE (with style white-space:pre) element preserves whitespaces so they are
19525         // displayed the same, as they come from the server (1194).
19526         // In case of a long header values of post parameters the value must be wrapped (2105).
19527         return wrapText(param.value, true);
19528     },
19529
19530     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19531
19532     appendTab: function(netInfoBox, tabId, tabTitle)
19533     {
19534         // Create new tab and body.
19535         var args = {tabId: tabId, tabTitle: tabTitle};
19536         ///this.customTab.append(args, netInfoBox.getElementsByClassName("netInfoTabs").item(0));
19537         ///this.customBody.append(args, netInfoBox.getElementsByClassName("netInfoBodies").item(0));
19538         this.customTab.append(args, $$(".netInfoTabs", netInfoBox)[0]);
19539         this.customBody.append(args, $$(".netInfoBodies", netInfoBox)[0]);
19540     },
19541
19542     selectTabByName: function(netInfoBox, tabName)
19543     {
19544         var tab = getChildByClass(netInfoBox, "netInfoTabs", "netInfo"+tabName+"Tab");
19545         if (tab)
19546             this.selectTab(tab);
19547     },
19548
19549     selectTab: function(tab)
19550     {
19551         var view = tab.getAttribute("view");
19552
19553         var netInfoBox = getAncestorByClass(tab, "netInfoBody");
19554
19555         var selectedTab = netInfoBox.selectedTab;
19556
19557         if (selectedTab)
19558         {
19559             //netInfoBox.selectedText.removeAttribute("selected");
19560             removeClass(netInfoBox.selectedText, "netInfoTextSelected");
19561
19562             removeClass(selectedTab, "netInfoTabSelected");
19563             //selectedTab.removeAttribute("selected");
19564             selectedTab.setAttribute("aria-selected", "false");
19565         }
19566
19567         var textBodyName = "netInfo" + view + "Text";
19568
19569         selectedTab = netInfoBox.selectedTab = tab;
19570
19571         netInfoBox.selectedText = $$("."+textBodyName, netInfoBox)[0];
19572         //netInfoBox.selectedText = netInfoBox.getElementsByClassName(textBodyName).item(0);
19573
19574         //netInfoBox.selectedText.setAttribute("selected", "true");
19575         setClass(netInfoBox.selectedText, "netInfoTextSelected");
19576
19577         setClass(selectedTab, "netInfoTabSelected");
19578         selectedTab.setAttribute("selected", "true");
19579         selectedTab.setAttribute("aria-selected", "true");
19580
19581         var file = Firebug.getRepObject(netInfoBox);
19582
19583         //var context = Firebug.getElementPanel(netInfoBox).context;
19584         var context = Firebug.chrome;
19585
19586         this.updateInfo(netInfoBox, file, context);
19587     },
19588
19589     updateInfo: function(netInfoBox, file, context)
19590     {
19591         if (FBTrace.DBG_NET)
19592             FBTrace.sysout("net.updateInfo; file", file);
19593
19594         if (!netInfoBox)
19595         {
19596             if (FBTrace.DBG_NET || FBTrace.DBG_ERRORS)
19597                 FBTrace.sysout("net.updateInfo; ERROR netInfo == null " + file.href, file);
19598             return;
19599         }
19600
19601         var tab = netInfoBox.selectedTab;
19602
19603         if (hasClass(tab, "netInfoParamsTab"))
19604         {
19605             if (file.urlParams && !netInfoBox.urlParamsPresented)
19606             {
19607                 netInfoBox.urlParamsPresented = true;
19608                 this.insertHeaderRows(netInfoBox, file.urlParams, "Params");
19609             }
19610         }
19611
19612         else if (hasClass(tab, "netInfoHeadersTab"))
19613         {
19614             var headersText = $$(".netInfoHeadersText", netInfoBox)[0];
19615             //var headersText = netInfoBox.getElementsByClassName("netInfoHeadersText").item(0);
19616
19617             if (file.responseHeaders && !netInfoBox.responseHeadersPresented)
19618             {
19619                 netInfoBox.responseHeadersPresented = true;
19620                 NetInfoHeaders.renderHeaders(headersText, file.responseHeaders, "ResponseHeaders");
19621             }
19622
19623             if (file.requestHeaders && !netInfoBox.requestHeadersPresented)
19624             {
19625                 netInfoBox.requestHeadersPresented = true;
19626                 NetInfoHeaders.renderHeaders(headersText, file.requestHeaders, "RequestHeaders");
19627             }
19628         }
19629
19630         else if (hasClass(tab, "netInfoPostTab"))
19631         {
19632             if (!netInfoBox.postPresented)
19633             {
19634                 netInfoBox.postPresented  = true;
19635                 //var postText = netInfoBox.getElementsByClassName("netInfoPostText").item(0);
19636                 var postText = $$(".netInfoPostText", netInfoBox)[0];
19637                 NetInfoPostData.render(context, postText, file);
19638             }
19639         }
19640
19641         else if (hasClass(tab, "netInfoPutTab"))
19642         {
19643             if (!netInfoBox.putPresented)
19644             {
19645                 netInfoBox.putPresented  = true;
19646                 //var putText = netInfoBox.getElementsByClassName("netInfoPutText").item(0);
19647                 var putText = $$(".netInfoPutText", netInfoBox)[0];
19648                 NetInfoPostData.render(context, putText, file);
19649             }
19650         }
19651
19652         else if (hasClass(tab, "netInfoResponseTab") && file.loaded && !netInfoBox.responsePresented)
19653         {
19654             ///var responseTextBox = netInfoBox.getElementsByClassName("netInfoResponseText").item(0);
19655             var responseTextBox = $$(".netInfoResponseText", netInfoBox)[0];
19656             if (file.category == "image")
19657             {
19658                 netInfoBox.responsePresented = true;
19659
19660                 var responseImage = netInfoBox.ownerDocument.createElement("img");
19661                 responseImage.src = file.href;
19662
19663                 clearNode(responseTextBox);
19664                 responseTextBox.appendChild(responseImage, responseTextBox);
19665             }
19666             else ///if (!(binaryCategoryMap.hasOwnProperty(file.category)))
19667             {
19668                 this.setResponseText(file, netInfoBox, responseTextBox, context);
19669             }
19670         }
19671
19672         else if (hasClass(tab, "netInfoCacheTab") && file.loaded && !netInfoBox.cachePresented)
19673         {
19674             var responseTextBox = netInfoBox.getElementsByClassName("netInfoCacheText").item(0);
19675             if (file.cacheEntry) {
19676                 netInfoBox.cachePresented = true;
19677                 this.insertHeaderRows(netInfoBox, file.cacheEntry, "Cache");
19678             }
19679         }
19680
19681         else if (hasClass(tab, "netInfoHtmlTab") && file.loaded && !netInfoBox.htmlPresented)
19682         {
19683             netInfoBox.htmlPresented = true;
19684
19685             var text = Utils.getResponseText(file, context);
19686
19687             ///var iframe = netInfoBox.getElementsByClassName("netInfoHtmlPreview").item(0);
19688             var iframe = $$(".netInfoHtmlPreview", netInfoBox)[0];
19689
19690             ///iframe.contentWindow.document.body.innerHTML = text;
19691
19692             // TODO: xxxpedro net - remove scripts
19693             var reScript = /<script(.|\s)*?\/script>/gi;
19694
19695             text = text.replace(reScript, "");
19696
19697             iframe.contentWindow.document.write(text);
19698             iframe.contentWindow.document.close();
19699         }
19700
19701         // Notify listeners about update so, content of custom tabs can be updated.
19702         dispatch(NetInfoBody.fbListeners, "updateTabBody", [netInfoBox, file, context]);
19703     },
19704
19705     setResponseText: function(file, netInfoBox, responseTextBox, context)
19706     {
19707         //**********************************************
19708         //**********************************************
19709         //**********************************************
19710         netInfoBox.responsePresented = true;
19711         // line breaks somehow are different in IE
19712         // make this only once in the initialization? we don't have net panels and modules yet.
19713         if (isIE)
19714             responseTextBox.style.whiteSpace = "nowrap";
19715
19716         responseTextBox[
19717                 typeof responseTextBox.textContent != "undefined" ?
19718                         "textContent" :
19719                         "innerText"
19720             ] = file.responseText;
19721
19722         return;
19723         //**********************************************
19724         //**********************************************
19725         //**********************************************
19726
19727         // Get response text and make sure it doesn't exceed the max limit.
19728         var text = Utils.getResponseText(file, context);
19729         var limit = Firebug.netDisplayedResponseLimit + 15;
19730         var limitReached = text ? (text.length > limit) : false;
19731         if (limitReached)
19732             text = text.substr(0, limit) + "...";
19733
19734         // Insert the response into the UI.
19735         if (text)
19736             insertWrappedText(text, responseTextBox);
19737         else
19738             insertWrappedText("", responseTextBox);
19739
19740         // Append a message informing the user that the response isn't fully displayed.
19741         if (limitReached)
19742         {
19743             var object = {
19744                 text: $STR("net.responseSizeLimitMessage"),
19745                 onClickLink: function() {
19746                     var panel = context.getPanel("net", true);
19747                     panel.openResponseInTab(file);
19748                 }
19749             };
19750             Firebug.NetMonitor.ResponseSizeLimit.append(object, responseTextBox);
19751         }
19752
19753         netInfoBox.responsePresented = true;
19754
19755         if (FBTrace.DBG_NET)
19756             FBTrace.sysout("net.setResponseText; response text updated");
19757     },
19758
19759     insertHeaderRows: function(netInfoBox, headers, tableName, rowName)
19760     {
19761         if (!headers.length)
19762             return;
19763
19764         var headersTable = $$(".netInfo"+tableName+"Table", netInfoBox)[0];
19765         //var headersTable = netInfoBox.getElementsByClassName("netInfo"+tableName+"Table").item(0);
19766         var tbody = getChildByClass(headersTable, "netInfo" + rowName + "Body");
19767         if (!tbody)
19768             tbody = headersTable.firstChild;
19769         var titleRow = getChildByClass(tbody, "netInfo" + rowName + "Title");
19770
19771         this.headerDataTag.insertRows({headers: headers}, titleRow ? titleRow : tbody);
19772         removeClass(titleRow, "collapsed");
19773     }
19774 });
19775
19776 var NetInfoBody = Firebug.NetMonitor.NetInfoBody;
19777
19778 // ************************************************************************************************
19779
19780 /**
19781  * @domplate Used within the Net panel to display raw source of request and response headers
19782  * as well as pretty-formatted summary of these headers.
19783  */
19784 Firebug.NetMonitor.NetInfoHeaders = domplate(Firebug.Rep, //new Firebug.Listener(),
19785 {
19786     tag:
19787         DIV({"class": "netInfoHeadersTable", "role": "tabpanel"},
19788             DIV({"class": "netInfoHeadersGroup netInfoResponseHeadersTitle"},
19789                 SPAN($STR("ResponseHeaders")),
19790                 SPAN({"class": "netHeadersViewSource response collapsed", onclick: "$onViewSource",
19791                     _sourceDisplayed: false, _rowName: "ResponseHeaders"},
19792                     $STR("net.headers.view source")
19793                 )
19794             ),
19795             TABLE({cellpadding: 0, cellspacing: 0},
19796                 TBODY({"class": "netInfoResponseHeadersBody", "role": "list",
19797                     "aria-label": $STR("ResponseHeaders")})
19798             ),
19799             DIV({"class": "netInfoHeadersGroup netInfoRequestHeadersTitle"},
19800                 SPAN($STR("RequestHeaders")),
19801                 SPAN({"class": "netHeadersViewSource request collapsed", onclick: "$onViewSource",
19802                     _sourceDisplayed: false, _rowName: "RequestHeaders"},
19803                     $STR("net.headers.view source")
19804                 )
19805             ),
19806             TABLE({cellpadding: 0, cellspacing: 0},
19807                 TBODY({"class": "netInfoRequestHeadersBody", "role": "list",
19808                     "aria-label": $STR("RequestHeaders")})
19809             )
19810         ),
19811
19812     sourceTag:
19813         TR({"role": "presentation"},
19814             TD({colspan: 2, "role": "presentation"},
19815                 PRE({"class": "source"})
19816             )
19817         ),
19818
19819     onViewSource: function(event)
19820     {
19821         var target = event.target;
19822         var requestHeaders = (target.rowName == "RequestHeaders");
19823
19824         var netInfoBox = getAncestorByClass(target, "netInfoBody");
19825         var file = netInfoBox.repObject;
19826
19827         if (target.sourceDisplayed)
19828         {
19829             var headers = requestHeaders ? file.requestHeaders : file.responseHeaders;
19830             this.insertHeaderRows(netInfoBox, headers, target.rowName);
19831             target.innerHTML = $STR("net.headers.view source");
19832         }
19833         else
19834         {
19835             var source = requestHeaders ? file.requestHeadersText : file.responseHeadersText;
19836             this.insertSource(netInfoBox, source, target.rowName);
19837             target.innerHTML = $STR("net.headers.pretty print");
19838         }
19839
19840         target.sourceDisplayed = !target.sourceDisplayed;
19841
19842         cancelEvent(event);
19843     },
19844
19845     insertSource: function(netInfoBox, source, rowName)
19846     {
19847         // This breaks copy to clipboard.
19848         //if (source)
19849         //    source = source.replace(/\r\n/gm, "<span style='color:lightgray'>\\r\\n</span>\r\n");
19850
19851         ///var tbody = netInfoBox.getElementsByClassName("netInfo" + rowName + "Body").item(0);
19852         var tbody = $$(".netInfo" + rowName + "Body", netInfoBox)[0];
19853         var node = this.sourceTag.replace({}, tbody);
19854         ///var sourceNode = node.getElementsByClassName("source").item(0);
19855         var sourceNode = $$(".source", node)[0];
19856         sourceNode.innerHTML = source;
19857     },
19858
19859     insertHeaderRows: function(netInfoBox, headers, rowName)
19860     {
19861         var headersTable = $$(".netInfoHeadersTable", netInfoBox)[0];
19862         var tbody = $$(".netInfo" + rowName + "Body", headersTable)[0];
19863
19864         //var headersTable = netInfoBox.getElementsByClassName("netInfoHeadersTable").item(0);
19865         //var tbody = headersTable.getElementsByClassName("netInfo" + rowName + "Body").item(0);
19866
19867         clearNode(tbody);
19868
19869         if (!headers.length)
19870             return;
19871
19872         NetInfoBody.headerDataTag.insertRows({headers: headers}, tbody);
19873
19874         var titleRow = getChildByClass(headersTable, "netInfo" + rowName + "Title");
19875         removeClass(titleRow, "collapsed");
19876     },
19877
19878     init: function(parent)
19879     {
19880         var rootNode = this.tag.append({}, parent);
19881
19882         var netInfoBox = getAncestorByClass(parent, "netInfoBody");
19883         var file = netInfoBox.repObject;
19884
19885         var viewSource;
19886
19887         viewSource = $$(".request", rootNode)[0];
19888         //viewSource = rootNode.getElementsByClassName("netHeadersViewSource request").item(0);
19889         if (file.requestHeadersText)
19890             removeClass(viewSource, "collapsed");
19891
19892         viewSource = $$(".response", rootNode)[0];
19893         //viewSource = rootNode.getElementsByClassName("netHeadersViewSource response").item(0);
19894         if (file.responseHeadersText)
19895             removeClass(viewSource, "collapsed");
19896     },
19897
19898     renderHeaders: function(parent, headers, rowName)
19899     {
19900         if (!parent.firstChild)
19901             this.init(parent);
19902
19903         this.insertHeaderRows(parent, headers, rowName);
19904     }
19905 });
19906
19907 var NetInfoHeaders = Firebug.NetMonitor.NetInfoHeaders;
19908
19909 // ************************************************************************************************
19910
19911 /**
19912  * @domplate Represents posted data within request info (the info, which is visible when
19913  * a request entry is expanded. This template renders content of the Post tab.
19914  */
19915 Firebug.NetMonitor.NetInfoPostData = domplate(Firebug.Rep, /*new Firebug.Listener(),*/
19916 {
19917     // application/x-www-form-urlencoded
19918     paramsTable:
19919         TABLE({"class": "netInfoPostParamsTable", cellpadding: 0, cellspacing: 0, "role": "presentation"},
19920             TBODY({"role": "list", "aria-label": $STR("net.label.Parameters")},
19921                 TR({"class": "netInfoPostParamsTitle", "role": "presentation"},
19922                     TD({colspan: 3, "role": "presentation"},
19923                         DIV({"class": "netInfoPostParams"},
19924                             $STR("net.label.Parameters"),
19925                             SPAN({"class": "netInfoPostContentType"},
19926                                 "application/x-www-form-urlencoded"
19927                             )
19928                         )
19929                     )
19930                 )
19931             )
19932         ),
19933
19934     // multipart/form-data
19935     partsTable:
19936         TABLE({"class": "netInfoPostPartsTable", cellpadding: 0, cellspacing: 0, "role": "presentation"},
19937             TBODY({"role": "list", "aria-label": $STR("net.label.Parts")},
19938                 TR({"class": "netInfoPostPartsTitle", "role": "presentation"},
19939                     TD({colspan: 2, "role":"presentation" },
19940                         DIV({"class": "netInfoPostParams"},
19941                             $STR("net.label.Parts"),
19942                             SPAN({"class": "netInfoPostContentType"},
19943                                 "multipart/form-data"
19944                             )
19945                         )
19946                     )
19947                 )
19948             )
19949         ),
19950
19951     // application/json
19952     jsonTable:
19953         TABLE({"class": "netInfoPostJSONTable", cellpadding: 0, cellspacing: 0, "role": "presentation"},
19954             ///TBODY({"role": "list", "aria-label": $STR("jsonviewer.tab.JSON")},
19955             TBODY({"role": "list", "aria-label": $STR("JSON")},
19956                 TR({"class": "netInfoPostJSONTitle", "role": "presentation"},
19957                     TD({"role": "presentation" },
19958                         DIV({"class": "netInfoPostParams"},
19959                             ///$STR("jsonviewer.tab.JSON")
19960                             $STR("JSON")
19961                         )
19962                     )
19963                 ),
19964                 TR(
19965                     TD({"class": "netInfoPostJSONBody"})
19966                 )
19967             )
19968         ),
19969
19970     // application/xml
19971     xmlTable:
19972         TABLE({"class": "netInfoPostXMLTable", cellpadding: 0, cellspacing: 0, "role": "presentation"},
19973             TBODY({"role": "list", "aria-label": $STR("xmlviewer.tab.XML")},
19974                 TR({"class": "netInfoPostXMLTitle", "role": "presentation"},
19975                     TD({"role": "presentation" },
19976                         DIV({"class": "netInfoPostParams"},
19977                             $STR("xmlviewer.tab.XML")
19978                         )
19979                     )
19980                 ),
19981                 TR(
19982                     TD({"class": "netInfoPostXMLBody"})
19983                 )
19984             )
19985         ),
19986
19987     sourceTable:
19988         TABLE({"class": "netInfoPostSourceTable", cellpadding: 0, cellspacing: 0, "role": "presentation"},
19989             TBODY({"role": "list", "aria-label": $STR("net.label.Source")},
19990                 TR({"class": "netInfoPostSourceTitle", "role": "presentation"},
19991                     TD({colspan: 2, "role": "presentation"},
19992                         DIV({"class": "netInfoPostSource"},
19993                             $STR("net.label.Source")
19994                         )
19995                     )
19996                 )
19997             )
19998         ),
19999
20000     sourceBodyTag:
20001         TR({"role": "presentation"},
20002             TD({colspan: 2, "role": "presentation"},
20003                 FOR("line", "$param|getParamValueIterator",
20004                     CODE({"class":"focusRow subFocusRow" , "role": "listitem"},"$line")
20005                 )
20006             )
20007         ),
20008
20009     getParamValueIterator: function(param)
20010     {
20011         return NetInfoBody.getParamValueIterator(param);
20012     },
20013
20014     render: function(context, parentNode, file)
20015     {
20016         //debugger;
20017         var spy = getAncestorByClass(parentNode, "spyHead");
20018         var spyObject = spy.repObject;
20019         var data = spyObject.data;
20020
20021         ///var contentType = Utils.findHeader(file.requestHeaders, "content-type");
20022         var contentType = file.mimeType;
20023
20024         ///var text = Utils.getPostText(file, context, true);
20025         ///if (text == undefined)
20026         ///    return;
20027
20028         ///if (Utils.isURLEncodedRequest(file, context))
20029         // fake Utils.isURLEncodedRequest identification
20030         if (contentType && contentType == "application/x-www-form-urlencoded" ||
20031             data && data.indexOf("=") != -1)
20032         {
20033             ///var lines = text.split("\n");
20034             ///var params = parseURLEncodedText(lines[lines.length-1]);
20035             var params = parseURLEncodedTextArray(data);
20036             if (params)
20037                 this.insertParameters(parentNode, params);
20038         }
20039
20040         ///if (Utils.isMultiPartRequest(file, context))
20041         ///{
20042         ///    var data = this.parseMultiPartText(file, context);
20043         ///    if (data)
20044         ///        this.insertParts(parentNode, data);
20045         ///}
20046
20047         // moved to the top
20048         ///var contentType = Utils.findHeader(file.requestHeaders, "content-type");
20049
20050         ///if (Firebug.JSONViewerModel.isJSON(contentType))
20051         var jsonData = {
20052             responseText: data
20053         };
20054
20055         if (Firebug.JSONViewerModel.isJSON(contentType, data))
20056             ///this.insertJSON(parentNode, file, context);
20057             this.insertJSON(parentNode, jsonData, context);
20058
20059         ///if (Firebug.XMLViewerModel.isXML(contentType))
20060         ///    this.insertXML(parentNode, file, context);
20061
20062         ///var postText = Utils.getPostText(file, context);
20063         ///postText = Utils.formatPostText(postText);
20064         var postText = data;
20065         if (postText)
20066             this.insertSource(parentNode, postText);
20067     },
20068
20069     insertParameters: function(parentNode, params)
20070     {
20071         if (!params || !params.length)
20072             return;
20073
20074         var paramTable = this.paramsTable.append({object:{}}, parentNode);
20075         var row = $$(".netInfoPostParamsTitle", paramTable)[0];
20076         //var paramTable = this.paramsTable.append(null, parentNode);
20077         //var row = paramTable.getElementsByClassName("netInfoPostParamsTitle").item(0);
20078
20079         var tbody = paramTable.getElementsByTagName("tbody")[0];
20080
20081         NetInfoBody.headerDataTag.insertRows({headers: params}, row);
20082     },
20083
20084     insertParts: function(parentNode, data)
20085     {
20086         if (!data.params || !data.params.length)
20087             return;
20088
20089         var partsTable = this.partsTable.append({object:{}}, parentNode);
20090         var row = $$(".netInfoPostPartsTitle", paramTable)[0];
20091         //var partsTable = this.partsTable.append(null, parentNode);
20092         //var row = partsTable.getElementsByClassName("netInfoPostPartsTitle").item(0);
20093
20094         NetInfoBody.headerDataTag.insertRows({headers: data.params}, row);
20095     },
20096
20097     insertJSON: function(parentNode, file, context)
20098     {
20099         ///var text = Utils.getPostText(file, context);
20100         var text = file.responseText;
20101         ///var data = parseJSONString(text, "http://" + file.request.originalURI.host);
20102         var data = parseJSONString(text);
20103         if (!data)
20104             return;
20105
20106         ///var jsonTable = this.jsonTable.append(null, parentNode);
20107         var jsonTable = this.jsonTable.append({}, parentNode);
20108         ///var jsonBody = jsonTable.getElementsByClassName("netInfoPostJSONBody").item(0);
20109         var jsonBody = $$(".netInfoPostJSONBody", jsonTable)[0];
20110
20111         if (!this.toggles)
20112             this.toggles = {};
20113
20114         Firebug.DOMPanel.DirTable.tag.replace(
20115             {object: data, toggles: this.toggles}, jsonBody);
20116     },
20117
20118     insertXML: function(parentNode, file, context)
20119     {
20120         var text = Utils.getPostText(file, context);
20121
20122         var jsonTable = this.xmlTable.append(null, parentNode);
20123         ///var jsonBody = jsonTable.getElementsByClassName("netInfoPostXMLBody").item(0);
20124         var jsonBody = $$(".netInfoPostXMLBody", jsonTable)[0];
20125
20126         Firebug.XMLViewerModel.insertXML(jsonBody, text);
20127     },
20128
20129     insertSource: function(parentNode, text)
20130     {
20131         var sourceTable = this.sourceTable.append({object:{}}, parentNode);
20132         var row = $$(".netInfoPostSourceTitle", sourceTable)[0];
20133         //var sourceTable = this.sourceTable.append(null, parentNode);
20134         //var row = sourceTable.getElementsByClassName("netInfoPostSourceTitle").item(0);
20135
20136         var param = {value: [text]};
20137         this.sourceBodyTag.insertRows({param: param}, row);
20138     },
20139
20140     parseMultiPartText: function(file, context)
20141     {
20142         var text = Utils.getPostText(file, context);
20143         if (text == undefined)
20144             return null;
20145
20146         FBTrace.sysout("net.parseMultiPartText; boundary: ", text);
20147
20148         var boundary = text.match(/\s*boundary=\s*(.*)/)[1];
20149
20150         var divider = "\r\n\r\n";
20151         var bodyStart = text.indexOf(divider);
20152         var body = text.substr(bodyStart + divider.length);
20153
20154         var postData = {};
20155         postData.mimeType = "multipart/form-data";
20156         postData.params = [];
20157
20158         var parts = body.split("--" + boundary);
20159         for (var i=0; i<parts.length; i++)
20160         {
20161             var part = parts[i].split(divider);
20162             if (part.length != 2)
20163                 continue;
20164
20165             var m = part[0].match(/\s*name=\"(.*)\"(;|$)/);
20166             postData.params.push({
20167                 name: (m && m.length > 1) ? m[1] : "",
20168                 value: trim(part[1])
20169             });
20170         }
20171
20172         return postData;
20173     }
20174 });
20175
20176 var NetInfoPostData = Firebug.NetMonitor.NetInfoPostData;
20177
20178 // ************************************************************************************************
20179
20180
20181 // TODO: xxxpedro net i18n
20182 var $STRP = function(a){return a;};
20183
20184 Firebug.NetMonitor.NetLimit = domplate(Firebug.Rep,
20185 {
20186     collapsed: true,
20187
20188     tableTag:
20189         DIV(
20190             TABLE({width: "100%", cellpadding: 0, cellspacing: 0},
20191                 TBODY()
20192             )
20193         ),
20194
20195     limitTag:
20196         TR({"class": "netRow netLimitRow", $collapsed: "$isCollapsed"},
20197             TD({"class": "netCol netLimitCol", colspan: 6},
20198                 TABLE({cellpadding: 0, cellspacing: 0},
20199                     TBODY(
20200                         TR(
20201                             TD(
20202                                 SPAN({"class": "netLimitLabel"},
20203                                     $STRP("plural.Limit_Exceeded", [0])
20204                                 )
20205                             ),
20206                             TD({style: "width:100%"}),
20207                             TD(
20208                                 BUTTON({"class": "netLimitButton", title: "$limitPrefsTitle",
20209                                     onclick: "$onPreferences"},
20210                                   $STR("LimitPrefs")
20211                                 )
20212                             ),
20213                             TD("&nbsp;")
20214                         )
20215                     )
20216                 )
20217             )
20218         ),
20219
20220     isCollapsed: function()
20221     {
20222         return this.collapsed;
20223     },
20224
20225     onPreferences: function(event)
20226     {
20227         openNewTab("about:config");
20228     },
20229
20230     updateCounter: function(row)
20231     {
20232         removeClass(row, "collapsed");
20233
20234         // Update info within the limit row.
20235         var limitLabel = row.getElementsByClassName("netLimitLabel").item(0);
20236         limitLabel.firstChild.nodeValue = $STRP("plural.Limit_Exceeded", [row.limitInfo.totalCount]);
20237     },
20238
20239     createTable: function(parent, limitInfo)
20240     {
20241         var table = this.tableTag.replace({}, parent);
20242         var row = this.createRow(table.firstChild.firstChild, limitInfo);
20243         return [table, row];
20244     },
20245
20246     createRow: function(parent, limitInfo)
20247     {
20248         var row = this.limitTag.insertRows(limitInfo, parent, this)[0];
20249         row.limitInfo = limitInfo;
20250         return row;
20251     },
20252
20253     // nsIPrefObserver
20254     observe: function(subject, topic, data)
20255     {
20256         // We're observing preferences only.
20257         if (topic != "nsPref:changed")
20258           return;
20259
20260         if (data.indexOf("net.logLimit") != -1)
20261             this.updateMaxLimit();
20262     },
20263
20264     updateMaxLimit: function()
20265     {
20266         var value = Firebug.getPref(Firebug.prefDomain, "net.logLimit");
20267         maxQueueRequests = value ? value : maxQueueRequests;
20268     }
20269 });
20270
20271 var NetLimit = Firebug.NetMonitor.NetLimit;
20272
20273 // ************************************************************************************************
20274
20275 Firebug.NetMonitor.ResponseSizeLimit = domplate(Firebug.Rep,
20276 {
20277     tag:
20278         DIV({"class": "netInfoResponseSizeLimit"},
20279             SPAN("$object.beforeLink"),
20280             A({"class": "objectLink", onclick: "$onClickLink"},
20281                 "$object.linkText"
20282             ),
20283             SPAN("$object.afterLink")
20284         ),
20285
20286     reLink: /^(.*)<a>(.*)<\/a>(.*$)/,
20287     append: function(obj, parent)
20288     {
20289         var m = obj.text.match(this.reLink);
20290         return this.tag.append({onClickLink: obj.onClickLink,
20291             object: {
20292             beforeLink: m[1],
20293             linkText: m[2],
20294             afterLink: m[3]
20295         }}, parent, this);
20296     }
20297 });
20298
20299 // ************************************************************************************************
20300 // ************************************************************************************************
20301
20302 Firebug.NetMonitor.Utils =
20303 {
20304     findHeader: function(headers, name)
20305     {
20306         if (!headers)
20307             return null;
20308
20309         name = name.toLowerCase();
20310         for (var i = 0; i < headers.length; ++i)
20311         {
20312             var headerName = headers[i].name.toLowerCase();
20313             if (headerName == name)
20314                 return headers[i].value;
20315         }
20316     },
20317
20318     formatPostText: function(text)
20319     {
20320         if (text instanceof XMLDocument)
20321             return getElementXML(text.documentElement);
20322         else
20323             return text;
20324     },
20325
20326     getPostText: function(file, context, noLimit)
20327     {
20328         if (!file.postText)
20329         {
20330             file.postText = readPostTextFromRequest(file.request, context);
20331
20332             if (!file.postText && context)
20333                 file.postText = readPostTextFromPage(file.href, context);
20334         }
20335
20336         if (!file.postText)
20337             return file.postText;
20338
20339         var limit = Firebug.netDisplayedPostBodyLimit;
20340         if (file.postText.length > limit && !noLimit)
20341         {
20342             return cropString(file.postText, limit,
20343                 "\n\n... " + $STR("net.postDataSizeLimitMessage") + " ...\n\n");
20344         }
20345
20346         return file.postText;
20347     },
20348
20349     getResponseText: function(file, context)
20350     {
20351         // The response can be also empty string so, check agains "undefined".
20352         return (typeof(file.responseText) != "undefined")? file.responseText :
20353             context.sourceCache.loadText(file.href, file.method, file);
20354     },
20355
20356     isURLEncodedRequest: function(file, context)
20357     {
20358         var text = Utils.getPostText(file, context);
20359         if (text && text.toLowerCase().indexOf("content-type: application/x-www-form-urlencoded") == 0)
20360             return true;
20361
20362         // The header value doesn't have to be always exactly "application/x-www-form-urlencoded",
20363         // there can be even charset specified. So, use indexOf rather than just "==".
20364         var headerValue = Utils.findHeader(file.requestHeaders, "content-type");
20365         if (headerValue && headerValue.indexOf("application/x-www-form-urlencoded") == 0)
20366             return true;
20367
20368         return false;
20369     },
20370
20371     isMultiPartRequest: function(file, context)
20372     {
20373         var text = Utils.getPostText(file, context);
20374         if (text && text.toLowerCase().indexOf("content-type: multipart/form-data") == 0)
20375             return true;
20376         return false;
20377     },
20378
20379     getMimeType: function(mimeType, uri)
20380     {
20381         if (!mimeType || !(mimeCategoryMap.hasOwnProperty(mimeType)))
20382         {
20383             var ext = getFileExtension(uri);
20384             if (!ext)
20385                 return mimeType;
20386             else
20387             {
20388                 var extMimeType = mimeExtensionMap[ext.toLowerCase()];
20389                 return extMimeType ? extMimeType : mimeType;
20390             }
20391         }
20392         else
20393             return mimeType;
20394     },
20395
20396     getDateFromSeconds: function(s)
20397     {
20398         var d = new Date();
20399         d.setTime(s*1000);
20400         return d;
20401     },
20402
20403     getHttpHeaders: function(request, file)
20404     {
20405         try
20406         {
20407             var http = QI(request, Ci.nsIHttpChannel);
20408             file.status = request.responseStatus;
20409
20410             // xxxHonza: is there any problem to do this in requestedFile method?
20411             file.method = http.requestMethod;
20412             file.urlParams = parseURLParams(file.href);
20413             file.mimeType = Utils.getMimeType(request.contentType, request.name);
20414
20415             if (!file.responseHeaders && Firebug.collectHttpHeaders)
20416             {
20417                 var requestHeaders = [], responseHeaders = [];
20418
20419                 http.visitRequestHeaders({
20420                     visitHeader: function(name, value)
20421                     {
20422                         requestHeaders.push({name: name, value: value});
20423                     }
20424                 });
20425                 http.visitResponseHeaders({
20426                     visitHeader: function(name, value)
20427                     {
20428                         responseHeaders.push({name: name, value: value});
20429                     }
20430                 });
20431
20432                 file.requestHeaders = requestHeaders;
20433                 file.responseHeaders = responseHeaders;
20434             }
20435         }
20436         catch (exc)
20437         {
20438             // An exception can be throwed e.g. when the request is aborted and
20439             // request.responseStatus is accessed.
20440             if (FBTrace.DBG_ERRORS)
20441                 FBTrace.sysout("net.getHttpHeaders FAILS " + file.href, exc);
20442         }
20443     },
20444
20445     isXHR: function(request)
20446     {
20447         try
20448         {
20449             var callbacks = request.notificationCallbacks;
20450             var xhrRequest = callbacks ? callbacks.getInterface(Ci.nsIXMLHttpRequest) : null;
20451             if (FBTrace.DBG_NET)
20452                 FBTrace.sysout("net.isXHR; " + (xhrRequest != null) + ", " + safeGetName(request));
20453
20454             return (xhrRequest != null);
20455         }
20456         catch (exc)
20457         {
20458         }
20459
20460        return false;
20461     },
20462
20463     getFileCategory: function(file)
20464     {
20465         if (file.category)
20466         {
20467             if (FBTrace.DBG_NET)
20468                 FBTrace.sysout("net.getFileCategory; current: " + file.category + " for: " + file.href, file);
20469             return file.category;
20470         }
20471
20472         if (file.isXHR)
20473         {
20474             if (FBTrace.DBG_NET)
20475                 FBTrace.sysout("net.getFileCategory; XHR for: " + file.href, file);
20476             return file.category = "xhr";
20477         }
20478
20479         if (!file.mimeType)
20480         {
20481             var ext = getFileExtension(file.href);
20482             if (ext)
20483                 file.mimeType = mimeExtensionMap[ext.toLowerCase()];
20484         }
20485
20486         /*if (FBTrace.DBG_NET)
20487             FBTrace.sysout("net.getFileCategory; " + mimeCategoryMap[file.mimeType] +
20488                 ", mimeType: " + file.mimeType + " for: " + file.href, file);*/
20489
20490         if (!file.mimeType)
20491             return "";
20492
20493         // Solve cases when charset is also specified, eg "text/html; charset=UTF-8".
20494         var mimeType = file.mimeType;
20495         if (mimeType)
20496             mimeType = mimeType.split(";")[0];
20497
20498         return (file.category = mimeCategoryMap[mimeType]);
20499     }
20500 };
20501
20502 var Utils = Firebug.NetMonitor.Utils;
20503
20504 // ************************************************************************************************
20505
20506 //Firebug.registerRep(Firebug.NetMonitor.NetRequestTable);
20507 //Firebug.registerActivableModule(Firebug.NetMonitor);
20508 //Firebug.registerPanel(NetPanel);
20509
20510 Firebug.registerModule(Firebug.NetMonitor);
20511 //Firebug.registerRep(Firebug.NetMonitor.BreakpointRep);
20512
20513 // ************************************************************************************************
20514 }});
20515
20516
20517 /* See license.txt for terms of usage */
20518
20519 FBL.ns(function() { with (FBL) {
20520
20521 // ************************************************************************************************
20522 // Constants
20523
20524 //const Cc = Components.classes;
20525 //const Ci = Components.interfaces;
20526
20527 // List of contexts with XHR spy attached.
20528 var contexts = [];
20529
20530 // ************************************************************************************************
20531 // Spy Module
20532
20533 /**
20534  * @module Represents a XHR Spy module. The main purpose of the XHR Spy feature is to monitor
20535  * XHR activity of the current page and create appropriate log into the Console panel.
20536  * This feature can be controlled by an option <i>Show XMLHttpRequests</i> (from within the
20537  * console panel).
20538  *
20539  * The module is responsible for attaching/detaching a HTTP Observers when Firebug is
20540  * activated/deactivated for a site.
20541  */
20542 Firebug.Spy = extend(Firebug.Module,
20543 /** @lends Firebug.Spy */
20544 {
20545     dispatchName: "spy",
20546
20547     initialize: function()
20548     {
20549         if (Firebug.TraceModule)
20550             Firebug.TraceModule.addListener(this.TraceListener);
20551
20552         Firebug.Module.initialize.apply(this, arguments);
20553     },
20554
20555     shutdown: function()
20556     {
20557         Firebug.Module.shutdown.apply(this, arguments);
20558
20559         if (Firebug.TraceModule)
20560             Firebug.TraceModule.removeListener(this.TraceListener);
20561     },
20562
20563     initContext: function(context)
20564     {
20565         context.spies = [];
20566
20567         if (Firebug.showXMLHttpRequests && Firebug.Console.isAlwaysEnabled())
20568             this.attachObserver(context, context.window);
20569
20570         if (FBTrace.DBG_SPY)
20571             FBTrace.sysout("spy.initContext " + contexts.length + " ", context.getName());
20572     },
20573
20574     destroyContext: function(context)
20575     {
20576         // For any spies that are in progress, remove our listeners so that they don't leak
20577         this.detachObserver(context, null);
20578
20579         if (FBTrace.DBG_SPY && context.spies.length)
20580             FBTrace.sysout("spy.destroyContext; ERROR There are leaking Spies ("
20581                 + context.spies.length + ") " + context.getName());
20582
20583         delete context.spies;
20584
20585         if (FBTrace.DBG_SPY)
20586             FBTrace.sysout("spy.destroyContext " + contexts.length + " ", context.getName());
20587     },
20588
20589     watchWindow: function(context, win)
20590     {
20591         if (Firebug.showXMLHttpRequests && Firebug.Console.isAlwaysEnabled())
20592             this.attachObserver(context, win);
20593     },
20594
20595     unwatchWindow: function(context, win)
20596     {
20597         try
20598         {
20599             // This make sure that the existing context is properly removed from "contexts" array.
20600             this.detachObserver(context, win);
20601         }
20602         catch (ex)
20603         {
20604             // Get exceptions here sometimes, so let's just ignore them
20605             // since the window is going away anyhow
20606             ERROR(ex);
20607         }
20608     },
20609
20610     updateOption: function(name, value)
20611     {
20612         // XXXjjb Honza, if Console.isEnabled(context) false, then this can't be called,
20613         // but somehow seems not correct
20614         if (name == "showXMLHttpRequests")
20615         {
20616             var tach = value ? this.attachObserver : this.detachObserver;
20617             for (var i = 0; i < TabWatcher.contexts.length; ++i)
20618             {
20619                 var context = TabWatcher.contexts[i];
20620                 iterateWindows(context.window, function(win)
20621                 {
20622                     tach.apply(this, [context, win]);
20623                 });
20624             }
20625         }
20626     },
20627
20628     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
20629     // Attaching Spy to XHR requests.
20630
20631     /**
20632      * Returns false if Spy should not be attached to XHRs executed by the specified window.
20633      */
20634     skipSpy: function(win)
20635     {
20636         if (!win)
20637             return true;
20638
20639         // Don't attach spy to chrome.
20640         var uri = safeGetWindowLocation(win);
20641         if (uri && (uri.indexOf("about:") == 0 || uri.indexOf("chrome:") == 0))
20642             return true;
20643     },
20644
20645     attachObserver: function(context, win)
20646     {
20647         if (Firebug.Spy.skipSpy(win))
20648             return;
20649
20650         for (var i=0; i<contexts.length; ++i)
20651         {
20652             if ((contexts[i].context == context) && (contexts[i].win == win))
20653                 return;
20654         }
20655
20656         // Register HTTP observers only once.
20657         if (contexts.length == 0)
20658         {
20659             httpObserver.addObserver(SpyHttpObserver, "firebug-http-event", false);
20660             SpyHttpActivityObserver.registerObserver();
20661         }
20662
20663         contexts.push({context: context, win: win});
20664
20665         if (FBTrace.DBG_SPY)
20666             FBTrace.sysout("spy.attachObserver (HTTP) " + contexts.length + " ", context.getName());
20667     },
20668
20669     detachObserver: function(context, win)
20670     {
20671         for (var i=0; i<contexts.length; ++i)
20672         {
20673             if (contexts[i].context == context)
20674             {
20675                 if (win && (contexts[i].win != win))
20676                     continue;
20677
20678                 contexts.splice(i, 1);
20679
20680                 // If no context is using spy, remvove the (only one) HTTP observer.
20681                 if (contexts.length == 0)
20682                 {
20683                     httpObserver.removeObserver(SpyHttpObserver, "firebug-http-event");
20684                     SpyHttpActivityObserver.unregisterObserver();
20685                 }
20686
20687                 if (FBTrace.DBG_SPY)
20688                     FBTrace.sysout("spy.detachObserver (HTTP) " + contexts.length + " ",
20689                         context.getName());
20690                 return;
20691             }
20692         }
20693     },
20694
20695     /**
20696      * Return XHR object that is associated with specified request <i>nsIHttpChannel</i>.
20697      * Returns null if the request doesn't represent XHR.
20698      */
20699     getXHR: function(request)
20700     {
20701         // Does also query-interface for nsIHttpChannel.
20702         if (!(request instanceof Ci.nsIHttpChannel))
20703             return null;
20704
20705         try
20706         {
20707             var callbacks = request.notificationCallbacks;
20708             return (callbacks ? callbacks.getInterface(Ci.nsIXMLHttpRequest) : null);
20709         }
20710         catch (exc)
20711         {
20712             if (exc.name == "NS_NOINTERFACE")
20713             {
20714                 if (FBTrace.DBG_SPY)
20715                     FBTrace.sysout("spy.getXHR; Request is not nsIXMLHttpRequest: " +
20716                         safeGetRequestName(request));
20717             }
20718         }
20719
20720        return null;
20721     }
20722 });
20723
20724
20725
20726
20727
20728 // ************************************************************************************************
20729
20730 /*
20731 function getSpyForXHR(request, xhrRequest, context, noCreate)
20732 {
20733     var spy = null;
20734
20735     // Iterate all existing spy objects in this context and look for one that is
20736     // already created for this request.
20737     var length = context.spies.length;
20738     for (var i=0; i<length; i++)
20739     {
20740         spy = context.spies[i];
20741         if (spy.request == request)
20742             return spy;
20743     }
20744
20745     if (noCreate)
20746         return null;
20747
20748     spy = new Firebug.Spy.XMLHttpRequestSpy(request, xhrRequest, context);
20749     context.spies.push(spy);
20750
20751     var name = request.URI.asciiSpec;
20752     var origName = request.originalURI.asciiSpec;
20753
20754     // Attach spy only to the original request. Notice that there can be more network requests
20755     // made by the same XHR if redirects are involved.
20756     if (name == origName)
20757         spy.attach();
20758
20759     if (FBTrace.DBG_SPY)
20760         FBTrace.sysout("spy.getSpyForXHR; New spy object created (" +
20761             (name == origName ? "new XHR" : "redirected XHR") + ") for: " + name, spy);
20762
20763     return spy;
20764 }
20765 /**/
20766
20767 // ************************************************************************************************
20768
20769 /**
20770  * @class This class represents a Spy object that is attached to XHR. This object
20771  * registers various listeners into the XHR in order to monitor various events fired
20772  * during the request process (onLoad, onAbort, etc.)
20773  */
20774 /*
20775 Firebug.Spy.XMLHttpRequestSpy = function(request, xhrRequest, context)
20776 {
20777     this.request = request;
20778     this.xhrRequest = xhrRequest;
20779     this.context = context;
20780     this.responseText = "";
20781
20782     // For compatibility with the Net templates.
20783     this.isXHR = true;
20784
20785     // Support for activity-observer
20786     this.transactionStarted = false;
20787     this.transactionClosed = false;
20788 };
20789 /**/
20790
20791 //Firebug.Spy.XMLHttpRequestSpy.prototype =
20792 /** @lends Firebug.Spy.XMLHttpRequestSpy */
20793 /*
20794 {
20795     attach: function()
20796     {
20797         var spy = this;
20798         this.onReadyStateChange = function(event) { onHTTPSpyReadyStateChange(spy, event); };
20799         this.onLoad = function() { onHTTPSpyLoad(spy); };
20800         this.onError = function() { onHTTPSpyError(spy); };
20801         this.onAbort = function() { onHTTPSpyAbort(spy); };
20802
20803         // xxxHonza: #502959 is still failing on Fx 3.5
20804         // Use activity distributor to identify 3.6
20805         if (SpyHttpActivityObserver.getActivityDistributor())
20806         {
20807             this.onreadystatechange = this.xhrRequest.onreadystatechange;
20808             this.xhrRequest.onreadystatechange = this.onReadyStateChange;
20809         }
20810
20811         this.xhrRequest.addEventListener("load", this.onLoad, false);
20812         this.xhrRequest.addEventListener("error", this.onError, false);
20813         this.xhrRequest.addEventListener("abort", this.onAbort, false);
20814
20815         // xxxHonza: should be removed from FB 3.6
20816         if (!SpyHttpActivityObserver.getActivityDistributor())
20817             this.context.sourceCache.addListener(this);
20818     },
20819
20820     detach: function()
20821     {
20822         // Bubble out if already detached.
20823         if (!this.onLoad)
20824             return;
20825
20826         // If the activity distributor is available, let's detach it when the XHR
20827         // transaction is closed. Since, in case of multipart XHRs the onLoad method
20828         // (readyState == 4) can be called mutliple times.
20829         // Keep in mind:
20830         // 1) It can happen that that the TRANSACTION_CLOSE event comes before
20831         // the onLoad (if the XHR is made as part of the page load) so, detach if
20832         // it's already closed.
20833         // 2) In case of immediate cache responses, the transaction doesn't have to
20834         // be started at all (or the activity observer is no available in Firefox 3.5).
20835         // So, also detach in this case.
20836         if (this.transactionStarted && !this.transactionClosed)
20837             return;
20838
20839         if (FBTrace.DBG_SPY)
20840             FBTrace.sysout("spy.detach; " + this.href);
20841
20842         // Remove itself from the list of active spies.
20843         remove(this.context.spies, this);
20844
20845         if (this.onreadystatechange)
20846             this.xhrRequest.onreadystatechange = this.onreadystatechange;
20847
20848         try { this.xhrRequest.removeEventListener("load", this.onLoad, false); } catch (e) {}
20849         try { this.xhrRequest.removeEventListener("error", this.onError, false); } catch (e) {}
20850         try { this.xhrRequest.removeEventListener("abort", this.onAbort, false); } catch (e) {}
20851
20852         this.onreadystatechange = null;
20853         this.onLoad = null;
20854         this.onError = null;
20855         this.onAbort = null;
20856
20857         // xxxHonza: shouuld be removed from FB 1.6
20858         if (!SpyHttpActivityObserver.getActivityDistributor())
20859             this.context.sourceCache.removeListener(this);
20860     },
20861
20862     getURL: function()
20863     {
20864         return this.xhrRequest.channel ? this.xhrRequest.channel.name : this.href;
20865     },
20866
20867     // Cache listener
20868     onStopRequest: function(context, request, responseText)
20869     {
20870         if (!responseText)
20871             return;
20872
20873         if (request == this.request)
20874             this.responseText = responseText;
20875     },
20876 };
20877 /**/
20878 // ************************************************************************************************
20879 /*
20880 function onHTTPSpyReadyStateChange(spy, event)
20881 {
20882     if (FBTrace.DBG_SPY)
20883         FBTrace.sysout("spy.onHTTPSpyReadyStateChange " + spy.xhrRequest.readyState +
20884             " (multipart: " + spy.xhrRequest.multipart + ")");
20885
20886     // Remember just in case spy is detached (readyState == 4).
20887     var originalHandler = spy.onreadystatechange;
20888
20889     // Force response text to be updated in the UI (in case the console entry
20890     // has been already expanded and the response tab selected).
20891     if (spy.logRow && spy.xhrRequest.readyState >= 3)
20892     {
20893         var netInfoBox = getChildByClass(spy.logRow, "spyHead", "netInfoBody");
20894         if (netInfoBox)
20895         {
20896             netInfoBox.htmlPresented = false;
20897             netInfoBox.responsePresented = false;
20898         }
20899     }
20900
20901     // If the request is loading update the end time.
20902     if (spy.xhrRequest.readyState == 3)
20903     {
20904         spy.responseTime = spy.endTime - spy.sendTime;
20905         updateTime(spy);
20906     }
20907
20908     // Request loaded. Get all the info from the request now, just in case the
20909     // XHR would be aborted in the original onReadyStateChange handler.
20910     if (spy.xhrRequest.readyState == 4)
20911     {
20912         // Cumulate response so, multipart response content is properly displayed.
20913         if (SpyHttpActivityObserver.getActivityDistributor())
20914             spy.responseText += spy.xhrRequest.responseText;
20915         else
20916         {
20917             // xxxHonza: remove from FB 1.6
20918             if (!spy.responseText)
20919                 spy.responseText = spy.xhrRequest.responseText;
20920         }
20921
20922         // The XHR is loaded now (used also by the activity observer).
20923         spy.loaded = true;
20924
20925         // Update UI.
20926         updateHttpSpyInfo(spy);
20927
20928         // Notify Net pane about a request beeing loaded.
20929         // xxxHonza: I don't think this is necessary.
20930         var netProgress = spy.context.netProgress;
20931         if (netProgress)
20932             netProgress.post(netProgress.stopFile, [spy.request, spy.endTime, spy.postText, spy.responseText]);
20933
20934         // Notify registered listeners about finish of the XHR.
20935         dispatch(Firebug.Spy.fbListeners, "onLoad", [spy.context, spy]);
20936     }
20937
20938     // Pass the event to the original page handler.
20939     callPageHandler(spy, event, originalHandler);
20940 }
20941
20942 function onHTTPSpyLoad(spy)
20943 {
20944     if (FBTrace.DBG_SPY)
20945         FBTrace.sysout("spy.onHTTPSpyLoad: " + spy.href, spy);
20946
20947     // Detach must be done in onLoad (not in onreadystatechange) otherwise
20948     // onAbort would not be handled.
20949     spy.detach();
20950
20951     // xxxHonza: Still needed for Fx 3.5 (#502959)
20952     if (!SpyHttpActivityObserver.getActivityDistributor())
20953         onHTTPSpyReadyStateChange(spy, null);
20954 }
20955
20956 function onHTTPSpyError(spy)
20957 {
20958     if (FBTrace.DBG_SPY)
20959         FBTrace.sysout("spy.onHTTPSpyError; " + spy.href, spy);
20960
20961     spy.detach();
20962     spy.loaded = true;
20963
20964     if (spy.logRow)
20965     {
20966         removeClass(spy.logRow, "loading");
20967         setClass(spy.logRow, "error");
20968     }
20969 }
20970
20971 function onHTTPSpyAbort(spy)
20972 {
20973     if (FBTrace.DBG_SPY)
20974         FBTrace.sysout("spy.onHTTPSpyAbort: " + spy.href, spy);
20975
20976     spy.detach();
20977     spy.loaded = true;
20978
20979     if (spy.logRow)
20980     {
20981         removeClass(spy.logRow, "loading");
20982         setClass(spy.logRow, "error");
20983     }
20984
20985     spy.statusText = "Aborted";
20986     updateLogRow(spy);
20987
20988     // Notify Net pane about a request beeing aborted.
20989     // xxxHonza: the net panel shoud find out this itself.
20990     var netProgress = spy.context.netProgress;
20991     if (netProgress)
20992         netProgress.post(netProgress.abortFile, [spy.request, spy.endTime, spy.postText, spy.responseText]);
20993 }
20994 /**/
20995
20996 // ************************************************************************************************
20997
20998 /**
20999  * @domplate Represents a template for XHRs logged in the Console panel. The body of the
21000  * log (displayed when expanded) is rendered using {@link Firebug.NetMonitor.NetInfoBody}.
21001  */
21002
21003 Firebug.Spy.XHR = domplate(Firebug.Rep,
21004 /** @lends Firebug.Spy.XHR */
21005
21006 {
21007     tag:
21008         DIV({"class": "spyHead", _repObject: "$object"},
21009             TABLE({"class": "spyHeadTable focusRow outerFocusRow", cellpadding: 0, cellspacing: 0,
21010                 "role": "listitem", "aria-expanded": "false"},
21011                 TBODY({"role": "presentation"},
21012                     TR({"class": "spyRow"},
21013                         TD({"class": "spyTitleCol spyCol", onclick: "$onToggleBody"},
21014                             DIV({"class": "spyTitle"},
21015                                 "$object|getCaption"
21016                             ),
21017                             DIV({"class": "spyFullTitle spyTitle"},
21018                                 "$object|getFullUri"
21019                             )
21020                         ),
21021                         TD({"class": "spyCol"},
21022                             DIV({"class": "spyStatus"}, "$object|getStatus")
21023                         ),
21024                         TD({"class": "spyCol"},
21025                             SPAN({"class": "spyIcon"})
21026                         ),
21027                         TD({"class": "spyCol"},
21028                             SPAN({"class": "spyTime"})
21029                         ),
21030                         TD({"class": "spyCol"},
21031                             TAG(FirebugReps.SourceLink.tag, {object: "$object.sourceLink"})
21032                         )
21033                     )
21034                 )
21035             )
21036         ),
21037
21038     getCaption: function(spy)
21039     {
21040         return spy.method.toUpperCase() + " " + cropString(spy.getURL(), 100);
21041     },
21042
21043     getFullUri: function(spy)
21044     {
21045         return spy.method.toUpperCase() + " " + spy.getURL();
21046     },
21047
21048     getStatus: function(spy)
21049     {
21050         var text = "";
21051         if (spy.statusCode)
21052             text += spy.statusCode + " ";
21053
21054         if (spy.statusText)
21055             return text += spy.statusText;
21056
21057         return text;
21058     },
21059
21060     onToggleBody: function(event)
21061     {
21062         var target = event.currentTarget || event.srcElement;
21063         var logRow = getAncestorByClass(target, "logRow-spy");
21064
21065         if (isLeftClick(event))
21066         {
21067             toggleClass(logRow, "opened");
21068
21069             var spy = getChildByClass(logRow, "spyHead").repObject;
21070             var spyHeadTable = getAncestorByClass(target, "spyHeadTable");
21071
21072             if (hasClass(logRow, "opened"))
21073             {
21074                 updateHttpSpyInfo(spy, logRow);
21075                 if (spyHeadTable)
21076                     spyHeadTable.setAttribute('aria-expanded', 'true');
21077             }
21078             else
21079             {
21080                 //var netInfoBox = getChildByClass(spy.logRow, "spyHead", "netInfoBody");
21081                 //dispatch(Firebug.NetMonitor.NetInfoBody.fbListeners, "destroyTabBody", [netInfoBox, spy]);
21082                 //if (spyHeadTable)
21083                 //    spyHeadTable.setAttribute('aria-expanded', 'false');
21084             }
21085         }
21086     },
21087
21088     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
21089
21090     copyURL: function(spy)
21091     {
21092         copyToClipboard(spy.getURL());
21093     },
21094
21095     copyParams: function(spy)
21096     {
21097         var text = spy.postText;
21098         if (!text)
21099             return;
21100
21101         var url = reEncodeURL(spy, text, true);
21102         copyToClipboard(url);
21103     },
21104
21105     copyResponse: function(spy)
21106     {
21107         copyToClipboard(spy.responseText);
21108     },
21109
21110     openInTab: function(spy)
21111     {
21112         openNewTab(spy.getURL(), spy.postText);
21113     },
21114
21115     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
21116
21117     supportsObject: function(object)
21118     {
21119         // TODO: xxxpedro spy xhr
21120         return false;
21121
21122         return object instanceof Firebug.Spy.XMLHttpRequestSpy;
21123     },
21124
21125     browseObject: function(spy, context)
21126     {
21127         var url = spy.getURL();
21128         openNewTab(url);
21129         return true;
21130     },
21131
21132     getRealObject: function(spy, context)
21133     {
21134         return spy.xhrRequest;
21135     },
21136
21137     getContextMenuItems: function(spy)
21138     {
21139         var items = [
21140             {label: "CopyLocation", command: bindFixed(this.copyURL, this, spy) }
21141         ];
21142
21143         if (spy.postText)
21144         {
21145             items.push(
21146                 {label: "CopyLocationParameters", command: bindFixed(this.copyParams, this, spy) }
21147             );
21148         }
21149
21150         items.push(
21151             {label: "CopyResponse", command: bindFixed(this.copyResponse, this, spy) },
21152             "-",
21153             {label: "OpenInTab", command: bindFixed(this.openInTab, this, spy) }
21154         );
21155
21156         return items;
21157     }
21158 });
21159
21160 // ************************************************************************************************
21161
21162 function updateTime(spy)
21163 {
21164     var timeBox = spy.logRow.getElementsByClassName("spyTime").item(0);
21165     if (spy.responseTime)
21166         timeBox.textContent = " " + formatTime(spy.responseTime);
21167 }
21168
21169 function updateLogRow(spy)
21170 {
21171     updateTime(spy);
21172
21173     var statusBox = spy.logRow.getElementsByClassName("spyStatus").item(0);
21174     statusBox.textContent = Firebug.Spy.XHR.getStatus(spy);
21175
21176     removeClass(spy.logRow, "loading");
21177     setClass(spy.logRow, "loaded");
21178
21179     try
21180     {
21181         var errorRange = Math.floor(spy.xhrRequest.status/100);
21182         if (errorRange == 4 || errorRange == 5)
21183             setClass(spy.logRow, "error");
21184     }
21185     catch (exc)
21186     {
21187     }
21188 }
21189
21190 var updateHttpSpyInfo = function updateHttpSpyInfo(spy, logRow)
21191 {
21192     if (!spy.logRow && logRow)
21193         spy.logRow = logRow;
21194
21195     if (!spy.logRow || !hasClass(spy.logRow, "opened"))
21196         return;
21197
21198     if (!spy.params)
21199         //spy.params = parseURLParams(spy.href+"");
21200         spy.params = parseURLParams(spy.href+"");
21201
21202     if (!spy.requestHeaders)
21203         spy.requestHeaders = getRequestHeaders(spy);
21204
21205     if (!spy.responseHeaders && spy.loaded)
21206         spy.responseHeaders = getResponseHeaders(spy);
21207
21208     var template = Firebug.NetMonitor.NetInfoBody;
21209     var netInfoBox = getChildByClass(spy.logRow, "spyHead", "netInfoBody");
21210     if (!netInfoBox)
21211     {
21212         var head = getChildByClass(spy.logRow, "spyHead");
21213         netInfoBox = template.tag.append({"file": spy}, head);
21214         dispatch(template.fbListeners, "initTabBody", [netInfoBox, spy]);
21215         template.selectTabByName(netInfoBox, "Response");
21216     }
21217     else
21218     {
21219         template.updateInfo(netInfoBox, spy, spy.context);
21220     }
21221 };
21222
21223
21224
21225 // ************************************************************************************************
21226
21227 function getRequestHeaders(spy)
21228 {
21229     var headers = [];
21230
21231     var channel = spy.xhrRequest.channel;
21232     if (channel instanceof Ci.nsIHttpChannel)
21233     {
21234         channel.visitRequestHeaders({
21235             visitHeader: function(name, value)
21236             {
21237                 headers.push({name: name, value: value});
21238             }
21239         });
21240     }
21241
21242     return headers;
21243 }
21244
21245 function getResponseHeaders(spy)
21246 {
21247     var headers = [];
21248
21249     try
21250     {
21251         var channel = spy.xhrRequest.channel;
21252         if (channel instanceof Ci.nsIHttpChannel)
21253         {
21254             channel.visitResponseHeaders({
21255                 visitHeader: function(name, value)
21256                 {
21257                     headers.push({name: name, value: value});
21258                 }
21259             });
21260         }
21261     }
21262     catch (exc)
21263     {
21264         if (FBTrace.DBG_SPY || FBTrace.DBG_ERRORS)
21265             FBTrace.sysout("spy.getResponseHeaders; EXCEPTION " +
21266                 safeGetRequestName(spy.request), exc);
21267     }
21268
21269     return headers;
21270 }
21271
21272 // ************************************************************************************************
21273 // Registration
21274
21275 Firebug.registerModule(Firebug.Spy);
21276 //Firebug.registerRep(Firebug.Spy.XHR);
21277
21278 // ************************************************************************************************
21279 }});
21280
21281
21282 /* See license.txt for terms of usage */
21283
21284 FBL.ns(function() { with (FBL) {
21285
21286 // ************************************************************************************************
21287
21288 // List of JSON content types.
21289 var contentTypes =
21290 {
21291     // TODO: create issue: jsonViewer will not try to evaluate the contents of the requested file
21292     // if the content-type is set to "text/plain"
21293     //"text/plain": 1,
21294     "text/javascript": 1,
21295     "text/x-javascript": 1,
21296     "text/json": 1,
21297     "text/x-json": 1,
21298     "application/json": 1,
21299     "application/x-json": 1,
21300     "application/javascript": 1,
21301     "application/x-javascript": 1,
21302     "application/json-rpc": 1
21303 };
21304
21305 // ************************************************************************************************
21306 // Model implementation
21307
21308 Firebug.JSONViewerModel = extend(Firebug.Module,
21309 {
21310     dispatchName: "jsonViewer",
21311     initialize: function()
21312     {
21313         Firebug.NetMonitor.NetInfoBody.addListener(this);
21314
21315         // Used by Firebug.DOMPanel.DirTable domplate.
21316         this.toggles = {};
21317     },
21318
21319     shutdown: function()
21320     {
21321         Firebug.NetMonitor.NetInfoBody.removeListener(this);
21322     },
21323
21324     initTabBody: function(infoBox, file)
21325     {
21326         if (FBTrace.DBG_JSONVIEWER)
21327             FBTrace.sysout("jsonviewer.initTabBody", infoBox);
21328
21329         // Let listeners to parse the JSON.
21330         dispatch(this.fbListeners, "onParseJSON", [file]);
21331
21332         // The JSON is still no there, try to parse most common cases.
21333         if (!file.jsonObject)
21334         {
21335             ///if (this.isJSON(safeGetContentType(file.request), file.responseText))
21336             if (this.isJSON(file.mimeType, file.responseText))
21337                 file.jsonObject = this.parseJSON(file);
21338         }
21339
21340         // The jsonObject is created so, the JSON tab can be displayed.
21341         if (file.jsonObject && hasProperties(file.jsonObject))
21342         {
21343             Firebug.NetMonitor.NetInfoBody.appendTab(infoBox, "JSON",
21344                 ///$STR("jsonviewer.tab.JSON"));
21345                 $STR("JSON"));
21346
21347             if (FBTrace.DBG_JSONVIEWER)
21348                 FBTrace.sysout("jsonviewer.initTabBody; JSON object available " +
21349                     (typeof(file.jsonObject) != "undefined"), file.jsonObject);
21350         }
21351     },
21352
21353     isJSON: function(contentType, data)
21354     {
21355         // Workaround for JSON responses without proper content type
21356         // Let's consider all responses starting with "{" as JSON. In the worst
21357         // case there will be an exception when parsing. This means that no-JSON
21358         // responses (and post data) (with "{") can be parsed unnecessarily,
21359         // which represents a little overhead, but this happens only if the request
21360         // is actually expanded by the user in the UI (Net & Console panels).
21361
21362         ///var responseText = data ? trimLeft(data) : null;
21363         ///if (responseText && responseText.indexOf("{") == 0)
21364         ///    return true;
21365         var responseText = data ? trim(data) : null;
21366         if (responseText && responseText.indexOf("{") == 0)
21367             return true;
21368
21369         if (!contentType)
21370             return false;
21371
21372         contentType = contentType.split(";")[0];
21373         contentType = trim(contentType);
21374         return contentTypes[contentType];
21375     },
21376
21377     // Update listener for TabView
21378     updateTabBody: function(infoBox, file, context)
21379     {
21380         var tab = infoBox.selectedTab;
21381         ///var tabBody = infoBox.getElementsByClassName("netInfoJSONText").item(0);
21382         var tabBody = $$(".netInfoJSONText", infoBox)[0];
21383         if (!hasClass(tab, "netInfoJSONTab") || tabBody.updated)
21384             return;
21385
21386         tabBody.updated = true;
21387
21388         if (file.jsonObject) {
21389             Firebug.DOMPanel.DirTable.tag.replace(
21390                  {object: file.jsonObject, toggles: this.toggles}, tabBody);
21391         }
21392     },
21393
21394     parseJSON: function(file)
21395     {
21396         var jsonString = new String(file.responseText);
21397         ///return parseJSONString(jsonString, "http://" + file.request.originalURI.host);
21398         return parseJSONString(jsonString);
21399     }
21400 });
21401
21402 // ************************************************************************************************
21403 // Registration
21404
21405 Firebug.registerModule(Firebug.JSONViewerModel);
21406
21407 // ************************************************************************************************
21408 }});
21409
21410
21411 /* See license.txt for terms of usage */
21412
21413 FBL.ns(function() { with (FBL) {
21414
21415 // ************************************************************************************************
21416 // Constants
21417
21418 // List of XML related content types.
21419 var xmlContentTypes =
21420 [
21421     "text/xml",
21422     "application/xml",
21423     "application/xhtml+xml",
21424     "application/rss+xml",
21425     "application/atom+xml",,
21426     "application/vnd.mozilla.maybe.feed",
21427     "application/rdf+xml",
21428     "application/vnd.mozilla.xul+xml"
21429 ];
21430
21431 // ************************************************************************************************
21432 // Model implementation
21433
21434 /**
21435  * @module Implements viewer for XML based network responses. In order to create a new
21436  * tab wihin network request detail, a listener is registered into
21437  * <code>Firebug.NetMonitor.NetInfoBody</code> object.
21438  */
21439 Firebug.XMLViewerModel = extend(Firebug.Module,
21440 {
21441     dispatchName: "xmlViewer",
21442
21443     initialize: function()
21444     {
21445         ///Firebug.ActivableModule.initialize.apply(this, arguments);
21446         Firebug.Module.initialize.apply(this, arguments);
21447         Firebug.NetMonitor.NetInfoBody.addListener(this);
21448     },
21449
21450     shutdown: function()
21451     {
21452         ///Firebug.ActivableModule.shutdown.apply(this, arguments);
21453         Firebug.Module.shutdown.apply(this, arguments);
21454         Firebug.NetMonitor.NetInfoBody.removeListener(this);
21455     },
21456
21457     /**
21458      * Check response's content-type and if it's a XML, create a new tab with XML preview.
21459      */
21460     initTabBody: function(infoBox, file)
21461     {
21462         if (FBTrace.DBG_XMLVIEWER)
21463             FBTrace.sysout("xmlviewer.initTabBody", infoBox);
21464
21465         // If the response is XML let's display a pretty preview.
21466         ///if (this.isXML(safeGetContentType(file.request)))
21467         if (this.isXML(file.mimeType, file.responseText))
21468         {
21469             Firebug.NetMonitor.NetInfoBody.appendTab(infoBox, "XML",
21470                 ///$STR("xmlviewer.tab.XML"));
21471                 $STR("XML"));
21472
21473             if (FBTrace.DBG_XMLVIEWER)
21474                 FBTrace.sysout("xmlviewer.initTabBody; XML response available");
21475         }
21476     },
21477
21478     isXML: function(contentType)
21479     {
21480         if (!contentType)
21481             return false;
21482
21483         // Look if the response is XML based.
21484         for (var i=0; i<xmlContentTypes.length; i++)
21485         {
21486             if (contentType.indexOf(xmlContentTypes[i]) == 0)
21487                 return true;
21488         }
21489
21490         return false;
21491     },
21492
21493     /**
21494      * Parse XML response and render pretty printed preview.
21495      */
21496     updateTabBody: function(infoBox, file, context)
21497     {
21498         var tab = infoBox.selectedTab;
21499         ///var tabBody = infoBox.getElementsByClassName("netInfoXMLText").item(0);
21500         var tabBody = $$(".netInfoXMLText", infoBox)[0];
21501         if (!hasClass(tab, "netInfoXMLTab") || tabBody.updated)
21502             return;
21503
21504         tabBody.updated = true;
21505
21506         this.insertXML(tabBody, Firebug.NetMonitor.Utils.getResponseText(file, context));
21507     },
21508
21509     insertXML: function(parentNode, text)
21510     {
21511         var xmlText = text.replace(/^\s*<?.+?>\s*/, "");
21512
21513         var div = parentNode.ownerDocument.createElement("div");
21514         div.innerHTML = xmlText;
21515
21516         var root = div.getElementsByTagName("*")[0];
21517
21518         /***
21519         var parser = CCIN("@mozilla.org/xmlextras/domparser;1", "nsIDOMParser");
21520         var doc = parser.parseFromString(text, "text/xml");
21521         var root = doc.documentElement;
21522
21523         // Error handling
21524         var nsURI = "http://www.mozilla.org/newlayout/xml/parsererror.xml";
21525         if (root.namespaceURI == nsURI && root.nodeName == "parsererror")
21526         {
21527             this.ParseError.tag.replace({error: {
21528                 message: root.firstChild.nodeValue,
21529                 source: root.lastChild.textContent
21530             }}, parentNode);
21531             return;
21532         }
21533         /**/
21534
21535         if (FBTrace.DBG_XMLVIEWER)
21536             FBTrace.sysout("xmlviewer.updateTabBody; XML response parsed", doc);
21537
21538         // Override getHidden in these templates. The parsed XML documen is
21539         // hidden, but we want to display it using 'visible' styling.
21540         /*
21541         var templates = [
21542             Firebug.HTMLPanel.CompleteElement,
21543             Firebug.HTMLPanel.Element,
21544             Firebug.HTMLPanel.TextElement,
21545             Firebug.HTMLPanel.EmptyElement,
21546             Firebug.HTMLPanel.XEmptyElement,
21547         ];
21548
21549         var originals = [];
21550         for (var i=0; i<templates.length; i++)
21551         {
21552             originals[i] = templates[i].getHidden;
21553             templates[i].getHidden = function() {
21554                 return "";
21555             }
21556         }
21557         /**/
21558
21559         // Generate XML preview.
21560         ///Firebug.HTMLPanel.CompleteElement.tag.replace({object: doc.documentElement}, parentNode);
21561
21562         // TODO: xxxpedro html3
21563         ///Firebug.HTMLPanel.CompleteElement.tag.replace({object: root}, parentNode);
21564         var html = [];
21565         Firebug.Reps.appendNode(root, html);
21566         parentNode.innerHTML = html.join("");
21567
21568
21569         /*
21570         for (var i=0; i<originals.length; i++)
21571             templates[i].getHidden = originals[i];/**/
21572     }
21573 });
21574
21575 // ************************************************************************************************
21576 // Domplate
21577
21578 /**
21579  * @domplate Represents a template for displaying XML parser errors. Used by
21580  * <code>Firebug.XMLViewerModel</code>.
21581  */
21582 Firebug.XMLViewerModel.ParseError = domplate(Firebug.Rep,
21583 {
21584     tag:
21585         DIV({"class": "xmlInfoError"},
21586             DIV({"class": "xmlInfoErrorMsg"}, "$error.message"),
21587             PRE({"class": "xmlInfoErrorSource"}, "$error|getSource")
21588         ),
21589
21590     getSource: function(error)
21591     {
21592         var parts = error.source.split("\n");
21593         if (parts.length != 2)
21594             return error.source;
21595
21596         var limit = 50;
21597         var column = parts[1].length;
21598         if (column >= limit) {
21599             parts[0] = "..." + parts[0].substr(column - limit);
21600             parts[1] = "..." + parts[1].substr(column - limit);
21601         }
21602
21603         if (parts[0].length > 80)
21604             parts[0] = parts[0].substr(0, 80) + "...";
21605
21606         return parts.join("\n");
21607     }
21608 });
21609
21610 // ************************************************************************************************
21611 // Registration
21612
21613 Firebug.registerModule(Firebug.XMLViewerModel);
21614
21615 }});
21616
21617
21618 /* See license.txt for terms of usage */
21619
21620 // next-generation Console Panel (will override consoje.js)
21621 FBL.ns(function() { with (FBL) {
21622 // ************************************************************************************************
21623
21624 // ************************************************************************************************
21625 // Constants
21626
21627 /*
21628 const Cc = Components.classes;
21629 const Ci = Components.interfaces;
21630 const nsIPrefBranch2 = Ci.nsIPrefBranch2;
21631 const PrefService = Cc["@mozilla.org/preferences-service;1"];
21632 const prefs = PrefService.getService(nsIPrefBranch2);
21633 /**/
21634 /*
21635
21636 // new offline message handler
21637 o = {x:1,y:2};
21638
21639 r = Firebug.getRep(o);
21640
21641 r.tag.tag.compile();
21642
21643 outputs = [];
21644 html = r.tag.renderHTML({object:o}, outputs);
21645
21646
21647 // finish rendering the template (the DOM part)
21648 target = $("build");
21649 target.innerHTML = html;
21650 root = target.firstChild;
21651
21652 domArgs = [root, r.tag.context, 0];
21653 domArgs.push.apply(domArgs, r.tag.domArgs);
21654 domArgs.push.apply(domArgs, outputs);
21655 r.tag.tag.renderDOM.apply(self ? self : r.tag.subject, domArgs);
21656
21657
21658  */
21659 var consoleQueue = [];
21660 var lastHighlightedObject;
21661 var FirebugContext = Env.browser;
21662
21663 // ************************************************************************************************
21664
21665 var maxQueueRequests = 500;
21666
21667 // ************************************************************************************************
21668
21669 Firebug.ConsoleBase =
21670 {
21671     log: function(object, context, className, rep, noThrottle, sourceLink)
21672     {
21673         //dispatch(this.fbListeners,"log",[context, object, className, sourceLink]);
21674         return this.logRow(appendObject, object, context, className, rep, sourceLink, noThrottle);
21675     },
21676
21677     logFormatted: function(objects, context, className, noThrottle, sourceLink)
21678     {
21679         //dispatch(this.fbListeners,"logFormatted",[context, objects, className, sourceLink]);
21680         return this.logRow(appendFormatted, objects, context, className, null, sourceLink, noThrottle);
21681     },
21682
21683     openGroup: function(objects, context, className, rep, noThrottle, sourceLink, noPush)
21684     {
21685         return this.logRow(appendOpenGroup, objects, context, className, rep, sourceLink, noThrottle);
21686     },
21687
21688     closeGroup: function(context, noThrottle)
21689     {
21690         return this.logRow(appendCloseGroup, null, context, null, null, null, noThrottle, true);
21691     },
21692
21693     logRow: function(appender, objects, context, className, rep, sourceLink, noThrottle, noRow)
21694     {
21695         // TODO: xxxpedro console console2
21696         noThrottle = true; // xxxpedro forced because there is no TabContext yet
21697
21698         if (!context)
21699             context = FirebugContext;
21700
21701         if (FBTrace.DBG_ERRORS && !context)
21702             FBTrace.sysout("Console.logRow has no context, skipping objects", objects);
21703
21704         if (!context)
21705             return;
21706
21707         if (noThrottle || !context)
21708         {
21709             var panel = this.getPanel(context);
21710             if (panel)
21711             {
21712                 var row = panel.append(appender, objects, className, rep, sourceLink, noRow);
21713                 var container = panel.panelNode;
21714
21715                 // TODO: xxxpedro what is this? console console2
21716                 /*
21717                 var template = Firebug.NetMonitor.NetLimit;
21718
21719                 while (container.childNodes.length > maxQueueRequests + 1)
21720                 {
21721                     clearDomplate(container.firstChild.nextSibling);
21722                     container.removeChild(container.firstChild.nextSibling);
21723                     panel.limit.limitInfo.totalCount++;
21724                     template.updateCounter(panel.limit);
21725                 }
21726                 dispatch([Firebug.A11yModel], "onLogRowCreated", [panel , row]);
21727                 /**/
21728                 return row;
21729             }
21730             else
21731             {
21732                 consoleQueue.push([appender, objects, context, className, rep, sourceLink, noThrottle, noRow]);
21733             }
21734         }
21735         else
21736         {
21737             if (!context.throttle)
21738             {
21739                 //FBTrace.sysout("console.logRow has not context.throttle! ");
21740                 return;
21741             }
21742             var args = [appender, objects, context, className, rep, sourceLink, true, noRow];
21743             context.throttle(this.logRow, this, args);
21744         }
21745     },
21746
21747     appendFormatted: function(args, row, context)
21748     {
21749         if (!context)
21750             context = FirebugContext;
21751
21752         var panel = this.getPanel(context);
21753         panel.appendFormatted(args, row);
21754     },
21755
21756     clear: function(context)
21757     {
21758         if (!context)
21759             //context = FirebugContext;
21760             context = Firebug.context;
21761
21762         /*
21763         if (context)
21764             Firebug.Errors.clear(context);
21765         /**/
21766
21767         var panel = this.getPanel(context, true);
21768         if (panel)
21769         {
21770             panel.clear();
21771         }
21772     },
21773
21774     // Override to direct output to your panel
21775     getPanel: function(context, noCreate)
21776     {
21777         //return context.getPanel("console", noCreate);
21778         // TODO: xxxpedro console console2
21779         return Firebug.chrome ? Firebug.chrome.getPanel("Console") : null;
21780     }
21781
21782 };
21783
21784 // ************************************************************************************************
21785
21786 //TODO: xxxpedro
21787 //var ActivableConsole = extend(Firebug.ActivableModule, Firebug.ConsoleBase);
21788 var ActivableConsole = extend(Firebug.ConsoleBase,
21789 {
21790     isAlwaysEnabled: function()
21791     {
21792         return true;
21793     }
21794 });
21795
21796 Firebug.Console = Firebug.Console = extend(ActivableConsole,
21797 //Firebug.Console = extend(ActivableConsole,
21798 {
21799     dispatchName: "console",
21800
21801     error: function()
21802     {
21803         Firebug.Console.logFormatted(arguments, Firebug.browser, "error");
21804     },
21805
21806     flush: function()
21807     {
21808         dispatch(this.fbListeners,"flush",[]);
21809
21810         for (var i=0, length=consoleQueue.length; i<length; i++)
21811         {
21812             var args = consoleQueue[i];
21813             this.logRow.apply(this, args);
21814         }
21815     },
21816
21817     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
21818     // extends Module
21819
21820     showPanel: function(browser, panel)
21821     {
21822     },
21823
21824     getFirebugConsoleElement: function(context, win)
21825     {
21826         var element = win.document.getElementById("_firebugConsole");
21827         if (!element)
21828         {
21829             if (FBTrace.DBG_CONSOLE)
21830                 FBTrace.sysout("getFirebugConsoleElement forcing element");
21831             var elementForcer = "(function(){var r=null; try { r = window._getFirebugConsoleElement();}catch(exc){r=exc;} return r;})();";  // we could just add the elements here
21832
21833             if (context.stopped)
21834                 Firebug.Console.injector.evaluateConsoleScript(context);  // todo evaluate consoleForcer on stack
21835             else
21836                 var r = Firebug.CommandLine.evaluateInWebPage(elementForcer, context, win);
21837
21838             if (FBTrace.DBG_CONSOLE)
21839                 FBTrace.sysout("getFirebugConsoleElement forcing element result "+r, r);
21840
21841             var element = win.document.getElementById("_firebugConsole");
21842             if (!element) // elementForce fails
21843             {
21844                 if (FBTrace.DBG_ERRORS) FBTrace.sysout("console.getFirebugConsoleElement: no _firebugConsole in win:", win);
21845                 Firebug.Console.logFormatted(["Firebug cannot find _firebugConsole element", r, win], context, "error", true);
21846             }
21847         }
21848
21849         return element;
21850     },
21851
21852     isReadyElsePreparing: function(context, win) // this is the only code that should call injector.attachIfNeeded
21853     {
21854         if (FBTrace.DBG_CONSOLE)
21855             FBTrace.sysout("console.isReadyElsePreparing, win is " +
21856                 (win?"an argument: ":"null, context.window: ") +
21857                 (win?win.location:context.window.location), (win?win:context.window));
21858
21859         if (win)
21860             return this.injector.attachIfNeeded(context, win);
21861         else
21862         {
21863             var attached = true;
21864             for (var i = 0; i < context.windows.length; i++)
21865                 attached = attached && this.injector.attachIfNeeded(context, context.windows[i]);
21866             // already in the list above attached = attached && this.injector.attachIfNeeded(context, context.window);
21867             if (context.windows.indexOf(context.window) == -1)
21868                 FBTrace.sysout("isReadyElsePreparing ***************** context.window not in context.windows");
21869             if (FBTrace.DBG_CONSOLE)
21870                 FBTrace.sysout("console.isReadyElsePreparing attached to "+context.windows.length+" and returns "+attached);
21871             return attached;
21872         }
21873     },
21874
21875     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
21876     // extends ActivableModule
21877
21878     initialize: function()
21879     {
21880         this.panelName = "console";
21881
21882         //TODO: xxxpedro
21883         //Firebug.ActivableModule.initialize.apply(this, arguments);
21884         //Firebug.Debugger.addListener(this);
21885     },
21886
21887     enable: function()
21888     {
21889         if (Firebug.Console.isAlwaysEnabled())
21890             this.watchForErrors();
21891     },
21892
21893     disable: function()
21894     {
21895         if (Firebug.Console.isAlwaysEnabled())
21896             this.unwatchForErrors();
21897     },
21898
21899     initContext: function(context, persistedState)
21900     {
21901         Firebug.ActivableModule.initContext.apply(this, arguments);
21902         context.consoleReloadWarning = true;  // mark as need to warn.
21903     },
21904
21905     loadedContext: function(context)
21906     {
21907         for (var url in context.sourceFileMap)
21908             return;  // if there are any sourceFiles, then do nothing
21909
21910         // else we saw no JS, so the reload warning it not needed.
21911         this.clearReloadWarning(context);
21912     },
21913
21914     clearReloadWarning: function(context) // remove the warning about reloading.
21915     {
21916          if (context.consoleReloadWarning)
21917          {
21918              var panel = context.getPanel(this.panelName);
21919              panel.clearReloadWarning();
21920              delete context.consoleReloadWarning;
21921          }
21922     },
21923
21924     togglePersist: function(context)
21925     {
21926         var panel = context.getPanel(this.panelName);
21927         panel.persistContent = panel.persistContent ? false : true;
21928         Firebug.chrome.setGlobalAttribute("cmd_togglePersistConsole", "checked", panel.persistContent);
21929     },
21930
21931     showContext: function(browser, context)
21932     {
21933         Firebug.chrome.setGlobalAttribute("cmd_clearConsole", "disabled", !context);
21934
21935         Firebug.ActivableModule.showContext.apply(this, arguments);
21936     },
21937
21938     destroyContext: function(context, persistedState)
21939     {
21940         Firebug.Console.injector.detachConsole(context, context.window);  // TODO iterate windows?
21941     },
21942
21943     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
21944
21945     onPanelEnable: function(panelName)
21946     {
21947         if (panelName != this.panelName)  // we don't care about other panels
21948             return;
21949
21950         if (FBTrace.DBG_CONSOLE)
21951             FBTrace.sysout("console.onPanelEnable**************");
21952
21953         this.watchForErrors();
21954         Firebug.Debugger.addDependentModule(this); // we inject the console during JS compiles so we need jsd
21955     },
21956
21957     onPanelDisable: function(panelName)
21958     {
21959         if (panelName != this.panelName)  // we don't care about other panels
21960             return;
21961
21962         if (FBTrace.DBG_CONSOLE)
21963             FBTrace.sysout("console.onPanelDisable**************");
21964
21965         Firebug.Debugger.removeDependentModule(this); // we inject the console during JS compiles so we need jsd
21966         this.unwatchForErrors();
21967
21968         // Make sure possible errors coming from the page and displayed in the Firefox
21969         // status bar are removed.
21970         this.clear();
21971     },
21972
21973     onSuspendFirebug: function()
21974     {
21975         if (FBTrace.DBG_CONSOLE)
21976             FBTrace.sysout("console.onSuspendFirebug\n");
21977         if (Firebug.Console.isAlwaysEnabled())
21978             this.unwatchForErrors();
21979     },
21980
21981     onResumeFirebug: function()
21982     {
21983         if (FBTrace.DBG_CONSOLE)
21984             FBTrace.sysout("console.onResumeFirebug\n");
21985         if (Firebug.Console.isAlwaysEnabled())
21986             this.watchForErrors();
21987     },
21988
21989     watchForErrors: function()
21990     {
21991         Firebug.Errors.checkEnabled();
21992         $('fbStatusIcon').setAttribute("console", "on");
21993     },
21994
21995     unwatchForErrors: function()
21996     {
21997         Firebug.Errors.checkEnabled();
21998         $('fbStatusIcon').removeAttribute("console");
21999     },
22000
22001     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22002     // Firebug.Debugger listener
22003
22004     onMonitorScript: function(context, frame)
22005     {
22006         Firebug.Console.log(frame, context);
22007     },
22008
22009     onFunctionCall: function(context, frame, depth, calling)
22010     {
22011         if (calling)
22012             Firebug.Console.openGroup([frame, "depth:"+depth], context);
22013         else
22014             Firebug.Console.closeGroup(context);
22015     },
22016
22017     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22018
22019     logRow: function(appender, objects, context, className, rep, sourceLink, noThrottle, noRow)
22020     {
22021         if (!context)
22022             context = FirebugContext;
22023
22024         if (FBTrace.DBG_WINDOWS && !context) FBTrace.sysout("Console.logRow: no context \n");
22025
22026         if (this.isAlwaysEnabled())
22027             return Firebug.ConsoleBase.logRow.apply(this, arguments);
22028     }
22029 });
22030
22031 Firebug.ConsoleListener =
22032 {
22033     log: function(context, object, className, sourceLink)
22034     {
22035     },
22036
22037     logFormatted: function(context, objects, className, sourceLink)
22038     {
22039     }
22040 };
22041
22042 // ************************************************************************************************
22043
22044 Firebug.ConsolePanel = function () {} // XXjjb attach Firebug so this panel can be extended.
22045
22046 //TODO: xxxpedro
22047 //Firebug.ConsolePanel.prototype = extend(Firebug.ActivablePanel,
22048 Firebug.ConsolePanel.prototype = extend(Firebug.Panel,
22049 {
22050     wasScrolledToBottom: false,
22051     messageCount: 0,
22052     lastLogTime: 0,
22053     groups: null,
22054     limit: null,
22055
22056     append: function(appender, objects, className, rep, sourceLink, noRow)
22057     {
22058         var container = this.getTopContainer();
22059
22060         if (noRow)
22061         {
22062             appender.apply(this, [objects]);
22063         }
22064         else
22065         {
22066             // xxxHonza: Don't update the this.wasScrolledToBottom flag now.
22067             // At the beginning (when the first log is created) the isScrolledToBottom
22068             // always returns true.
22069             //if (this.panelNode.offsetHeight)
22070             //    this.wasScrolledToBottom = isScrolledToBottom(this.panelNode);
22071
22072             var row = this.createRow("logRow", className);
22073             appender.apply(this, [objects, row, rep]);
22074
22075             if (sourceLink)
22076                 FirebugReps.SourceLink.tag.append({object: sourceLink}, row);
22077
22078             container.appendChild(row);
22079
22080             this.filterLogRow(row, this.wasScrolledToBottom);
22081
22082             if (this.wasScrolledToBottom)
22083                 scrollToBottom(this.panelNode);
22084
22085             return row;
22086         }
22087     },
22088
22089     clear: function()
22090     {
22091         if (this.panelNode)
22092         {
22093             if (FBTrace.DBG_CONSOLE)
22094                 FBTrace.sysout("ConsolePanel.clear");
22095             clearNode(this.panelNode);
22096             this.insertLogLimit(this.context);
22097         }
22098     },
22099
22100     insertLogLimit: function()
22101     {
22102         // Create limit row. This row is the first in the list of entries
22103         // and initially hidden. It's displayed as soon as the number of
22104         // entries reaches the limit.
22105         var row = this.createRow("limitRow");
22106
22107         var limitInfo = {
22108             totalCount: 0,
22109             limitPrefsTitle: $STRF("LimitPrefsTitle", [Firebug.prefDomain+".console.logLimit"])
22110         };
22111
22112         //TODO: xxxpedro console net limit!?
22113         return;
22114         var netLimitRep = Firebug.NetMonitor.NetLimit;
22115         var nodes = netLimitRep.createTable(row, limitInfo);
22116
22117         this.limit = nodes[1];
22118
22119         var container = this.panelNode;
22120         container.insertBefore(nodes[0], container.firstChild);
22121     },
22122
22123     insertReloadWarning: function()
22124     {
22125         // put the message in, we will clear if the window console is injected.
22126         this.warningRow = this.append(appendObject, $STR("message.Reload to activate window console"), "info");
22127     },
22128
22129     clearReloadWarning: function()
22130     {
22131         if (this.warningRow)
22132         {
22133             this.warningRow.parentNode.removeChild(this.warningRow);
22134             delete this.warningRow;
22135         }
22136     },
22137
22138     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22139
22140     appendObject: function(object, row, rep)
22141     {
22142         if (!rep)
22143             rep = Firebug.getRep(object);
22144         return rep.tag.append({object: object}, row);
22145     },
22146
22147     appendFormatted: function(objects, row, rep)
22148     {
22149         if (!objects || !objects.length)
22150             return;
22151
22152         function logText(text, row)
22153         {
22154             var node = row.ownerDocument.createTextNode(text);
22155             row.appendChild(node);
22156         }
22157
22158         var format = objects[0];
22159         var objIndex = 0;
22160
22161         if (typeof(format) != "string")
22162         {
22163             format = "";
22164             objIndex = -1;
22165         }
22166         else  // a string
22167         {
22168             if (objects.length === 1) // then we have only a string...
22169             {
22170                 if (format.length < 1) { // ...and it has no characters.
22171                     logText("(an empty string)", row);
22172                     return;
22173                 }
22174             }
22175         }
22176
22177         var parts = parseFormat(format);
22178         var trialIndex = objIndex;
22179         for (var i= 0; i < parts.length; i++)
22180         {
22181             var part = parts[i];
22182             if (part && typeof(part) == "object")
22183             {
22184                 if (++trialIndex > objects.length)  // then too few parameters for format, assume unformatted.
22185                 {
22186                     format = "";
22187                     objIndex = -1;
22188                     parts.length = 0;
22189                     break;
22190                 }
22191             }
22192
22193         }
22194         for (var i = 0; i < parts.length; ++i)
22195         {
22196             var part = parts[i];
22197             if (part && typeof(part) == "object")
22198             {
22199                 var object = objects[++objIndex];
22200                 if (typeof(object) != "undefined")
22201                     this.appendObject(object, row, part.rep);
22202                 else
22203                     this.appendObject(part.type, row, FirebugReps.Text);
22204             }
22205             else
22206                 FirebugReps.Text.tag.append({object: part}, row);
22207         }
22208
22209         for (var i = objIndex+1; i < objects.length; ++i)
22210         {
22211             logText(" ", row);
22212             var object = objects[i];
22213             if (typeof(object) == "string")
22214                 FirebugReps.Text.tag.append({object: object}, row);
22215             else
22216                 this.appendObject(object, row);
22217         }
22218     },
22219
22220     appendOpenGroup: function(objects, row, rep)
22221     {
22222         if (!this.groups)
22223             this.groups = [];
22224
22225         setClass(row, "logGroup");
22226         setClass(row, "opened");
22227
22228         var innerRow = this.createRow("logRow");
22229         setClass(innerRow, "logGroupLabel");
22230         if (rep)
22231             rep.tag.replace({"objects": objects}, innerRow);
22232         else
22233             this.appendFormatted(objects, innerRow, rep);
22234         row.appendChild(innerRow);
22235         //dispatch([Firebug.A11yModel], 'onLogRowCreated', [this, innerRow]);
22236         var groupBody = this.createRow("logGroupBody");
22237         row.appendChild(groupBody);
22238         groupBody.setAttribute('role', 'group');
22239         this.groups.push(groupBody);
22240
22241         addEvent(innerRow, "mousedown", function(event)
22242         {
22243             if (isLeftClick(event))
22244             {
22245                 //console.log(event.currentTarget == event.target);
22246
22247                 var target = event.target || event.srcElement;
22248
22249                 target = getAncestorByClass(target, "logGroupLabel");
22250
22251                 var groupRow = target.parentNode;
22252
22253                 if (hasClass(groupRow, "opened"))
22254                 {
22255                     removeClass(groupRow, "opened");
22256                     target.setAttribute('aria-expanded', 'false');
22257                 }
22258                 else
22259                 {
22260                     setClass(groupRow, "opened");
22261                     target.setAttribute('aria-expanded', 'true');
22262                 }
22263             }
22264         });
22265     },
22266
22267     appendCloseGroup: function(object, row, rep)
22268     {
22269         if (this.groups)
22270             this.groups.pop();
22271     },
22272
22273     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22274     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22275     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22276     // TODO: xxxpedro console2
22277     onMouseMove: function(event)
22278     {
22279         if (!Firebug.Inspector) return;
22280
22281         var target = event.srcElement || event.target;
22282
22283         var object = getAncestorByClass(target, "objectLink-element");
22284         object = object ? object.repObject : null;
22285
22286         if(object && instanceOf(object, "Element") && object.nodeType == 1)
22287         {
22288             if(object != lastHighlightedObject)
22289             {
22290                 Firebug.Inspector.drawBoxModel(object);
22291                 object = lastHighlightedObject;
22292             }
22293         }
22294         else
22295             Firebug.Inspector.hideBoxModel();
22296
22297     },
22298
22299     onMouseDown: function(event)
22300     {
22301         var target = event.srcElement || event.target;
22302
22303         var object = getAncestorByClass(target, "objectLink");
22304         var repObject = object ? object.repObject : null;
22305
22306         if (!repObject)
22307         {
22308             return;
22309         }
22310
22311         if (hasClass(object, "objectLink-object"))
22312         {
22313             Firebug.chrome.selectPanel("DOM");
22314             Firebug.chrome.getPanel("DOM").select(repObject, true);
22315         }
22316         else if (hasClass(object, "objectLink-element"))
22317         {
22318             Firebug.chrome.selectPanel("HTML");
22319             Firebug.chrome.getPanel("HTML").select(repObject, true);
22320         }
22321
22322         /*
22323         if(object && instanceOf(object, "Element") && object.nodeType == 1)
22324         {
22325             if(object != lastHighlightedObject)
22326             {
22327                 Firebug.Inspector.drawBoxModel(object);
22328                 object = lastHighlightedObject;
22329             }
22330         }
22331         else
22332             Firebug.Inspector.hideBoxModel();
22333         /**/
22334
22335     },
22336     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22337     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22338     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22339
22340     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22341     // extends Panel
22342
22343     name: "Console",
22344     title: "Console",
22345     //searchable: true,
22346     //breakable: true,
22347     //editable: false,
22348
22349     options:
22350     {
22351         hasCommandLine: true,
22352         hasToolButtons: true,
22353         isPreRendered: true
22354     },
22355
22356     create: function()
22357     {
22358         Firebug.Panel.create.apply(this, arguments);
22359
22360         this.context = Firebug.browser.window;
22361         this.document = Firebug.chrome.document;
22362         this.onMouseMove = bind(this.onMouseMove, this);
22363         this.onMouseDown = bind(this.onMouseDown, this);
22364
22365         this.clearButton = new Button({
22366             element: $("fbConsole_btClear"),
22367             owner: Firebug.Console,
22368             onClick: Firebug.Console.clear
22369         });
22370     },
22371
22372     initialize: function()
22373     {
22374         Firebug.Panel.initialize.apply(this, arguments);  // loads persisted content
22375         //Firebug.ActivablePanel.initialize.apply(this, arguments);  // loads persisted content
22376
22377         if (!this.persistedContent && Firebug.Console.isAlwaysEnabled())
22378         {
22379             this.insertLogLimit(this.context);
22380
22381             // Initialize log limit and listen for changes.
22382             this.updateMaxLimit();
22383
22384             if (this.context.consoleReloadWarning)  // we have not yet injected the console
22385                 this.insertReloadWarning();
22386         }
22387
22388         //Firebug.Console.injector.install(Firebug.browser.window);
22389
22390         addEvent(this.panelNode, "mouseover", this.onMouseMove);
22391         addEvent(this.panelNode, "mousedown", this.onMouseDown);
22392
22393         this.clearButton.initialize();
22394
22395         //consolex.trace();
22396         //TODO: xxxpedro remove this
22397         /*
22398         Firebug.Console.openGroup(["asd"], null, "group", null, false);
22399         Firebug.Console.log("asd");
22400         Firebug.Console.log("asd");
22401         Firebug.Console.log("asd");
22402         /**/
22403
22404         //TODO: xxxpedro preferences prefs
22405         //prefs.addObserver(Firebug.prefDomain, this, false);
22406     },
22407
22408     initializeNode : function()
22409     {
22410         //dispatch([Firebug.A11yModel], 'onInitializeNode', [this]);
22411         if (FBTrace.DBG_CONSOLE)
22412         {
22413             this.onScroller = bind(this.onScroll, this);
22414             addEvent(this.panelNode, "scroll", this.onScroller);
22415         }
22416
22417         this.onResizer = bind(this.onResize, this);
22418         this.resizeEventTarget = Firebug.chrome.$('fbContentBox');
22419         addEvent(this.resizeEventTarget, "resize", this.onResizer);
22420     },
22421
22422     destroyNode : function()
22423     {
22424         //dispatch([Firebug.A11yModel], 'onDestroyNode', [this]);
22425         if (this.onScroller)
22426             removeEvent(this.panelNode, "scroll", this.onScroller);
22427
22428         //removeEvent(this.resizeEventTarget, "resize", this.onResizer);
22429     },
22430
22431     shutdown: function()
22432     {
22433         //TODO: xxxpedro console console2
22434         this.clearButton.shutdown();
22435
22436         removeEvent(this.panelNode, "mousemove", this.onMouseMove);
22437         removeEvent(this.panelNode, "mousedown", this.onMouseDown);
22438
22439         this.destroyNode();
22440
22441         Firebug.Panel.shutdown.apply(this, arguments);
22442
22443         //TODO: xxxpedro preferences prefs
22444         //prefs.removeObserver(Firebug.prefDomain, this, false);
22445     },
22446
22447     ishow: function(state)
22448     {
22449         if (FBTrace.DBG_CONSOLE)
22450             FBTrace.sysout("Console.panel show; " + this.context.getName(), state);
22451
22452         var enabled = Firebug.Console.isAlwaysEnabled();
22453         if (enabled)
22454         {
22455              Firebug.Console.disabledPanelPage.hide(this);
22456              this.showCommandLine(true);
22457              this.showToolbarButtons("fbConsoleButtons", true);
22458              Firebug.chrome.setGlobalAttribute("cmd_togglePersistConsole", "checked", this.persistContent);
22459
22460              if (state && state.wasScrolledToBottom)
22461              {
22462                  this.wasScrolledToBottom = state.wasScrolledToBottom;
22463                  delete state.wasScrolledToBottom;
22464              }
22465
22466              if (this.wasScrolledToBottom)
22467                  scrollToBottom(this.panelNode);
22468
22469              if (FBTrace.DBG_CONSOLE)
22470                  FBTrace.sysout("console.show ------------------ wasScrolledToBottom: " +
22471                     this.wasScrolledToBottom + ", " + this.context.getName());
22472         }
22473         else
22474         {
22475             this.hide(state);
22476             Firebug.Console.disabledPanelPage.show(this);
22477         }
22478     },
22479
22480     ihide: function(state)
22481     {
22482         if (FBTrace.DBG_CONSOLE)
22483             FBTrace.sysout("Console.panel hide; " + this.context.getName(), state);
22484
22485         this.showToolbarButtons("fbConsoleButtons", false);
22486         this.showCommandLine(false);
22487
22488         if (FBTrace.DBG_CONSOLE)
22489             FBTrace.sysout("console.hide ------------------ wasScrolledToBottom: " +
22490                 this.wasScrolledToBottom + ", " + this.context.getName());
22491     },
22492
22493     destroy: function(state)
22494     {
22495         if (this.panelNode.offsetHeight)
22496             this.wasScrolledToBottom = isScrolledToBottom(this.panelNode);
22497
22498         if (state)
22499             state.wasScrolledToBottom = this.wasScrolledToBottom;
22500
22501         if (FBTrace.DBG_CONSOLE)
22502             FBTrace.sysout("console.destroy ------------------ wasScrolledToBottom: " +
22503                 this.wasScrolledToBottom + ", " + this.context.getName());
22504     },
22505
22506     shouldBreakOnNext: function()
22507     {
22508         // xxxHonza: shouldn't the breakOnErrors be context related?
22509         // xxxJJB, yes, but we can't support it because we can't yet tell
22510         // which window the error is on.
22511         return Firebug.getPref(Firebug.servicePrefDomain, "breakOnErrors");
22512     },
22513
22514     getBreakOnNextTooltip: function(enabled)
22515     {
22516         return (enabled ? $STR("console.Disable Break On All Errors") :
22517             $STR("console.Break On All Errors"));
22518     },
22519
22520     enablePanel: function(module)
22521     {
22522         if (FBTrace.DBG_CONSOLE)
22523             FBTrace.sysout("console.ConsolePanel.enablePanel; " + this.context.getName());
22524
22525         Firebug.ActivablePanel.enablePanel.apply(this, arguments);
22526
22527         this.showCommandLine(true);
22528
22529         if (this.wasScrolledToBottom)
22530             scrollToBottom(this.panelNode);
22531     },
22532
22533     disablePanel: function(module)
22534     {
22535         if (FBTrace.DBG_CONSOLE)
22536             FBTrace.sysout("console.ConsolePanel.disablePanel; " + this.context.getName());
22537
22538         Firebug.ActivablePanel.disablePanel.apply(this, arguments);
22539
22540         this.showCommandLine(false);
22541     },
22542
22543     getOptionsMenuItems: function()
22544     {
22545         return [
22546             optionMenu("ShowJavaScriptErrors", "showJSErrors"),
22547             optionMenu("ShowJavaScriptWarnings", "showJSWarnings"),
22548             optionMenu("ShowCSSErrors", "showCSSErrors"),
22549             optionMenu("ShowXMLErrors", "showXMLErrors"),
22550             optionMenu("ShowXMLHttpRequests", "showXMLHttpRequests"),
22551             optionMenu("ShowChromeErrors", "showChromeErrors"),
22552             optionMenu("ShowChromeMessages", "showChromeMessages"),
22553             optionMenu("ShowExternalErrors", "showExternalErrors"),
22554             optionMenu("ShowNetworkErrors", "showNetworkErrors"),
22555             this.getShowStackTraceMenuItem(),
22556             this.getStrictOptionMenuItem(),
22557             "-",
22558             optionMenu("LargeCommandLine", "largeCommandLine")
22559         ];
22560     },
22561
22562     getShowStackTraceMenuItem: function()
22563     {
22564         var menuItem = serviceOptionMenu("ShowStackTrace", "showStackTrace");
22565         if (FirebugContext && !Firebug.Debugger.isAlwaysEnabled())
22566             menuItem.disabled = true;
22567         return menuItem;
22568     },
22569
22570     getStrictOptionMenuItem: function()
22571     {
22572         var strictDomain = "javascript.options";
22573         var strictName = "strict";
22574         var strictValue = prefs.getBoolPref(strictDomain+"."+strictName);
22575         return {label: "JavascriptOptionsStrict", type: "checkbox", checked: strictValue,
22576             command: bindFixed(Firebug.setPref, Firebug, strictDomain, strictName, !strictValue) };
22577     },
22578
22579     getBreakOnMenuItems: function()
22580     {
22581         //xxxHonza: no BON options for now.
22582         /*return [
22583             optionMenu("console.option.Persist Break On Error", "persistBreakOnError")
22584         ];*/
22585        return [];
22586     },
22587
22588     search: function(text)
22589     {
22590         if (!text)
22591             return;
22592
22593         // Make previously visible nodes invisible again
22594         if (this.matchSet)
22595         {
22596             for (var i in this.matchSet)
22597                 removeClass(this.matchSet[i], "matched");
22598         }
22599
22600         this.matchSet = [];
22601
22602         function findRow(node) { return getAncestorByClass(node, "logRow"); }
22603         var search = new TextSearch(this.panelNode, findRow);
22604
22605         var logRow = search.find(text);
22606         if (!logRow)
22607         {
22608             dispatch([Firebug.A11yModel], 'onConsoleSearchMatchFound', [this, text, []]);
22609             return false;
22610         }
22611         for (; logRow; logRow = search.findNext())
22612         {
22613             setClass(logRow, "matched");
22614             this.matchSet.push(logRow);
22615         }
22616         dispatch([Firebug.A11yModel], 'onConsoleSearchMatchFound', [this, text, this.matchSet]);
22617         return true;
22618     },
22619
22620     breakOnNext: function(breaking)
22621     {
22622         Firebug.setPref(Firebug.servicePrefDomain, "breakOnErrors", breaking);
22623     },
22624
22625     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22626     // private
22627
22628     createRow: function(rowName, className)
22629     {
22630         var elt = this.document.createElement("div");
22631         elt.className = rowName + (className ? " " + rowName + "-" + className : "");
22632         return elt;
22633     },
22634
22635     getTopContainer: function()
22636     {
22637         if (this.groups && this.groups.length)
22638             return this.groups[this.groups.length-1];
22639         else
22640             return this.panelNode;
22641     },
22642
22643     filterLogRow: function(logRow, scrolledToBottom)
22644     {
22645         if (this.searchText)
22646         {
22647             setClass(logRow, "matching");
22648             setClass(logRow, "matched");
22649
22650             // Search after a delay because we must wait for a frame to be created for
22651             // the new logRow so that the finder will be able to locate it
22652             setTimeout(bindFixed(function()
22653             {
22654                 if (this.searchFilter(this.searchText, logRow))
22655                     this.matchSet.push(logRow);
22656                 else
22657                     removeClass(logRow, "matched");
22658
22659                 removeClass(logRow, "matching");
22660
22661                 if (scrolledToBottom)
22662                     scrollToBottom(this.panelNode);
22663             }, this), 100);
22664         }
22665     },
22666
22667     searchFilter: function(text, logRow)
22668     {
22669         var count = this.panelNode.childNodes.length;
22670         var searchRange = this.document.createRange();
22671         searchRange.setStart(this.panelNode, 0);
22672         searchRange.setEnd(this.panelNode, count);
22673
22674         var startPt = this.document.createRange();
22675         startPt.setStartBefore(logRow);
22676
22677         var endPt = this.document.createRange();
22678         endPt.setStartAfter(logRow);
22679
22680         return finder.Find(text, searchRange, startPt, endPt) != null;
22681     },
22682
22683     // nsIPrefObserver
22684     observe: function(subject, topic, data)
22685     {
22686         // We're observing preferences only.
22687         if (topic != "nsPref:changed")
22688           return;
22689
22690         // xxxHonza check this out.
22691         var prefDomain = "Firebug.extension.";
22692         var prefName = data.substr(prefDomain.length);
22693         if (prefName == "console.logLimit")
22694             this.updateMaxLimit();
22695     },
22696
22697     updateMaxLimit: function()
22698     {
22699         var value = 1000;
22700         //TODO: xxxpedro preferences log limit?
22701         //var value = Firebug.getPref(Firebug.prefDomain, "console.logLimit");
22702         maxQueueRequests =  value ? value : maxQueueRequests;
22703     },
22704
22705     showCommandLine: function(shouldShow)
22706     {
22707         //TODO: xxxpedro show command line important
22708         return;
22709
22710         if (shouldShow)
22711         {
22712             collapse(Firebug.chrome.$("fbCommandBox"), false);
22713             Firebug.CommandLine.setMultiLine(Firebug.largeCommandLine, Firebug.chrome);
22714         }
22715         else
22716         {
22717             // Make sure that entire content of the Console panel is hidden when
22718             // the panel is disabled.
22719             Firebug.CommandLine.setMultiLine(false, Firebug.chrome, Firebug.largeCommandLine);
22720             collapse(Firebug.chrome.$("fbCommandBox"), true);
22721         }
22722     },
22723
22724     onScroll: function(event)
22725     {
22726         // Update the scroll position flag if the position changes.
22727         this.wasScrolledToBottom = FBL.isScrolledToBottom(this.panelNode);
22728
22729         if (FBTrace.DBG_CONSOLE)
22730             FBTrace.sysout("console.onScroll ------------------ wasScrolledToBottom: " +
22731                 this.wasScrolledToBottom + ", wasScrolledToBottom: " +
22732                 this.context.getName(), event);
22733     },
22734
22735     onResize: function(event)
22736     {
22737         if (FBTrace.DBG_CONSOLE)
22738             FBTrace.sysout("console.onResize ------------------ wasScrolledToBottom: " +
22739                 this.wasScrolledToBottom + ", offsetHeight: " + this.panelNode.offsetHeight +
22740                 ", scrollTop: " + this.panelNode.scrollTop + ", scrollHeight: " +
22741                 this.panelNode.scrollHeight + ", " + this.context.getName(), event);
22742
22743         if (this.wasScrolledToBottom)
22744             scrollToBottom(this.panelNode);
22745     }
22746 });
22747
22748 // ************************************************************************************************
22749
22750 function parseFormat(format)
22751 {
22752     var parts = [];
22753     if (format.length <= 0)
22754         return parts;
22755
22756     var reg = /((^%|.%)(\d+)?(\.)([a-zA-Z]))|((^%|.%)([a-zA-Z]))/;
22757     for (var m = reg.exec(format); m; m = reg.exec(format))
22758     {
22759         if (m[0].substr(0, 2) == "%%")
22760         {
22761             parts.push(format.substr(0, m.index));
22762             parts.push(m[0].substr(1));
22763         }
22764         else
22765         {
22766             var type = m[8] ? m[8] : m[5];
22767             var precision = m[3] ? parseInt(m[3]) : (m[4] == "." ? -1 : 0);
22768
22769             var rep = null;
22770             switch (type)
22771             {
22772                 case "s":
22773                     rep = FirebugReps.Text;
22774                     break;
22775                 case "f":
22776                 case "i":
22777                 case "d":
22778                     rep = FirebugReps.Number;
22779                     break;
22780                 case "o":
22781                     rep = null;
22782                     break;
22783             }
22784
22785             parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index+1));
22786             parts.push({rep: rep, precision: precision, type: ("%" + type)});
22787         }
22788
22789         format = format.substr(m.index+m[0].length);
22790     }
22791
22792     parts.push(format);
22793     return parts;
22794 }
22795
22796 // ************************************************************************************************
22797
22798 var appendObject = Firebug.ConsolePanel.prototype.appendObject;
22799 var appendFormatted = Firebug.ConsolePanel.prototype.appendFormatted;
22800 var appendOpenGroup = Firebug.ConsolePanel.prototype.appendOpenGroup;
22801 var appendCloseGroup = Firebug.ConsolePanel.prototype.appendCloseGroup;
22802
22803 // ************************************************************************************************
22804
22805 //Firebug.registerActivableModule(Firebug.Console);
22806 Firebug.registerModule(Firebug.Console);
22807 Firebug.registerPanel(Firebug.ConsolePanel);
22808
22809 // ************************************************************************************************
22810 }});
22811
22812
22813 /* See license.txt for terms of usage */
22814
22815 FBL.ns(function() { with (FBL) {
22816
22817 // ************************************************************************************************
22818 // Constants
22819
22820 //const Cc = Components.classes;
22821 //const Ci = Components.interfaces;
22822
22823 var frameCounters = {};
22824 var traceRecursion = 0;
22825
22826 Firebug.Console.injector =
22827 {
22828     install: function(context)
22829     {
22830         var win = context.window;
22831
22832         var consoleHandler = new FirebugConsoleHandler(context, win);
22833
22834         var properties =
22835         [
22836             "log",
22837             "debug",
22838             "info",
22839             "warn",
22840             "error",
22841             "assert",
22842             "dir",
22843             "dirxml",
22844             "group",
22845             "groupCollapsed",
22846             "groupEnd",
22847             "time",
22848             "timeEnd",
22849             "count",
22850             "trace",
22851             "profile",
22852             "profileEnd",
22853             "clear",
22854             "open",
22855             "close"
22856         ];
22857
22858         var Handler = function(name)
22859         {
22860             var c = consoleHandler;
22861             var f = consoleHandler[name];
22862             return function(){return f.apply(c,arguments);};
22863         };
22864
22865         var installer = function(c)
22866         {
22867             for (var i=0, l=properties.length; i<l; i++)
22868             {
22869                 var name = properties[i];
22870                 c[name] = new Handler(name);
22871                 c.firebuglite = Firebug.version;
22872             }
22873         };
22874
22875         var sandbox;
22876
22877         if (win.console)
22878         {
22879             if (Env.Options.overrideConsole)
22880                 sandbox = new win.Function("arguments.callee.install(window.console={})");
22881             else
22882                 // if there's a console object and overrideConsole is false we should just quit
22883                 return;
22884         }
22885         else
22886         {
22887             try
22888             {
22889                 // try overriding the console object
22890                 sandbox = new win.Function("arguments.callee.install(window.console={})");
22891             }
22892             catch(E)
22893             {
22894                 // if something goes wrong create the firebug object instead
22895                 sandbox = new win.Function("arguments.callee.install(window.firebug={})");
22896             }
22897         }
22898
22899         sandbox.install = installer;
22900         sandbox();
22901     },
22902
22903     isAttached: function(context, win)
22904     {
22905         if (win.wrappedJSObject)
22906         {
22907             var attached = (win.wrappedJSObject._getFirebugConsoleElement ? true : false);
22908             if (FBTrace.DBG_CONSOLE)
22909                 FBTrace.sysout("Console.isAttached:"+attached+" to win.wrappedJSObject "+safeGetWindowLocation(win.wrappedJSObject));
22910
22911             return attached;
22912         }
22913         else
22914         {
22915             if (FBTrace.DBG_CONSOLE)
22916                 FBTrace.sysout("Console.isAttached? to win "+win.location+" fnc:"+win._getFirebugConsoleElement);
22917             return (win._getFirebugConsoleElement ? true : false);
22918         }
22919     },
22920
22921     attachIfNeeded: function(context, win)
22922     {
22923         if (FBTrace.DBG_CONSOLE)
22924             FBTrace.sysout("Console.attachIfNeeded has win "+(win? ((win.wrappedJSObject?"YES":"NO")+" wrappedJSObject"):"null") );
22925
22926         if (this.isAttached(context, win))
22927             return true;
22928
22929         if (FBTrace.DBG_CONSOLE)
22930             FBTrace.sysout("Console.attachIfNeeded found isAttached false ");
22931
22932         this.attachConsoleInjector(context, win);
22933         this.addConsoleListener(context, win);
22934
22935         Firebug.Console.clearReloadWarning(context);
22936
22937         var attached =  this.isAttached(context, win);
22938         if (attached)
22939             dispatch(Firebug.Console.fbListeners, "onConsoleInjected", [context, win]);
22940
22941         return attached;
22942     },
22943
22944     attachConsoleInjector: function(context, win)
22945     {
22946         var consoleInjection = this.getConsoleInjectionScript();  // Do it all here.
22947
22948         if (FBTrace.DBG_CONSOLE)
22949             FBTrace.sysout("attachConsoleInjector evaluating in "+win.location, consoleInjection);
22950
22951         Firebug.CommandLine.evaluateInWebPage(consoleInjection, context, win);
22952
22953         if (FBTrace.DBG_CONSOLE)
22954             FBTrace.sysout("attachConsoleInjector evaluation completed for "+win.location);
22955     },
22956
22957     getConsoleInjectionScript: function() {
22958         if (!this.consoleInjectionScript)
22959         {
22960             var script = "";
22961             script += "window.__defineGetter__('console', function() {\n";
22962             script += " return (window._firebug ? window._firebug : window.loadFirebugConsole()); })\n\n";
22963
22964             script += "window.loadFirebugConsole = function() {\n";
22965             script += "window._firebug =  new _FirebugConsole();";
22966
22967             if (FBTrace.DBG_CONSOLE)
22968                 script += " window.dump('loadFirebugConsole '+window.location+'\\n');\n";
22969
22970             script += " return window._firebug };\n";
22971
22972             var theFirebugConsoleScript = getResource("chrome://firebug/content/consoleInjected.js");
22973             script += theFirebugConsoleScript;
22974
22975
22976             this.consoleInjectionScript = script;
22977         }
22978         return this.consoleInjectionScript;
22979     },
22980
22981     forceConsoleCompilationInPage: function(context, win)
22982     {
22983         if (!win)
22984         {
22985             if (FBTrace.DBG_CONSOLE)
22986                 FBTrace.sysout("no win in forceConsoleCompilationInPage!");
22987             return;
22988         }
22989
22990         var consoleForcer = "window.loadFirebugConsole();";
22991
22992         if (context.stopped)
22993             Firebug.Console.injector.evaluateConsoleScript(context);  // todo evaluate consoleForcer on stack
22994         else
22995             Firebug.CommandLine.evaluateInWebPage(consoleForcer, context, win);
22996
22997         if (FBTrace.DBG_CONSOLE)
22998             FBTrace.sysout("forceConsoleCompilationInPage "+win.location, consoleForcer);
22999     },
23000
23001     evaluateConsoleScript: function(context)
23002     {
23003         var scriptSource = this.getConsoleInjectionScript(); // TODO XXXjjb this should be getConsoleInjectionScript
23004         Firebug.Debugger.evaluate(scriptSource, context);
23005     },
23006
23007     addConsoleListener: function(context, win)
23008     {
23009         if (!context.activeConsoleHandlers)  // then we have not been this way before
23010             context.activeConsoleHandlers = [];
23011         else
23012         {   // we've been this way before...
23013             for (var i=0; i<context.activeConsoleHandlers.length; i++)
23014             {
23015                 if (context.activeConsoleHandlers[i].window == win)
23016                 {
23017                     context.activeConsoleHandlers[i].detach();
23018                     if (FBTrace.DBG_CONSOLE)
23019                         FBTrace.sysout("consoleInjector addConsoleListener removed handler("+context.activeConsoleHandlers[i].handler_name+") from _firebugConsole in : "+win.location+"\n");
23020                     context.activeConsoleHandlers.splice(i,1);
23021                 }
23022             }
23023         }
23024
23025         // We need the element to attach our event listener.
23026         var element = Firebug.Console.getFirebugConsoleElement(context, win);
23027         if (element)
23028             element.setAttribute("FirebugVersion", Firebug.version); // Initialize Firebug version.
23029         else
23030             return false;
23031
23032         var handler = new FirebugConsoleHandler(context, win);
23033         handler.attachTo(element);
23034
23035         context.activeConsoleHandlers.push(handler);
23036
23037         if (FBTrace.DBG_CONSOLE)
23038             FBTrace.sysout("consoleInjector addConsoleListener attached handler("+handler.handler_name+") to _firebugConsole in : "+win.location+"\n");
23039         return true;
23040     },
23041
23042     detachConsole: function(context, win)
23043     {
23044         if (win && win.document)
23045         {
23046             var element = win.document.getElementById("_firebugConsole");
23047             if (element)
23048                 element.parentNode.removeChild(element);
23049         }
23050     }
23051 };
23052
23053 var total_handlers = 0;
23054 var FirebugConsoleHandler = function FirebugConsoleHandler(context, win)
23055 {
23056     this.window = win;
23057
23058     this.attachTo = function(element)
23059     {
23060         this.element = element;
23061         // When raised on our injected element, callback to Firebug and append to console
23062         this.boundHandler = bind(this.handleEvent, this);
23063         this.element.addEventListener('firebugAppendConsole', this.boundHandler, true); // capturing
23064     };
23065
23066     this.detach = function()
23067     {
23068         this.element.removeEventListener('firebugAppendConsole', this.boundHandler, true);
23069     };
23070
23071     this.handler_name = ++total_handlers;
23072     this.handleEvent = function(event)
23073     {
23074         if (FBTrace.DBG_CONSOLE)
23075             FBTrace.sysout("FirebugConsoleHandler("+this.handler_name+") "+event.target.getAttribute("methodName")+", event", event);
23076         if (!Firebug.CommandLine.CommandHandler.handle(event, this, win))
23077         {
23078             if (FBTrace.DBG_CONSOLE)
23079                 FBTrace.sysout("FirebugConsoleHandler", this);
23080
23081             var methodName = event.target.getAttribute("methodName");
23082             Firebug.Console.log($STRF("console.MethodNotSupported", [methodName]));
23083         }
23084     };
23085
23086     this.firebuglite = Firebug.version;
23087
23088     this.init = function()
23089     {
23090         var consoleElement = win.document.getElementById('_firebugConsole');
23091         consoleElement.setAttribute("FirebugVersion", Firebug.version);
23092     };
23093
23094     this.log = function()
23095     {
23096         logFormatted(arguments, "log");
23097     };
23098
23099     this.debug = function()
23100     {
23101         logFormatted(arguments, "debug", true);
23102     };
23103
23104     this.info = function()
23105     {
23106         logFormatted(arguments, "info", true);
23107     };
23108
23109     this.warn = function()
23110     {
23111         logFormatted(arguments, "warn", true);
23112     };
23113
23114     this.error = function()
23115     {
23116         //TODO: xxxpedro console error
23117         //if (arguments.length == 1)
23118         //{
23119         //    logAssert("error", arguments);  // add more info based on stack trace
23120         //}
23121         //else
23122         //{
23123             //Firebug.Errors.increaseCount(context);
23124             logFormatted(arguments, "error", true);  // user already added info
23125         //}
23126     };
23127
23128     this.exception = function()
23129     {
23130         logAssert("error", arguments);
23131     };
23132
23133     this.assert = function(x)
23134     {
23135         if (!x)
23136         {
23137             var rest = [];
23138             for (var i = 1; i < arguments.length; i++)
23139                 rest.push(arguments[i]);
23140             logAssert("assert", rest);
23141         }
23142     };
23143
23144     this.dir = function(o)
23145     {
23146         Firebug.Console.log(o, context, "dir", Firebug.DOMPanel.DirTable);
23147     };
23148
23149     this.dirxml = function(o)
23150     {
23151         ///if (o instanceof Window)
23152         if (instanceOf(o, "Window"))
23153             o = o.document.documentElement;
23154         ///else if (o instanceof Document)
23155         else if (instanceOf(o, "Document"))
23156             o = o.documentElement;
23157
23158         Firebug.Console.log(o, context, "dirxml", Firebug.HTMLPanel.SoloElement);
23159     };
23160
23161     this.group = function()
23162     {
23163         //TODO: xxxpedro;
23164         //var sourceLink = getStackLink();
23165         var sourceLink = null;
23166         Firebug.Console.openGroup(arguments, null, "group", null, false, sourceLink);
23167     };
23168
23169     this.groupEnd = function()
23170     {
23171         Firebug.Console.closeGroup(context);
23172     };
23173
23174     this.groupCollapsed = function()
23175     {
23176         var sourceLink = getStackLink();
23177         // noThrottle true is probably ok, openGroups will likely be short strings.
23178         var row = Firebug.Console.openGroup(arguments, null, "group", null, true, sourceLink);
23179         removeClass(row, "opened");
23180     };
23181
23182     this.profile = function(title)
23183     {
23184         logFormatted(["console.profile() not supported."], "warn", true);
23185
23186         //Firebug.Profiler.startProfiling(context, title);
23187     };
23188
23189     this.profileEnd = function()
23190     {
23191         logFormatted(["console.profile() not supported."], "warn", true);
23192
23193         //Firebug.Profiler.stopProfiling(context);
23194     };
23195
23196     this.count = function(key)
23197     {
23198         // TODO: xxxpedro console2: is there a better way to find a unique ID for the coun() call?
23199         var frameId = "0";
23200         //var frameId = FBL.getStackFrameId();
23201         if (frameId)
23202         {
23203             if (!frameCounters)
23204                 frameCounters = {};
23205
23206             if (key != undefined)
23207                 frameId += key;
23208
23209             var frameCounter = frameCounters[frameId];
23210             if (!frameCounter)
23211             {
23212                 var logRow = logFormatted(["0"], null, true, true);
23213
23214                 frameCounter = {logRow: logRow, count: 1};
23215                 frameCounters[frameId] = frameCounter;
23216             }
23217             else
23218                 ++frameCounter.count;
23219
23220             var label = key == undefined
23221                 ? frameCounter.count
23222                 : key + " " + frameCounter.count;
23223
23224             frameCounter.logRow.firstChild.firstChild.nodeValue = label;
23225         }
23226     };
23227
23228     this.trace = function()
23229     {
23230         var getFuncName = function getFuncName (f)
23231         {
23232             if (f.getName instanceof Function)
23233             {
23234                 return f.getName();
23235             }
23236             if (f.name) // in FireFox, Function objects have a name property...
23237             {
23238                 return f.name;
23239             }
23240
23241             var name = f.toString().match(/function\s*([_$\w\d]*)/)[1];
23242             return name || "anonymous";
23243         };
23244
23245         var wasVisited = function(fn)
23246         {
23247             for (var i=0, l=frames.length; i<l; i++)
23248             {
23249                 if (frames[i].fn == fn)
23250                 {
23251                     return true;
23252                 }
23253             }
23254
23255             return false;
23256         };
23257
23258         traceRecursion++;
23259
23260         if (traceRecursion > 1)
23261         {
23262             traceRecursion--;
23263             return;
23264         }
23265
23266         var frames = [];
23267
23268         for (var fn = arguments.callee.caller.caller; fn; fn = fn.caller)
23269         {
23270             if (wasVisited(fn)) break;
23271
23272             var args = [];
23273
23274             for (var i = 0, l = fn.arguments.length; i < l; ++i)
23275             {
23276                 args.push({value: fn.arguments[i]});
23277             }
23278
23279             frames.push({fn: fn, name: getFuncName(fn), args: args});
23280         }
23281
23282
23283         // ****************************************************************************************
23284
23285         try
23286         {
23287             (0)();
23288         }
23289         catch(e)
23290         {
23291             var result = e;
23292
23293             var stack =
23294                 result.stack || // Firefox / Google Chrome
23295                 result.stacktrace || // Opera
23296                 "";
23297
23298             stack = stack.replace(/\n\r|\r\n/g, "\n"); // normalize line breaks
23299             var items = stack.split(/[\n\r]/);
23300
23301             // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23302             // Google Chrome
23303             if (FBL.isSafari)
23304             {
23305                 //var reChromeStackItem = /^\s+at\s+([^\(]+)\s\((.*)\)$/;
23306                 //var reChromeStackItem = /^\s+at\s+(.*)((?:http|https|ftp|file):\/\/.*)$/;
23307                 var reChromeStackItem = /^\s+at\s+(.*)((?:http|https|ftp|file):\/\/.*)$/;
23308
23309                 var reChromeStackItemName = /\s*\($/;
23310                 var reChromeStackItemValue = /^(.+)\:(\d+\:\d+)\)?$/;
23311
23312                 var framePos = 0;
23313                 for (var i=4, length=items.length; i<length; i++, framePos++)
23314                 {
23315                     var frame = frames[framePos];
23316                     var item = items[i];
23317                     var match = item.match(reChromeStackItem);
23318
23319                     //Firebug.Console.log("["+ framePos +"]--------------------------");
23320                     //Firebug.Console.log(item);
23321                     //Firebug.Console.log("................");
23322
23323                     if (match)
23324                     {
23325                         var name = match[1];
23326                         if (name)
23327                         {
23328                             name = name.replace(reChromeStackItemName, "");
23329                             frame.name = name;
23330                         }
23331
23332                         //Firebug.Console.log("name: "+name);
23333
23334                         var value = match[2].match(reChromeStackItemValue);
23335                         if (value)
23336                         {
23337                             frame.href = value[1];
23338                             frame.lineNo = value[2];
23339
23340                             //Firebug.Console.log("url: "+value[1]);
23341                             //Firebug.Console.log("line: "+value[2]);
23342                         }
23343                         //else
23344                         //    Firebug.Console.log(match[2]);
23345
23346                     }
23347                 }
23348             }
23349             /**/
23350
23351             // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23352             else if (FBL.isFirefox)
23353             {
23354                 // Firefox
23355                 var reFirefoxStackItem = /^(.*)@(.*)$/;
23356                 var reFirefoxStackItemValue = /^(.+)\:(\d+)$/;
23357
23358                 var framePos = 0;
23359                 for (var i=2, length=items.length; i<length; i++, framePos++)
23360                 {
23361                     var frame = frames[framePos] || {};
23362                     var item = items[i];
23363                     var match = item.match(reFirefoxStackItem);
23364
23365                     if (match)
23366                     {
23367                         var name = match[1];
23368
23369                         //Firebug.Console.logFormatted("name: "+name);
23370
23371                         var value = match[2].match(reFirefoxStackItemValue);
23372                         if (value)
23373                         {
23374                             frame.href = value[1];
23375                             frame.lineNo = value[2];
23376
23377                             //Firebug.Console.log("href: "+ value[1]);
23378                             //Firebug.Console.log("line: " + value[2]);
23379                         }
23380                         //else
23381                         //    Firebug.Console.logFormatted([match[2]]);
23382                     }
23383                 }
23384             }
23385             /**/
23386
23387             // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23388             /*
23389             else if (FBL.isOpera)
23390             {
23391                 // Opera
23392                 var reOperaStackItem = /^\s\s(?:\.\.\.\s\s)?Line\s(\d+)\sof\s(.+)$/;
23393                 var reOperaStackItemValue = /^linked\sscript\s(.+)$/;
23394
23395                 for (var i=0, length=items.length; i<length; i+=2)
23396                 {
23397                     var item = items[i];
23398
23399                     var match = item.match(reOperaStackItem);
23400
23401                     if (match)
23402                     {
23403                         //Firebug.Console.log(match[1]);
23404
23405                         var value = match[2].match(reOperaStackItemValue);
23406
23407                         if (value)
23408                         {
23409                             //Firebug.Console.log(value[1]);
23410                         }
23411                         //else
23412                         //    Firebug.Console.log(match[2]);
23413
23414                         //Firebug.Console.log("--------------------------");
23415                     }
23416                 }
23417             }
23418             /**/
23419         }
23420
23421         //console.log(stack);
23422         //console.dir(frames);
23423         Firebug.Console.log({frames: frames}, context, "stackTrace", FirebugReps.StackTrace);
23424
23425         traceRecursion--;
23426     };
23427
23428     this.trace_ok = function()
23429     {
23430         var getFuncName = function getFuncName (f)
23431         {
23432             if (f.getName instanceof Function)
23433                 return f.getName();
23434             if (f.name) // in FireFox, Function objects have a name property...
23435                 return f.name;
23436
23437             var name = f.toString().match(/function\s*([_$\w\d]*)/)[1];
23438             return name || "anonymous";
23439         };
23440
23441         var wasVisited = function(fn)
23442         {
23443             for (var i=0, l=frames.length; i<l; i++)
23444             {
23445                 if (frames[i].fn == fn)
23446                     return true;
23447             }
23448
23449             return false;
23450         };
23451
23452         var frames = [];
23453
23454         for (var fn = arguments.callee.caller; fn; fn = fn.caller)
23455         {
23456             if (wasVisited(fn)) break;
23457
23458             var args = [];
23459
23460             for (var i = 0, l = fn.arguments.length; i < l; ++i)
23461             {
23462                 args.push({value: fn.arguments[i]});
23463             }
23464
23465             frames.push({fn: fn, name: getFuncName(fn), args: args});
23466         }
23467
23468         Firebug.Console.log({frames: frames}, context, "stackTrace", FirebugReps.StackTrace);
23469     };
23470
23471     this.clear = function()
23472     {
23473         Firebug.Console.clear(context);
23474     };
23475
23476     this.time = function(name, reset)
23477     {
23478         if (!name)
23479             return;
23480
23481         var time = new Date().getTime();
23482
23483         if (!this.timeCounters)
23484             this.timeCounters = {};
23485
23486         var key = "KEY"+name.toString();
23487
23488         if (!reset && this.timeCounters[key])
23489             return;
23490
23491         this.timeCounters[key] = time;
23492     };
23493
23494     this.timeEnd = function(name)
23495     {
23496         var time = new Date().getTime();
23497
23498         if (!this.timeCounters)
23499             return;
23500
23501         var key = "KEY"+name.toString();
23502
23503         var timeCounter = this.timeCounters[key];
23504         if (timeCounter)
23505         {
23506             var diff = time - timeCounter;
23507             var label = name + ": " + diff + "ms";
23508
23509             this.info(label);
23510
23511             delete this.timeCounters[key];
23512         }
23513         return diff;
23514     };
23515
23516     // These functions are over-ridden by commandLine
23517     this.evaluated = function(result, context)
23518     {
23519         if (FBTrace.DBG_CONSOLE)
23520             FBTrace.sysout("consoleInjector.FirebugConsoleHandler evalutated default called", result);
23521
23522         Firebug.Console.log(result, context);
23523     };
23524     this.evaluateError = function(result, context)
23525     {
23526         Firebug.Console.log(result, context, "errorMessage");
23527     };
23528
23529     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23530
23531     function logFormatted(args, className, linkToSource, noThrottle)
23532     {
23533         var sourceLink = linkToSource ? getStackLink() : null;
23534         return Firebug.Console.logFormatted(args, context, className, noThrottle, sourceLink);
23535     }
23536
23537     function logAssert(category, args)
23538     {
23539         Firebug.Errors.increaseCount(context);
23540
23541         if (!args || !args.length || args.length == 0)
23542             var msg = [FBL.$STR("Assertion")];
23543         else
23544             var msg = args[0];
23545
23546         if (Firebug.errorStackTrace)
23547         {
23548             var trace = Firebug.errorStackTrace;
23549             delete Firebug.errorStackTrace;
23550             if (FBTrace.DBG_CONSOLE)
23551                 FBTrace.sysout("logAssert trace from errorStackTrace", trace);
23552         }
23553         else if (msg.stack)
23554         {
23555             var trace = parseToStackTrace(msg.stack);
23556             if (FBTrace.DBG_CONSOLE)
23557                 FBTrace.sysout("logAssert trace from msg.stack", trace);
23558         }
23559         else
23560         {
23561             var trace = getJSDUserStack();
23562             if (FBTrace.DBG_CONSOLE)
23563                 FBTrace.sysout("logAssert trace from getJSDUserStack", trace);
23564         }
23565
23566         var errorObject = new FBL.ErrorMessage(msg, (msg.fileName?msg.fileName:win.location), (msg.lineNumber?msg.lineNumber:0), "", category, context, trace);
23567
23568
23569         if (trace && trace.frames && trace.frames[0])
23570            errorObject.correctWithStackTrace(trace);
23571
23572         errorObject.resetSource();
23573
23574         var objects = errorObject;
23575         if (args.length > 1)
23576         {
23577             objects = [errorObject];
23578             for (var i = 1; i < args.length; i++)
23579                 objects.push(args[i]);
23580         }
23581
23582         var row = Firebug.Console.log(objects, context, "errorMessage", null, true); // noThrottle
23583         row.scrollIntoView();
23584     }
23585
23586     function getComponentsStackDump()
23587     {
23588         // Starting with our stack, walk back to the user-level code
23589         var frame = Components.stack;
23590         var userURL = win.location.href.toString();
23591
23592         if (FBTrace.DBG_CONSOLE)
23593             FBTrace.sysout("consoleInjector.getComponentsStackDump initial stack for userURL "+userURL, frame);
23594
23595         // Drop frames until we get into user code.
23596         while (frame && FBL.isSystemURL(frame.filename) )
23597             frame = frame.caller;
23598
23599         // Drop two more frames, the injected console function and firebugAppendConsole()
23600         if (frame)
23601             frame = frame.caller;
23602         if (frame)
23603             frame = frame.caller;
23604
23605         if (FBTrace.DBG_CONSOLE)
23606             FBTrace.sysout("consoleInjector.getComponentsStackDump final stack for userURL "+userURL, frame);
23607
23608         return frame;
23609     }
23610
23611     function getStackLink()
23612     {
23613         // TODO: xxxpedro console2
23614         return;
23615         //return FBL.getFrameSourceLink(getComponentsStackDump());
23616     }
23617
23618     function getJSDUserStack()
23619     {
23620         var trace = FBL.getCurrentStackTrace(context);
23621
23622         var frames = trace ? trace.frames : null;
23623         if (frames && (frames.length > 0) )
23624         {
23625             var oldest = frames.length - 1;  // 6 - 1 = 5
23626             for (var i = 0; i < frames.length; i++)
23627             {
23628                 if (frames[oldest - i].href.indexOf("chrome:") == 0) break;
23629                 var fn = frames[oldest - i].fn + "";
23630                 if (fn && (fn.indexOf("_firebugEvalEvent") != -1) ) break;  // command line
23631             }
23632             FBTrace.sysout("consoleInjector getJSDUserStack: "+frames.length+" oldest: "+oldest+" i: "+i+" i - oldest + 2: "+(i - oldest + 2), trace);
23633             trace.frames = trace.frames.slice(2 - i);  // take the oldest frames, leave 2 behind they are injection code
23634
23635             return trace;
23636         }
23637         else
23638             return "Firebug failed to get stack trace with any frames";
23639     }
23640 };
23641
23642 // ************************************************************************************************
23643 // Register console namespace
23644
23645 FBL.registerConsole = function()
23646 {
23647     var win = Env.browser.window;
23648     Firebug.Console.injector.install(win);
23649 };
23650
23651 registerConsole();
23652
23653 }});
23654
23655
23656 /* See license.txt for terms of usage */
23657
23658 FBL.ns(function() { with (FBL) {
23659 // ************************************************************************************************
23660
23661
23662 // ************************************************************************************************
23663 // Globals
23664
23665 var commandPrefix = ">>>";
23666 var reOpenBracket = /[\[\(\{]/;
23667 var reCloseBracket = /[\]\)\}]/;
23668
23669 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23670
23671 var commandHistory = [];
23672 var commandPointer = -1;
23673
23674 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23675
23676 var isAutoCompleting = null;
23677 var autoCompletePrefix = null;
23678 var autoCompleteExpr = null;
23679 var autoCompleteBuffer = null;
23680 var autoCompletePosition = null;
23681
23682 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23683
23684 var fbCommandLine = null;
23685 var fbLargeCommandLine = null;
23686 var fbLargeCommandButtons = null;
23687
23688 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23689
23690 var _completion =
23691 {
23692     window:
23693     [
23694         "console"
23695     ],
23696
23697     document:
23698     [
23699         "getElementById",
23700         "getElementsByTagName"
23701     ]
23702 };
23703
23704 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23705
23706 var _stack = function(command)
23707 {
23708     Firebug.context.persistedState.commandHistory.push(command);
23709     Firebug.context.persistedState.commandPointer =
23710         Firebug.context.persistedState.commandHistory.length;
23711 };
23712
23713 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23714
23715 // ************************************************************************************************
23716 // CommandLine
23717
23718 Firebug.CommandLine = extend(Firebug.Module,
23719 {
23720     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23721
23722     element: null,
23723     isMultiLine: false,
23724     isActive: false,
23725
23726     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23727
23728     initialize: function(doc)
23729     {
23730         this.clear = bind(this.clear, this);
23731         this.enter = bind(this.enter, this);
23732
23733         this.onError = bind(this.onError, this);
23734         this.onKeyDown = bind(this.onKeyDown, this);
23735         this.onMultiLineKeyDown = bind(this.onMultiLineKeyDown, this);
23736
23737         addEvent(Firebug.browser.window, "error", this.onError);
23738         addEvent(Firebug.chrome.window, "error", this.onError);
23739     },
23740
23741     shutdown: function(doc)
23742     {
23743         this.deactivate();
23744
23745         removeEvent(Firebug.browser.window, "error", this.onError);
23746         removeEvent(Firebug.chrome.window, "error", this.onError);
23747     },
23748
23749     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23750
23751     activate: function(multiLine, hideToggleIcon, onRun)
23752     {
23753         defineCommandLineAPI();
23754
23755          Firebug.context.persistedState.commandHistory =
23756              Firebug.context.persistedState.commandHistory || [];
23757
23758          Firebug.context.persistedState.commandPointer =
23759              Firebug.context.persistedState.commandPointer || -1;
23760
23761         if (this.isActive)
23762         {
23763             if (this.isMultiLine == multiLine) return;
23764
23765             this.deactivate();
23766         }
23767
23768         fbCommandLine = $("fbCommandLine");
23769         fbLargeCommandLine = $("fbLargeCommandLine");
23770         fbLargeCommandButtons = $("fbLargeCommandButtons");
23771
23772         if (multiLine)
23773         {
23774             onRun = onRun || this.enter;
23775
23776             this.isMultiLine = true;
23777
23778             this.element = fbLargeCommandLine;
23779
23780             addEvent(this.element, "keydown", this.onMultiLineKeyDown);
23781
23782             addEvent($("fbSmallCommandLineIcon"), "click", Firebug.chrome.hideLargeCommandLine);
23783
23784             this.runButton = new Button({
23785                 element: $("fbCommand_btRun"),
23786                 owner: Firebug.CommandLine,
23787                 onClick: onRun
23788             });
23789
23790             this.runButton.initialize();
23791
23792             this.clearButton = new Button({
23793                 element: $("fbCommand_btClear"),
23794                 owner: Firebug.CommandLine,
23795                 onClick: this.clear
23796             });
23797
23798             this.clearButton.initialize();
23799         }
23800         else
23801         {
23802             this.isMultiLine = false;
23803             this.element = fbCommandLine;
23804
23805             if (!fbCommandLine)
23806                 return;
23807
23808             addEvent(this.element, "keydown", this.onKeyDown);
23809         }
23810
23811         //Firebug.Console.log("activate", this.element);
23812
23813         if (isOpera)
23814           fixOperaTabKey(this.element);
23815
23816         if(this.lastValue)
23817             this.element.value = this.lastValue;
23818
23819         this.isActive = true;
23820     },
23821
23822     deactivate: function()
23823     {
23824         if (!this.isActive) return;
23825
23826         //Firebug.Console.log("deactivate", this.element);
23827
23828         this.isActive = false;
23829
23830         this.lastValue = this.element.value;
23831
23832         if (this.isMultiLine)
23833         {
23834             removeEvent(this.element, "keydown", this.onMultiLineKeyDown);
23835
23836             removeEvent($("fbSmallCommandLineIcon"), "click", Firebug.chrome.hideLargeCommandLine);
23837
23838             this.runButton.destroy();
23839             this.clearButton.destroy();
23840         }
23841         else
23842         {
23843             removeEvent(this.element, "keydown", this.onKeyDown);
23844         }
23845
23846         this.element = null;
23847         delete this.element;
23848
23849         fbCommandLine = null;
23850         fbLargeCommandLine = null;
23851         fbLargeCommandButtons = null;
23852     },
23853
23854     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23855
23856     focus: function()
23857     {
23858         this.element.focus();
23859     },
23860
23861     blur: function()
23862     {
23863         this.element.blur();
23864     },
23865
23866     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23867
23868     clear: function()
23869     {
23870         this.element.value = "";
23871     },
23872
23873     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23874
23875     evaluate: function(expr)
23876     {
23877         // TODO: need to register the API in console.firebug.commandLineAPI
23878         var api = "Firebug.CommandLine.API";
23879
23880         var result = Firebug.context.evaluate(expr, "window", api, Firebug.Console.error);
23881
23882         return result;
23883     },
23884
23885     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23886
23887     enter: function()
23888     {
23889         var command = this.element.value;
23890
23891         if (!command) return;
23892
23893         _stack(command);
23894
23895         Firebug.Console.log(commandPrefix + " " + stripNewLines(command),
23896                 Firebug.browser, "command", FirebugReps.Text);
23897
23898         var result = this.evaluate(command);
23899
23900         Firebug.Console.log(result);
23901     },
23902
23903     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23904
23905     prevCommand: function()
23906     {
23907         if (Firebug.context.persistedState.commandPointer > 0 &&
23908             Firebug.context.persistedState.commandHistory.length > 0)
23909         {
23910             this.element.value = Firebug.context.persistedState.commandHistory
23911                                     [--Firebug.context.persistedState.commandPointer];
23912         }
23913     },
23914
23915     nextCommand: function()
23916     {
23917         var element = this.element;
23918
23919         var limit = Firebug.context.persistedState.commandHistory.length -1;
23920         var i = Firebug.context.persistedState.commandPointer;
23921
23922         if (i < limit)
23923           element.value = Firebug.context.persistedState.commandHistory
23924                               [++Firebug.context.persistedState.commandPointer];
23925
23926         else if (i == limit)
23927         {
23928             ++Firebug.context.persistedState.commandPointer;
23929             element.value = "";
23930         }
23931     },
23932
23933     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23934
23935     autocomplete: function(reverse)
23936     {
23937         var element = this.element;
23938
23939         var command = element.value;
23940         var offset = getExpressionOffset(command);
23941
23942         var valBegin = offset ? command.substr(0, offset) : "";
23943         var val = command.substr(offset);
23944
23945         var buffer, obj, objName, commandBegin, result, prefix;
23946
23947         // if it is the beginning of the completion
23948         if(!isAutoCompleting)
23949         {
23950
23951             // group1 - command begin
23952             // group2 - base object
23953             // group3 - property prefix
23954             var reObj = /(.*[^_$\w\d\.])?((?:[_$\w][_$\w\d]*\.)*)([_$\w][_$\w\d]*)?$/;
23955             var r = reObj.exec(val);
23956
23957             // parse command
23958             if (r[1] || r[2] || r[3])
23959             {
23960                 commandBegin = r[1] || "";
23961                 objName = r[2] || "";
23962                 prefix = r[3] || "";
23963             }
23964             else if (val == "")
23965             {
23966                 commandBegin = objName = prefix = "";
23967             } else
23968                 return;
23969
23970             isAutoCompleting = true;
23971
23972             // find base object
23973             if(objName == "")
23974                 obj = window;
23975
23976             else
23977             {
23978                 objName = objName.replace(/\.$/, "");
23979
23980                 var n = objName.split(".");
23981                 var target = window, o;
23982
23983                 for (var i=0, ni; ni = n[i]; i++)
23984                 {
23985                     if (o = target[ni])
23986                       target = o;
23987
23988                     else
23989                     {
23990                         target = null;
23991                         break;
23992                     }
23993                 }
23994                 obj = target;
23995             }
23996
23997             // map base object
23998             if(obj)
23999             {
24000                 autoCompletePrefix = prefix;
24001                 autoCompleteExpr = valBegin + commandBegin + (objName ? objName + "." : "");
24002                 autoCompletePosition = -1;
24003
24004                 buffer = autoCompleteBuffer = isIE ?
24005                     _completion[objName || "window"] || [] : [];
24006
24007                 for(var p in obj)
24008                     buffer.push(p);
24009             }
24010
24011         // if it is the continuation of the last completion
24012         } else
24013           buffer = autoCompleteBuffer;
24014
24015         if (buffer)
24016         {
24017             prefix = autoCompletePrefix;
24018
24019             var diff = reverse ? -1 : 1;
24020
24021             for(var i=autoCompletePosition+diff, l=buffer.length, bi; i>=0 && i<l; i+=diff)
24022             {
24023                 bi = buffer[i];
24024
24025                 if (bi.indexOf(prefix) == 0)
24026                 {
24027                     autoCompletePosition = i;
24028                     result = bi;
24029                     break;
24030                 }
24031             }
24032         }
24033
24034         if (result)
24035             element.value = autoCompleteExpr + result;
24036     },
24037
24038     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
24039
24040     setMultiLine: function(multiLine)
24041     {
24042         if (multiLine == this.isMultiLine) return;
24043
24044         this.activate(multiLine);
24045     },
24046
24047     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
24048
24049     onError: function(msg, href, lineNo)
24050     {
24051         href = href || "";
24052
24053         var lastSlash = href.lastIndexOf("/");
24054         var fileName = lastSlash == -1 ? href : href.substr(lastSlash+1);
24055         var html = [
24056             '<span class="errorMessage">', msg, '</span>',
24057             '<div class="objectBox-sourceLink">', fileName, ' (line ', lineNo, ')</div>'
24058           ];
24059
24060         // TODO: xxxpedro ajust to Console2
24061         //Firebug.Console.writeRow(html, "error");
24062     },
24063
24064     onKeyDown: function(e)
24065     {
24066         e = e || event;
24067
24068         var code = e.keyCode;
24069
24070         /*tab, shift, control, alt*/
24071         if (code != 9 && code != 16 && code != 17 && code != 18)
24072         {
24073             isAutoCompleting = false;
24074         }
24075
24076         if (code == 13 /* enter */)
24077         {
24078             this.enter();
24079             this.clear();
24080         }
24081         else if (code == 27 /* ESC */)
24082         {
24083             setTimeout(this.clear, 0);
24084         }
24085         else if (code == 38 /* up */)
24086         {
24087             this.prevCommand();
24088         }
24089         else if (code == 40 /* down */)
24090         {
24091             this.nextCommand();
24092         }
24093         else if (code == 9 /* tab */)
24094         {
24095             this.autocomplete(e.shiftKey);
24096         }
24097         else
24098             return;
24099
24100         cancelEvent(e, true);
24101         return false;
24102     },
24103
24104     onMultiLineKeyDown: function(e)
24105     {
24106         e = e || event;
24107
24108         var code = e.keyCode;
24109
24110         if (code == 13 /* enter */ && e.ctrlKey)
24111         {
24112             this.enter();
24113         }
24114     }
24115 });
24116
24117 Firebug.registerModule(Firebug.CommandLine);
24118
24119
24120 // ************************************************************************************************
24121 //
24122
24123 function getExpressionOffset(command)
24124 {
24125     // XXXjoe This is kind of a poor-man's JavaScript parser - trying
24126     // to find the start of the expression that the cursor is inside.
24127     // Not 100% fool proof, but hey...
24128
24129     var bracketCount = 0;
24130
24131     var start = command.length-1;
24132     for (; start >= 0; --start)
24133     {
24134         var c = command[start];
24135         if ((c == "," || c == ";" || c == " ") && !bracketCount)
24136             break;
24137         if (reOpenBracket.test(c))
24138         {
24139             if (bracketCount)
24140                 --bracketCount;
24141             else
24142                 break;
24143         }
24144         else if (reCloseBracket.test(c))
24145             ++bracketCount;
24146     }
24147
24148     return start + 1;
24149 }
24150
24151 // ************************************************************************************************
24152 // CommandLine API
24153
24154 var CommandLineAPI =
24155 {
24156     $: function(id)
24157     {
24158         return Firebug.browser.document.getElementById(id);
24159     },
24160
24161     $$: function(selector, context)
24162     {
24163         context = context || Firebug.browser.document;
24164         return Firebug.Selector ?
24165                 Firebug.Selector(selector, context) :
24166                 Firebug.Console.error("Firebug.Selector module not loaded.");
24167     },
24168
24169     $0: null,
24170
24171     $1: null,
24172
24173     dir: function(o)
24174     {
24175         Firebug.Console.log(o, Firebug.context, "dir", Firebug.DOMPanel.DirTable);
24176     },
24177
24178     dirxml: function(o)
24179     {
24180         ///if (o instanceof Window)
24181         if (instanceOf(o, "Window"))
24182             o = o.document.documentElement;
24183         ///else if (o instanceof Document)
24184         else if (instanceOf(o, "Document"))
24185             o = o.documentElement;
24186
24187         Firebug.Console.log(o, Firebug.context, "dirxml", Firebug.HTMLPanel.SoloElement);
24188     }
24189 };
24190
24191 // ************************************************************************************************
24192
24193 var defineCommandLineAPI = function defineCommandLineAPI()
24194 {
24195     Firebug.CommandLine.API = {};
24196     for (var m in CommandLineAPI)
24197         if (!Env.browser.window[m])
24198             Firebug.CommandLine.API[m] = CommandLineAPI[m];
24199
24200     var stack = FirebugChrome.htmlSelectionStack;
24201     if (stack)
24202     {
24203         Firebug.CommandLine.API.$0 = stack[0];
24204         Firebug.CommandLine.API.$1 = stack[1];
24205     }
24206 };
24207
24208 // ************************************************************************************************
24209 }});
24210
24211 /* See license.txt for terms of usage */
24212
24213 FBL.ns(function() { with (FBL) {
24214 // ************************************************************************************************
24215
24216 // ************************************************************************************************
24217 // Globals
24218
24219 var ElementCache = Firebug.Lite.Cache.Element;
24220 var cacheID = Firebug.Lite.Cache.ID;
24221
24222 var ignoreHTMLProps =
24223 {
24224     // ignores the attributes injected by Sizzle, otherwise it will
24225     // be visible on IE (when enumerating element.attributes)
24226     sizcache: 1,
24227     sizset: 1
24228 };
24229
24230 if (Firebug.ignoreFirebugElements)
24231     // ignores also the cache property injected by firebug
24232     ignoreHTMLProps[cacheID] = 1;
24233
24234
24235 // ************************************************************************************************
24236 // HTML Module
24237
24238 Firebug.HTML = extend(Firebug.Module,
24239 {
24240     appendTreeNode: function(nodeArray, html)
24241     {
24242         var reTrim = /^\s+|\s+$/g;
24243
24244         if (!nodeArray.length) nodeArray = [nodeArray];
24245
24246         for (var n=0, node; node=nodeArray[n]; n++)
24247         {
24248             if (node.nodeType == 1)
24249             {
24250                 if (Firebug.ignoreFirebugElements && node.firebugIgnore) continue;
24251
24252                 var uid = ElementCache(node);
24253                 var child = node.childNodes;
24254                 var childLength = child.length;
24255
24256                 var nodeName = node.nodeName.toLowerCase();
24257
24258                 var nodeVisible = isVisible(node);
24259
24260                 var hasSingleTextChild = childLength == 1 && node.firstChild.nodeType == 3 &&
24261                         nodeName != "script" && nodeName != "style";
24262
24263                 var nodeControl = !hasSingleTextChild && childLength > 0 ?
24264                     ('<div class="nodeControl"></div>') : '';
24265
24266                 // FIXME xxxpedro remove this
24267                 //var isIE = false;
24268
24269                 if(isIE && nodeControl)
24270                     html.push(nodeControl);
24271
24272                 if (typeof uid != 'undefined')
24273                     html.push(
24274                         '<div class="objectBox-element" ',
24275                         'id="', uid,
24276                         '">',
24277                         !isIE && nodeControl ? nodeControl: "",
24278                         '<span ',
24279                         cacheID,
24280                         '="', uid,
24281                         '"  class="nodeBox',
24282                         nodeVisible ? "" : " nodeHidden",
24283                         '">&lt;<span class="nodeTag">', nodeName, '</span>'
24284                     );
24285                 else
24286                     html.push(
24287                         '<div class="objectBox-element"><span class="nodeBox',
24288                         nodeVisible ? "" : " nodeHidden",
24289                         '">&lt;<span class="nodeTag">',
24290                         nodeName, '</span>'
24291                     );
24292
24293                 for (var i = 0; i < node.attributes.length; ++i)
24294                 {
24295                     var attr = node.attributes[i];
24296                     if (!attr.specified ||
24297                         // Issue 4432:  Firebug Lite: HTML is mixed-up with functions
24298                         // The problem here is that expando properties added to DOM elements in
24299                         // IE < 9 will behave like DOM attributes and so they'll show up when
24300                         // looking at element.attributes list.
24301                         isIE && (browserVersion-0<9) && typeof attr.nodeValue != "string" ||
24302                         Firebug.ignoreFirebugElements && ignoreHTMLProps.hasOwnProperty(attr.nodeName))
24303                             continue;
24304
24305                     var name = attr.nodeName.toLowerCase();
24306                     var value = name == "style" ? formatStyles(node.style.cssText) : attr.nodeValue;
24307
24308                     html.push('&nbsp;<span class="nodeName">', name,
24309                         '</span>=&quot;<span class="nodeValue">', escapeHTML(value),
24310                         '</span>&quot;');
24311                 }
24312
24313                 /*
24314                 // source code nodes
24315                 if (nodeName == 'script' || nodeName == 'style')
24316                 {
24317
24318                     if(document.all){
24319                         var src = node.innerHTML+'\n';
24320
24321                     }else {
24322                         var src = '\n'+node.innerHTML+'\n';
24323                     }
24324
24325                     var match = src.match(/\n/g);
24326                     var num = match ? match.length : 0;
24327                     var s = [], sl = 0;
24328
24329                     for(var c=1; c<num; c++){
24330                         s[sl++] = '<div line="'+c+'">' + c + '</div>';
24331                     }
24332
24333                     html.push('&gt;</div><div class="nodeGroup"><div class="nodeChildren"><div class="lineNo">',
24334                             s.join(''),
24335                             '</div><pre class="nodeCode">',
24336                             escapeHTML(src),
24337                             '</pre>',
24338                             '</div><div class="objectBox-element">&lt;/<span class="nodeTag">',
24339                             nodeName,
24340                             '</span>&gt;</div>',
24341                             '</div>'
24342                         );
24343
24344
24345                 }/**/
24346
24347                 // Just a single text node child
24348                 if (hasSingleTextChild)
24349                 {
24350                     var value = child[0].nodeValue.replace(reTrim, '');
24351                     if(value)
24352                     {
24353                         html.push(
24354                                 '&gt;<span class="nodeText">',
24355                                 escapeHTML(value),
24356                                 '</span>&lt;/<span class="nodeTag">',
24357                                 nodeName,
24358                                 '</span>&gt;</span></div>'
24359                             );
24360                     }
24361                     else
24362                       html.push('/&gt;</span></div>'); // blank text, print as childless node
24363
24364                 }
24365                 else if (childLength > 0)
24366                 {
24367                     html.push('&gt;</span></div>');
24368                 }
24369                 else
24370                     html.push('/&gt;</span></div>');
24371
24372             }
24373             else if (node.nodeType == 3)
24374             {
24375                 if ( node.parentNode && ( node.parentNode.nodeName.toLowerCase() == "script" ||
24376                      node.parentNode.nodeName.toLowerCase() == "style" ) )
24377                 {
24378                     var value = node.nodeValue.replace(reTrim, '');
24379
24380                     if(isIE){
24381                         var src = value+'\n';
24382
24383                     }else {
24384                         var src = '\n'+value+'\n';
24385                     }
24386
24387                     var match = src.match(/\n/g);
24388                     var num = match ? match.length : 0;
24389                     var s = [], sl = 0;
24390
24391                     for(var c=1; c<num; c++){
24392                         s[sl++] = '<div line="'+c+'">' + c + '</div>';
24393                     }
24394
24395                     html.push('<div class="lineNo">',
24396                             s.join(''),
24397                             '</div><pre class="sourceCode">',
24398                             escapeHTML(src),
24399                             '</pre>'
24400                         );
24401
24402                 }
24403                 else
24404                 {
24405                     var value = node.nodeValue.replace(reTrim, '');
24406                     if (value)
24407                         html.push('<div class="nodeText">', escapeHTML(value),'</div>');
24408                 }
24409             }
24410         }
24411     },
24412
24413     appendTreeChildren: function(treeNode)
24414     {
24415         var doc = Firebug.chrome.document;
24416         var uid = treeNode.id;
24417         var parentNode = ElementCache.get(uid);
24418
24419         if (parentNode.childNodes.length == 0) return;
24420
24421         var treeNext = treeNode.nextSibling;
24422         var treeParent = treeNode.parentNode;
24423
24424         // FIXME xxxpedro remove this
24425         //var isIE = false;
24426         var control = isIE ? treeNode.previousSibling : treeNode.firstChild;
24427         control.className = 'nodeControl nodeMaximized';
24428
24429         var html = [];
24430         var children = doc.createElement("div");
24431         children.className = "nodeChildren";
24432         this.appendTreeNode(parentNode.childNodes, html);
24433         children.innerHTML = html.join("");
24434
24435         treeParent.insertBefore(children, treeNext);
24436
24437         var closeElement = doc.createElement("div");
24438         closeElement.className = "objectBox-element";
24439         closeElement.innerHTML = '&lt;/<span class="nodeTag">' +
24440             parentNode.nodeName.toLowerCase() + '&gt;</span>';
24441
24442         treeParent.insertBefore(closeElement, treeNext);
24443
24444     },
24445
24446     removeTreeChildren: function(treeNode)
24447     {
24448         var children = treeNode.nextSibling;
24449         var closeTag = children.nextSibling;
24450
24451         // FIXME xxxpedro remove this
24452         //var isIE = false;
24453         var control = isIE ? treeNode.previousSibling : treeNode.firstChild;
24454         control.className = 'nodeControl';
24455
24456         children.parentNode.removeChild(children);
24457         closeTag.parentNode.removeChild(closeTag);
24458     },
24459
24460     isTreeNodeVisible: function(id)
24461     {
24462         return $(id);
24463     },
24464
24465     select: function(el)
24466     {
24467         var id = el && ElementCache(el);
24468         if (id)
24469             this.selectTreeNode(id);
24470     },
24471
24472     selectTreeNode: function(id)
24473     {
24474         id = ""+id;
24475         var node, stack = [];
24476         while(id && !this.isTreeNodeVisible(id))
24477         {
24478             stack.push(id);
24479
24480             var node = ElementCache.get(id).parentNode;
24481
24482             if (node)
24483                 id = ElementCache(node);
24484             else
24485                 break;
24486         }
24487
24488         stack.push(id);
24489
24490         while(stack.length > 0)
24491         {
24492             id = stack.pop();
24493             node = $(id);
24494
24495             if (stack.length > 0 && ElementCache.get(id).childNodes.length > 0)
24496               this.appendTreeChildren(node);
24497         }
24498
24499         selectElement(node);
24500
24501         // TODO: xxxpedro
24502         if (fbPanel1)
24503             fbPanel1.scrollTop = Math.round(node.offsetTop - fbPanel1.clientHeight/2);
24504     }
24505
24506 });
24507
24508 Firebug.registerModule(Firebug.HTML);
24509
24510 // ************************************************************************************************
24511 // HTML Panel
24512
24513 function HTMLPanel(){};
24514
24515 HTMLPanel.prototype = extend(Firebug.Panel,
24516 {
24517     name: "HTML",
24518     title: "HTML",
24519
24520     options: {
24521         hasSidePanel: true,
24522         //hasToolButtons: true,
24523         isPreRendered: !Firebug.flexChromeEnabled /* FIXME xxxpedro chromenew */,
24524         innerHTMLSync: true
24525     },
24526
24527     create: function(){
24528         Firebug.Panel.create.apply(this, arguments);
24529
24530         this.panelNode.style.padding = "4px 3px 1px 15px";
24531         this.panelNode.style.minWidth = "500px";
24532
24533         if (Env.Options.enablePersistent || Firebug.chrome.type != "popup")
24534             this.createUI();
24535
24536         if(this.sidePanelBar && !this.sidePanelBar.selectedPanel)
24537         {
24538             this.sidePanelBar.selectPanel("css");
24539         }
24540     },
24541
24542     destroy: function()
24543     {
24544         selectedElement = null;
24545         fbPanel1 = null;
24546
24547         selectedSidePanelTS = null;
24548         selectedSidePanelTimer = null;
24549
24550         Firebug.Panel.destroy.apply(this, arguments);
24551     },
24552
24553     createUI: function()
24554     {
24555         var rootNode = Firebug.browser.document.documentElement;
24556         var html = [];
24557         Firebug.HTML.appendTreeNode(rootNode, html);
24558
24559         this.panelNode.innerHTML = html.join("");
24560     },
24561
24562     initialize: function()
24563     {
24564         Firebug.Panel.initialize.apply(this, arguments);
24565         addEvent(this.panelNode, 'click', Firebug.HTML.onTreeClick);
24566
24567         fbPanel1 = $("fbPanel1");
24568
24569         if(!selectedElement)
24570         {
24571             Firebug.context.persistedState.selectedHTMLElementId =
24572                 Firebug.context.persistedState.selectedHTMLElementId &&
24573                 ElementCache.get(Firebug.context.persistedState.selectedHTMLElementId) ?
24574                 Firebug.context.persistedState.selectedHTMLElementId :
24575                 ElementCache(Firebug.browser.document.body);
24576
24577             Firebug.HTML.selectTreeNode(Firebug.context.persistedState.selectedHTMLElementId);
24578         }
24579
24580         // TODO: xxxpedro
24581         addEvent(fbPanel1, 'mousemove', Firebug.HTML.onListMouseMove);
24582         addEvent($("fbContent"), 'mouseout', Firebug.HTML.onListMouseMove);
24583         addEvent(Firebug.chrome.node, 'mouseout', Firebug.HTML.onListMouseMove);
24584     },
24585
24586     shutdown: function()
24587     {
24588         // TODO: xxxpedro
24589         removeEvent(fbPanel1, 'mousemove', Firebug.HTML.onListMouseMove);
24590         removeEvent($("fbContent"), 'mouseout', Firebug.HTML.onListMouseMove);
24591         removeEvent(Firebug.chrome.node, 'mouseout', Firebug.HTML.onListMouseMove);
24592
24593         removeEvent(this.panelNode, 'click', Firebug.HTML.onTreeClick);
24594
24595         fbPanel1 = null;
24596
24597         Firebug.Panel.shutdown.apply(this, arguments);
24598     },
24599
24600     reattach: function()
24601     {
24602         // TODO: panel reattach
24603         if(Firebug.context.persistedState.selectedHTMLElementId)
24604             Firebug.HTML.selectTreeNode(Firebug.context.persistedState.selectedHTMLElementId);
24605     },
24606
24607     updateSelection: function(object)
24608     {
24609         var id = ElementCache(object);
24610
24611         if (id)
24612         {
24613             Firebug.HTML.selectTreeNode(id);
24614         }
24615     }
24616 });
24617
24618 Firebug.registerPanel(HTMLPanel);
24619
24620 // ************************************************************************************************
24621
24622 var formatStyles = function(styles)
24623 {
24624     return isIE ?
24625         // IE return CSS property names in upper case, so we need to convert them
24626         styles.replace(/([^\s]+)\s*:/g, function(m,g){return g.toLowerCase()+":";}) :
24627         // other browsers are just fine
24628         styles;
24629 };
24630
24631 // ************************************************************************************************
24632
24633 var selectedElement = null;
24634 var fbPanel1 = null;
24635
24636 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
24637 var selectedSidePanelTS, selectedSidePanelTimer;
24638
24639 var selectElement= function selectElement(e)
24640 {
24641     if (e != selectedElement)
24642     {
24643         if (selectedElement)
24644             selectedElement.className = "objectBox-element";
24645
24646         e.className = e.className + " selectedElement";
24647
24648         if (FBL.isFirefox)
24649             e.style.MozBorderRadius = "2px";
24650
24651         else if (FBL.isSafari)
24652             e.style.WebkitBorderRadius = "2px";
24653
24654         e.style.borderRadius = "2px";
24655
24656         selectedElement = e;
24657
24658         Firebug.context.persistedState.selectedHTMLElementId = e.id;
24659
24660         var target = ElementCache.get(e.id);
24661         var sidePanelBar = Firebug.chrome.getPanel("HTML").sidePanelBar;
24662         var selectedSidePanel = sidePanelBar ? sidePanelBar.selectedPanel : null;
24663
24664         var stack = FirebugChrome.htmlSelectionStack;
24665
24666         stack.unshift(target);
24667
24668         if (stack.length > 2)
24669             stack.pop();
24670
24671         var lazySelect = function()
24672         {
24673             selectedSidePanelTS = new Date().getTime();
24674
24675             if (selectedSidePanel)
24676                 selectedSidePanel.select(target, true);
24677         };
24678
24679         if (selectedSidePanelTimer)
24680         {
24681             clearTimeout(selectedSidePanelTimer);
24682             selectedSidePanelTimer = null;
24683         }
24684
24685         if (new Date().getTime() - selectedSidePanelTS > 100)
24686             setTimeout(lazySelect, 0);
24687         else
24688             selectedSidePanelTimer = setTimeout(lazySelect, 150);
24689     }
24690 };
24691
24692
24693 // ************************************************************************************************
24694 // ***  TODO:  REFACTOR  **************************************************************************
24695 // ************************************************************************************************
24696 Firebug.HTML.onTreeClick = function (e)
24697 {
24698     e = e || event;
24699     var targ;
24700
24701     if (e.target) targ = e.target;
24702     else if (e.srcElement) targ = e.srcElement;
24703     if (targ.nodeType == 3) // defeat Safari bug
24704         targ = targ.parentNode;
24705
24706
24707     if (targ.className.indexOf('nodeControl') != -1 || targ.className == 'nodeTag')
24708     {
24709         // FIXME xxxpedro remove this
24710         //var isIE = false;
24711
24712         if(targ.className == 'nodeTag')
24713         {
24714             var control = isIE ? (targ.parentNode.previousSibling || targ) :
24715                           (targ.parentNode.previousSibling || targ);
24716
24717             selectElement(targ.parentNode.parentNode);
24718
24719             if (control.className.indexOf('nodeControl') == -1)
24720                 return;
24721
24722         } else
24723             control = targ;
24724
24725         FBL.cancelEvent(e);
24726
24727         var treeNode = isIE ? control.nextSibling : control.parentNode;
24728
24729         //FBL.Firebug.Console.log(treeNode);
24730
24731         if (control.className.indexOf(' nodeMaximized') != -1) {
24732             FBL.Firebug.HTML.removeTreeChildren(treeNode);
24733         } else {
24734             FBL.Firebug.HTML.appendTreeChildren(treeNode);
24735         }
24736     }
24737     else if (targ.className == 'nodeValue' || targ.className == 'nodeName')
24738     {
24739         /*
24740         var input = FBL.Firebug.chrome.document.getElementById('treeInput');
24741
24742         input.style.display = "block";
24743         input.style.left = targ.offsetLeft + 'px';
24744         input.style.top = FBL.topHeight + targ.offsetTop - FBL.fbPanel1.scrollTop + 'px';
24745         input.style.width = targ.offsetWidth + 6 + 'px';
24746         input.value = targ.textContent || targ.innerText;
24747         input.focus();
24748         /**/
24749     }
24750 };
24751
24752 function onListMouseOut(e)
24753 {
24754     e = e || event || window;
24755     var targ;
24756
24757     if (e.target) targ = e.target;
24758     else if (e.srcElement) targ = e.srcElement;
24759     if (targ.nodeType == 3) // defeat Safari bug
24760       targ = targ.parentNode;
24761
24762       if (hasClass(targ, "fbPanel")) {
24763           FBL.Firebug.Inspector.hideBoxModel();
24764           hoverElement = null;
24765       }
24766 };
24767
24768 var hoverElement = null;
24769 var hoverElementTS = 0;
24770
24771 Firebug.HTML.onListMouseMove = function onListMouseMove(e)
24772 {
24773     try
24774     {
24775         e = e || event || window;
24776         var targ;
24777
24778         if (e.target) targ = e.target;
24779         else if (e.srcElement) targ = e.srcElement;
24780         if (targ.nodeType == 3) // defeat Safari bug
24781             targ = targ.parentNode;
24782
24783         var found = false;
24784         while (targ && !found) {
24785             if (!/\snodeBox\s|\sobjectBox-selector\s/.test(" " + targ.className + " "))
24786                 targ = targ.parentNode;
24787             else
24788                 found = true;
24789         }
24790
24791         if (!targ)
24792         {
24793             FBL.Firebug.Inspector.hideBoxModel();
24794             hoverElement = null;
24795             return;
24796         }
24797
24798         /*
24799         if (typeof targ.attributes[cacheID] == 'undefined') return;
24800
24801         var uid = targ.attributes[cacheID];
24802         if (!uid) return;
24803         /**/
24804
24805         if (typeof targ.attributes[cacheID] == 'undefined') return;
24806
24807         var uid = targ.attributes[cacheID];
24808         if (!uid) return;
24809
24810         var el = ElementCache.get(uid.value);
24811
24812         var nodeName = el.nodeName.toLowerCase();
24813
24814         if (FBL.isIE && " meta title script link ".indexOf(" "+nodeName+" ") != -1)
24815             return;
24816
24817         if (!/\snodeBox\s|\sobjectBox-selector\s/.test(" " + targ.className + " ")) return;
24818
24819         if (el.id == "FirebugUI" || " html head body br script link iframe ".indexOf(" "+nodeName+" ") != -1) {
24820             FBL.Firebug.Inspector.hideBoxModel();
24821             hoverElement = null;
24822             return;
24823         }
24824
24825         if ((new Date().getTime() - hoverElementTS > 40) && hoverElement != el) {
24826             hoverElementTS = new Date().getTime();
24827             hoverElement = el;
24828             FBL.Firebug.Inspector.drawBoxModel(el);
24829         }
24830     }
24831     catch(E)
24832     {
24833     }
24834 };
24835
24836
24837 // ************************************************************************************************
24838
24839 Firebug.Reps = {
24840
24841     appendText: function(object, html)
24842     {
24843         html.push(escapeHTML(objectToString(object)));
24844     },
24845
24846     appendNull: function(object, html)
24847     {
24848         html.push('<span class="objectBox-null">', escapeHTML(objectToString(object)), '</span>');
24849     },
24850
24851     appendString: function(object, html)
24852     {
24853         html.push('<span class="objectBox-string">&quot;', escapeHTML(objectToString(object)),
24854             '&quot;</span>');
24855     },
24856
24857     appendInteger: function(object, html)
24858     {
24859         html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>');
24860     },
24861
24862     appendFloat: function(object, html)
24863     {
24864         html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>');
24865     },
24866
24867     appendFunction: function(object, html)
24868     {
24869         var reName = /function ?(.*?)\(/;
24870         var m = reName.exec(objectToString(object));
24871         var name = m && m[1] ? m[1] : "function";
24872         html.push('<span class="objectBox-function">', escapeHTML(name), '()</span>');
24873     },
24874
24875     appendObject: function(object, html)
24876     {
24877         /*
24878         var rep = Firebug.getRep(object);
24879         var outputs = [];
24880
24881         rep.tag.tag.compile();
24882
24883         var str = rep.tag.renderHTML({object: object}, outputs);
24884         html.push(str);
24885         /**/
24886
24887         try
24888         {
24889             if (object == undefined)
24890                 this.appendNull("undefined", html);
24891             else if (object == null)
24892                 this.appendNull("null", html);
24893             else if (typeof object == "string")
24894                 this.appendString(object, html);
24895             else if (typeof object == "number")
24896                 this.appendInteger(object, html);
24897             else if (typeof object == "boolean")
24898                 this.appendInteger(object, html);
24899             else if (typeof object == "function")
24900                 this.appendFunction(object, html);
24901             else if (object.nodeType == 1)
24902                 this.appendSelector(object, html);
24903             else if (typeof object == "object")
24904             {
24905                 if (typeof object.length != "undefined")
24906                     this.appendArray(object, html);
24907                 else
24908                     this.appendObjectFormatted(object, html);
24909             }
24910             else
24911                 this.appendText(object, html);
24912         }
24913         catch (exc)
24914         {
24915         }
24916         /**/
24917     },
24918
24919     appendObjectFormatted: function(object, html)
24920     {
24921         var text = objectToString(object);
24922         var reObject = /\[object (.*?)\]/;
24923
24924         var m = reObject.exec(text);
24925         html.push('<span class="objectBox-object">', m ? m[1] : text, '</span>');
24926     },
24927
24928     appendSelector: function(object, html)
24929     {
24930         var uid = ElementCache(object);
24931         var uidString = uid ? [cacheID, '="', uid, '"'].join("") : "";
24932
24933         html.push('<span class="objectBox-selector"', uidString, '>');
24934
24935         html.push('<span class="selectorTag">', escapeHTML(object.nodeName.toLowerCase()), '</span>');
24936         if (object.id)
24937             html.push('<span class="selectorId">#', escapeHTML(object.id), '</span>');
24938         if (object.className)
24939             html.push('<span class="selectorClass">.', escapeHTML(object.className), '</span>');
24940
24941         html.push('</span>');
24942     },
24943
24944     appendNode: function(node, html)
24945     {
24946         if (node.nodeType == 1)
24947         {
24948             var uid = ElementCache(node);
24949             var uidString = uid ? [cacheID, '="', uid, '"'].join("") : "";
24950
24951             html.push(
24952                 '<div class="objectBox-element"', uidString, '">',
24953                 '<span ', cacheID, '="', uid, '" class="nodeBox">',
24954                 '&lt;<span class="nodeTag">', node.nodeName.toLowerCase(), '</span>');
24955
24956             for (var i = 0; i < node.attributes.length; ++i)
24957             {
24958                 var attr = node.attributes[i];
24959                 if (!attr.specified || attr.nodeName == cacheID)
24960                     continue;
24961
24962                 var name = attr.nodeName.toLowerCase();
24963                 var value = name == "style" ? node.style.cssText : attr.nodeValue;
24964
24965                 html.push('&nbsp;<span class="nodeName">', name,
24966                     '</span>=&quot;<span class="nodeValue">', escapeHTML(value),
24967                     '</span>&quot;');
24968             }
24969
24970             if (node.firstChild)
24971             {
24972                 html.push('&gt;</div><div class="nodeChildren">');
24973
24974                 for (var child = node.firstChild; child; child = child.nextSibling)
24975                     this.appendNode(child, html);
24976
24977                 html.push('</div><div class="objectBox-element">&lt;/<span class="nodeTag">',
24978                     node.nodeName.toLowerCase(), '&gt;</span></span></div>');
24979             }
24980             else
24981                 html.push('/&gt;</span></div>');
24982         }
24983         else if (node.nodeType == 3)
24984         {
24985             var value = trim(node.nodeValue);
24986             if (value)
24987                 html.push('<div class="nodeText">', escapeHTML(value),'</div>');
24988         }
24989     },
24990
24991     appendArray: function(object, html)
24992     {
24993         html.push('<span class="objectBox-array"><b>[</b> ');
24994
24995         for (var i = 0, l = object.length, obj; i < l; ++i)
24996         {
24997             this.appendObject(object[i], html);
24998
24999             if (i < l-1)
25000             html.push(', ');
25001         }
25002
25003         html.push(' <b>]</b></span>');
25004     }
25005
25006 };
25007
25008
25009
25010 // ************************************************************************************************
25011 }});
25012
25013 /* See license.txt for terms of usage */
25014
25015 /*
25016
25017 Hack:
25018 Firebug.chrome.currentPanel = Firebug.chrome.selectedPanel;
25019 Firebug.showInfoTips = true;
25020 Firebug.InfoTip.initializeBrowser(Firebug.chrome);
25021
25022 /**/
25023
25024 FBL.ns(function() { with (FBL) {
25025
25026 // ************************************************************************************************
25027 // Constants
25028
25029 var maxWidth = 100, maxHeight = 80;
25030 var infoTipMargin = 10;
25031 var infoTipWindowPadding = 25;
25032
25033 // ************************************************************************************************
25034
25035 Firebug.InfoTip = extend(Firebug.Module,
25036 {
25037     dispatchName: "infoTip",
25038     tags: domplate(
25039     {
25040         infoTipTag: DIV({"class": "infoTip"}),
25041
25042         colorTag:
25043             DIV({style: "background: $rgbValue; width: 100px; height: 40px"}, "&nbsp;"),
25044
25045         imgTag:
25046             DIV({"class": "infoTipImageBox infoTipLoading"},
25047                 IMG({"class": "infoTipImage", src: "$urlValue", repeat: "$repeat",
25048                     onload: "$onLoadImage"}),
25049                 IMG({"class": "infoTipBgImage", collapsed: true, src: "blank.gif"}),
25050                 DIV({"class": "infoTipCaption"})
25051             ),
25052
25053         onLoadImage: function(event)
25054         {
25055             var img = event.currentTarget || event.srcElement;
25056             ///var bgImg = img.nextSibling;
25057             ///if (!bgImg)
25058             ///    return; // Sometimes gets called after element is dead
25059
25060             ///var caption = bgImg.nextSibling;
25061             var innerBox = img.parentNode;
25062
25063             /// TODO: xxxpedro infoTip hack
25064             var caption = getElementByClass(innerBox, "infoTipCaption");
25065             var bgImg = getElementByClass(innerBox, "infoTipBgImage");
25066             if (!bgImg)
25067                 return; // Sometimes gets called after element is dead
25068
25069             // TODO: xxxpedro infoTip IE and timing issue
25070             // TODO: use offline document to avoid flickering
25071             if (isIE)
25072                 removeClass(innerBox, "infoTipLoading");
25073
25074             var updateInfoTip = function(){
25075
25076             var w = img.naturalWidth || img.width || 10,
25077                 h = img.naturalHeight || img.height || 10;
25078
25079             var repeat = img.getAttribute("repeat");
25080
25081             if (repeat == "repeat-x" || (w == 1 && h > 1))
25082             {
25083                 collapse(img, true);
25084                 collapse(bgImg, false);
25085                 bgImg.style.background = "url(" + img.src + ") repeat-x";
25086                 bgImg.style.width = maxWidth + "px";
25087                 if (h > maxHeight)
25088                     bgImg.style.height = maxHeight + "px";
25089                 else
25090                     bgImg.style.height = h + "px";
25091             }
25092             else if (repeat == "repeat-y" || (h == 1 && w > 1))
25093             {
25094                 collapse(img, true);
25095                 collapse(bgImg, false);
25096                 bgImg.style.background = "url(" + img.src + ") repeat-y";
25097                 bgImg.style.height = maxHeight + "px";
25098                 if (w > maxWidth)
25099                     bgImg.style.width = maxWidth + "px";
25100                 else
25101                     bgImg.style.width = w + "px";
25102             }
25103             else if (repeat == "repeat" || (w == 1 && h == 1))
25104             {
25105                 collapse(img, true);
25106                 collapse(bgImg, false);
25107                 bgImg.style.background = "url(" + img.src + ") repeat";
25108                 bgImg.style.width = maxWidth + "px";
25109                 bgImg.style.height = maxHeight + "px";
25110             }
25111             else
25112             {
25113                 if (w > maxWidth || h > maxHeight)
25114                 {
25115                     if (w > h)
25116                     {
25117                         img.style.width = maxWidth + "px";
25118                         img.style.height = Math.round((h / w) * maxWidth) + "px";
25119                     }
25120                     else
25121                     {
25122                         img.style.width = Math.round((w / h) * maxHeight) + "px";
25123                         img.style.height = maxHeight + "px";
25124                     }
25125                 }
25126             }
25127
25128             //caption.innerHTML = $STRF("Dimensions", [w, h]);
25129             caption.innerHTML = $STRF(w + " x " + h);
25130
25131
25132             };
25133
25134             if (isIE)
25135                 setTimeout(updateInfoTip, 0);
25136             else
25137             {
25138                 updateInfoTip();
25139                 removeClass(innerBox, "infoTipLoading");
25140             }
25141
25142             ///
25143         }
25144
25145         /*
25146         /// onLoadImage original
25147         onLoadImage: function(event)
25148         {
25149             var img = event.currentTarget;
25150             var bgImg = img.nextSibling;
25151             if (!bgImg)
25152                 return; // Sometimes gets called after element is dead
25153
25154             var caption = bgImg.nextSibling;
25155             var innerBox = img.parentNode;
25156
25157             var w = img.naturalWidth, h = img.naturalHeight;
25158             var repeat = img.getAttribute("repeat");
25159
25160             if (repeat == "repeat-x" || (w == 1 && h > 1))
25161             {
25162                 collapse(img, true);
25163                 collapse(bgImg, false);
25164                 bgImg.style.background = "url(" + img.src + ") repeat-x";
25165                 bgImg.style.width = maxWidth + "px";
25166                 if (h > maxHeight)
25167                     bgImg.style.height = maxHeight + "px";
25168                 else
25169                     bgImg.style.height = h + "px";
25170             }
25171             else if (repeat == "repeat-y" || (h == 1 && w > 1))
25172             {
25173                 collapse(img, true);
25174                 collapse(bgImg, false);
25175                 bgImg.style.background = "url(" + img.src + ") repeat-y";
25176                 bgImg.style.height = maxHeight + "px";
25177                 if (w > maxWidth)
25178                     bgImg.style.width = maxWidth + "px";
25179                 else
25180                     bgImg.style.width = w + "px";
25181             }
25182             else if (repeat == "repeat" || (w == 1 && h == 1))
25183             {
25184                 collapse(img, true);
25185                 collapse(bgImg, false);
25186                 bgImg.style.background = "url(" + img.src + ") repeat";
25187                 bgImg.style.width = maxWidth + "px";
25188                 bgImg.style.height = maxHeight + "px";
25189             }
25190             else
25191             {
25192                 if (w > maxWidth || h > maxHeight)
25193                 {
25194                     if (w > h)
25195                     {
25196                         img.style.width = maxWidth + "px";
25197                         img.style.height = Math.round((h / w) * maxWidth) + "px";
25198                     }
25199                     else
25200                     {
25201                         img.style.width = Math.round((w / h) * maxHeight) + "px";
25202                         img.style.height = maxHeight + "px";
25203                     }
25204                 }
25205             }
25206
25207             caption.innerHTML = $STRF("Dimensions", [w, h]);
25208
25209             removeClass(innerBox, "infoTipLoading");
25210         }
25211         /**/
25212
25213     }),
25214
25215     initializeBrowser: function(browser)
25216     {
25217         browser.onInfoTipMouseOut = bind(this.onMouseOut, this, browser);
25218         browser.onInfoTipMouseMove = bind(this.onMouseMove, this, browser);
25219
25220         ///var doc = browser.contentDocument;
25221         var doc = browser.document;
25222         if (!doc)
25223             return;
25224
25225         ///doc.addEventListener("mouseover", browser.onInfoTipMouseMove, true);
25226         ///doc.addEventListener("mouseout", browser.onInfoTipMouseOut, true);
25227         ///doc.addEventListener("mousemove", browser.onInfoTipMouseMove, true);
25228         addEvent(doc, "mouseover", browser.onInfoTipMouseMove);
25229         addEvent(doc, "mouseout", browser.onInfoTipMouseOut);
25230         addEvent(doc, "mousemove", browser.onInfoTipMouseMove);
25231
25232         return browser.infoTip = this.tags.infoTipTag.append({}, getBody(doc));
25233     },
25234
25235     uninitializeBrowser: function(browser)
25236     {
25237         if (browser.infoTip)
25238         {
25239             ///var doc = browser.contentDocument;
25240             var doc = browser.document;
25241             ///doc.removeEventListener("mouseover", browser.onInfoTipMouseMove, true);
25242             ///doc.removeEventListener("mouseout", browser.onInfoTipMouseOut, true);
25243             ///doc.removeEventListener("mousemove", browser.onInfoTipMouseMove, true);
25244             removeEvent(doc, "mouseover", browser.onInfoTipMouseMove);
25245             removeEvent(doc, "mouseout", browser.onInfoTipMouseOut);
25246             removeEvent(doc, "mousemove", browser.onInfoTipMouseMove);
25247
25248             browser.infoTip.parentNode.removeChild(browser.infoTip);
25249             delete browser.infoTip;
25250             delete browser.onInfoTipMouseMove;
25251         }
25252     },
25253
25254     showInfoTip: function(infoTip, panel, target, x, y, rangeParent, rangeOffset)
25255     {
25256         if (!Firebug.showInfoTips)
25257             return;
25258
25259         var scrollParent = getOverflowParent(target);
25260         var scrollX = x + (scrollParent ? scrollParent.scrollLeft : 0);
25261
25262         if (panel.showInfoTip(infoTip, target, scrollX, y, rangeParent, rangeOffset))
25263         {
25264             var htmlElt = infoTip.ownerDocument.documentElement;
25265             var panelWidth = htmlElt.clientWidth;
25266             var panelHeight = htmlElt.clientHeight;
25267
25268             if (x+infoTip.offsetWidth+infoTipMargin > panelWidth)
25269             {
25270                 infoTip.style.left = Math.max(0, panelWidth-(infoTip.offsetWidth+infoTipMargin)) + "px";
25271                 infoTip.style.right = "auto";
25272             }
25273             else
25274             {
25275                 infoTip.style.left = (x+infoTipMargin) + "px";
25276                 infoTip.style.right = "auto";
25277             }
25278
25279             if (y+infoTip.offsetHeight+infoTipMargin > panelHeight)
25280             {
25281                 infoTip.style.top = Math.max(0, panelHeight-(infoTip.offsetHeight+infoTipMargin)) + "px";
25282                 infoTip.style.bottom = "auto";
25283             }
25284             else
25285             {
25286                 infoTip.style.top = (y+infoTipMargin) + "px";
25287                 infoTip.style.bottom = "auto";
25288             }
25289
25290             if (FBTrace.DBG_INFOTIP)
25291                 FBTrace.sysout("infotip.showInfoTip; top: " + infoTip.style.top +
25292                     ", left: " + infoTip.style.left + ", bottom: " + infoTip.style.bottom +
25293                     ", right:" + infoTip.style.right + ", offsetHeight: " + infoTip.offsetHeight +
25294                     ", offsetWidth: " + infoTip.offsetWidth +
25295                     ", x: " + x + ", panelWidth: " + panelWidth +
25296                     ", y: " + y + ", panelHeight: " + panelHeight);
25297
25298             infoTip.setAttribute("active", "true");
25299         }
25300         else
25301             this.hideInfoTip(infoTip);
25302     },
25303
25304     hideInfoTip: function(infoTip)
25305     {
25306         if (infoTip)
25307             infoTip.removeAttribute("active");
25308     },
25309
25310     onMouseOut: function(event, browser)
25311     {
25312         if (!event.relatedTarget)
25313             this.hideInfoTip(browser.infoTip);
25314     },
25315
25316     onMouseMove: function(event, browser)
25317     {
25318         // Ignore if the mouse is moving over the existing info tip.
25319         if (getAncestorByClass(event.target, "infoTip"))
25320             return;
25321
25322         if (browser.currentPanel)
25323         {
25324             var x = event.clientX, y = event.clientY, target = event.target || event.srcElement;
25325             this.showInfoTip(browser.infoTip, browser.currentPanel, target, x, y, event.rangeParent, event.rangeOffset);
25326         }
25327         else
25328             this.hideInfoTip(browser.infoTip);
25329     },
25330
25331     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
25332
25333     populateColorInfoTip: function(infoTip, color)
25334     {
25335         this.tags.colorTag.replace({rgbValue: color}, infoTip);
25336         return true;
25337     },
25338
25339     populateImageInfoTip: function(infoTip, url, repeat)
25340     {
25341         if (!repeat)
25342             repeat = "no-repeat";
25343
25344         this.tags.imgTag.replace({urlValue: url, repeat: repeat}, infoTip);
25345
25346         return true;
25347     },
25348
25349     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
25350     // extends Module
25351
25352     disable: function()
25353     {
25354         // XXXjoe For each browser, call uninitializeBrowser
25355     },
25356
25357     showPanel: function(browser, panel)
25358     {
25359         if (panel)
25360         {
25361             var infoTip = panel.panelBrowser.infoTip;
25362             if (!infoTip)
25363                 infoTip = this.initializeBrowser(panel.panelBrowser);
25364             this.hideInfoTip(infoTip);
25365         }
25366
25367     },
25368
25369     showSidePanel: function(browser, panel)
25370     {
25371         this.showPanel(browser, panel);
25372     }
25373 });
25374
25375 // ************************************************************************************************
25376
25377 Firebug.registerModule(Firebug.InfoTip);
25378
25379 // ************************************************************************************************
25380
25381 }});
25382
25383
25384 /* See license.txt for terms of usage */
25385
25386 FBL.ns(function() { with (FBL) {
25387 // ************************************************************************************************
25388
25389 var CssParser = null;
25390
25391 // ************************************************************************************************
25392
25393 // Simple CSS stylesheet parser from:
25394 // https://github.com/sergeche/webkit-css
25395
25396 /**
25397  * Simple CSS stylesheet parser that remembers rule's lines in file
25398  * @author Sergey Chikuyonok (serge.che@gmail.com)
25399  * @link http://chikuyonok.ru
25400  */
25401 CssParser = (function(){
25402     /**
25403      * Returns rule object
25404      * @param {Number} start Character index where CSS rule definition starts
25405      * @param {Number} body_start Character index where CSS rule's body starts
25406      * @param {Number} end Character index where CSS rule definition ends
25407      */
25408     function rule(start, body_start, end) {
25409         return {
25410             start: start || 0,
25411             body_start: body_start || 0,
25412             end: end || 0,
25413             line: -1,
25414             selector: null,
25415             parent: null,
25416
25417             /** @type {rule[]} */
25418             children: [],
25419
25420             addChild: function(start, body_start, end) {
25421                 var r = rule(start, body_start, end);
25422                 r.parent = this;
25423                 this.children.push(r);
25424                 return r;
25425             },
25426             /**
25427              * Returns last child element
25428              * @return {rule}
25429              */
25430             lastChild: function() {
25431                 return this.children[this.children.length - 1];
25432             }
25433         };
25434     }
25435
25436     /**
25437      * Replaces all occurances of substring defined by regexp
25438      * @param {String} str
25439      * @return {RegExp} re
25440      * @return {String}
25441      */
25442     function removeAll(str, re) {
25443         var m;
25444         while (m = str.match(re)) {
25445             str = str.substring(m[0].length);
25446         }
25447
25448         return str;
25449     }
25450
25451     /**
25452      * Trims whitespace from the beginning and the end of string
25453      * @param {String} str
25454      * @return {String}
25455      */
25456     function trim(str) {
25457         return str.replace(/^\s+|\s+$/g, '');
25458     }
25459
25460     /**
25461      * Normalizes CSS rules selector
25462      * @param {String} selector
25463      */
25464     function normalizeSelector(selector) {
25465         // remove newlines
25466         selector = selector.replace(/[\n\r]/g, ' ');
25467
25468         selector = trim(selector);
25469
25470         // remove spaces after commas
25471         selector = selector.replace(/\s*,\s*/g, ',');
25472
25473         return selector;
25474     }
25475
25476     /**
25477      * Preprocesses parsed rules: adjusts char indexes, skipping whitespace and
25478      * newlines, saves rule selector, removes comments, etc.
25479      * @param {String} text CSS stylesheet
25480      * @param {rule} rule_node CSS rule node
25481      * @return {rule[]}
25482      */
25483     function preprocessRules(text, rule_node) {
25484         for (var i = 0, il = rule_node.children.length; i < il; i++) {
25485             var r = rule_node.children[i],
25486                 rule_start = text.substring(r.start, r.body_start),
25487                 cur_len = rule_start.length;
25488
25489             // remove newlines for better regexp matching
25490             rule_start = rule_start.replace(/[\n\r]/g, ' ');
25491
25492             // remove @import rules
25493 //            rule_start = removeAll(rule_start, /^\s*@import\s*url\((['"])?.+?\1?\)\;?/g);
25494
25495             // remove comments
25496             rule_start = removeAll(rule_start, /^\s*\/\*.*?\*\/[\s\t]*/);
25497
25498             // remove whitespace
25499             rule_start = rule_start.replace(/^[\s\t]+/, '');
25500
25501             r.start += (cur_len - rule_start.length);
25502             r.selector = normalizeSelector(rule_start);
25503         }
25504
25505         return rule_node;
25506     }
25507
25508     /**
25509      * Saves all lise starting indexes for faster search
25510      * @param {String} text CSS stylesheet
25511      * @return {Number[]}
25512      */
25513     function saveLineIndexes(text) {
25514         var result = [0],
25515             i = 0,
25516             il = text.length,
25517             ch, ch2;
25518
25519         while (i < il) {
25520             ch = text.charAt(i);
25521
25522             if (ch == '\n' || ch == '\r') {
25523                 if (ch == '\r' && i < il - 1 && text.charAt(i + 1) == '\n') {
25524                     // windows line ending: CRLF. Skip next character
25525                     i++;
25526                 }
25527
25528                 result.push(i + 1);
25529             }
25530
25531             i++;
25532         }
25533
25534         return result;
25535     }
25536
25537     /**
25538      * Saves line number for parsed rules
25539      * @param {String} text CSS stylesheet
25540      * @param {rule} rule_node Rule node
25541      * @return {rule[]}
25542      */
25543     function saveLineNumbers(text, rule_node, line_indexes, startLine) {
25544         preprocessRules(text, rule_node);
25545
25546         startLine = startLine || 0;
25547
25548         // remember lines start indexes, preserving line ending characters
25549         if (!line_indexes)
25550             var line_indexes = saveLineIndexes(text);
25551
25552         // now find each rule's line
25553         for (var i = 0, il = rule_node.children.length; i < il; i++) {
25554             var r = rule_node.children[i];
25555             r.line = line_indexes.length + startLine;
25556             for (var j = 0, jl = line_indexes.length - 1; j < jl; j++) {
25557                 var line_ix = line_indexes[j];
25558                 if (r.start >=  line_indexes[j] && r.start <  line_indexes[j + 1]) {
25559                     r.line = j + 1 + startLine;
25560                     break;
25561                 }
25562             }
25563
25564             saveLineNumbers(text, r, line_indexes);
25565         }
25566
25567         return rule_node;
25568     }
25569
25570     return {
25571         /**
25572          * Parses text as CSS stylesheet, remembring each rule position inside
25573          * text
25574          * @param {String} text CSS stylesheet to parse
25575          */
25576         read: function(text, startLine) {
25577             var rule_start = [],
25578                 rule_body_start = [],
25579                 rules = [],
25580                 in_comment = 0,
25581                 root = rule(),
25582                 cur_parent = root,
25583                 last_rule = null,
25584                 stack = [],
25585                 ch, ch2;
25586
25587             stack.last = function() {
25588                 return this[this.length - 1];
25589             };
25590
25591             function hasStr(pos, substr) {
25592                 return text.substr(pos, substr.length) == substr;
25593             }
25594
25595             for (var i = 0, il = text.length; i < il; i++) {
25596                 ch = text.charAt(i);
25597                 ch2 = i < il - 1 ? text.charAt(i + 1) : '';
25598
25599                 if (!rule_start.length)
25600                     rule_start.push(i);
25601
25602                 switch (ch) {
25603                     case '@':
25604                         if (!in_comment) {
25605                             if (hasStr(i, '@import')) {
25606                                 var m = text.substr(i).match(/^@import\s*url\((['"])?.+?\1?\)\;?/);
25607                                 if (m) {
25608                                     cur_parent.addChild(i, i + 7, i + m[0].length);
25609                                     i += m[0].length;
25610                                     rule_start.pop();
25611                                 }
25612                                 break;
25613                             }
25614                         }
25615                     case '/':
25616                         // xxxpedro allowing comment inside comment
25617                         if (!in_comment && ch2 == '*') { // comment start
25618                             in_comment++;
25619                         }
25620                         break;
25621
25622                     case '*':
25623                         if (ch2 == '/') { // comment end
25624                             in_comment--;
25625                         }
25626                         break;
25627
25628                     case '{':
25629                         if (!in_comment) {
25630                             rule_body_start.push(i);
25631
25632                             cur_parent = cur_parent.addChild(rule_start.pop());
25633                             stack.push(cur_parent);
25634                         }
25635                         break;
25636
25637                     case '}':
25638                         // found the end of the rule
25639                         if (!in_comment) {
25640                             /** @type {rule} */
25641                             var last_rule = stack.pop();
25642                             rule_start.pop();
25643                             last_rule.body_start = rule_body_start.pop();
25644                             last_rule.end = i;
25645                             cur_parent = last_rule.parent || root;
25646                         }
25647                         break;
25648                 }
25649
25650             }
25651
25652             return saveLineNumbers(text, root, null, startLine);
25653         },
25654
25655         normalizeSelector: normalizeSelector,
25656
25657         /**
25658          * Find matched rule by selector.
25659          * @param {rule} rule_node Parsed rule node
25660          * @param {String} selector CSS selector
25661          * @param {String} source CSS stylesheet source code
25662          *
25663          * @return {rule[]|null} Array of matched rules, sorted by priority (most
25664          * recent on top)
25665          */
25666         findBySelector: function(rule_node, selector, source) {
25667             var selector = normalizeSelector(selector),
25668                 result = [];
25669
25670             if (rule_node) {
25671                 for (var i = 0, il = rule_node.children.length; i < il; i++) {
25672                     /** @type {rule} */
25673                     var r = rule_node.children[i];
25674                     if (r.selector == selector) {
25675                         result.push(r);
25676                     }
25677                 }
25678             }
25679
25680             if (result.length) {
25681                 return result;
25682             } else {
25683                 return null;
25684             }
25685         }
25686     };
25687 })();
25688
25689
25690 // ************************************************************************************************
25691
25692 FBL.CssParser = CssParser;
25693
25694 // ************************************************************************************************
25695 }});
25696
25697 /* See license.txt for terms of usage */
25698
25699 FBL.ns(function() { with (FBL) {
25700
25701 // ************************************************************************************************
25702 // StyleSheet Parser
25703
25704 var CssAnalyzer = {};
25705
25706 // ************************************************************************************************
25707 // Locals
25708
25709 var CSSRuleMap = {};
25710 var ElementCSSRulesMap = {};
25711
25712 var internalStyleSheetIndex = -1;
25713
25714 var reSelectorTag = /(^|\s)(?:\w+)/g;
25715 var reSelectorClass = /\.[\w\d_-]+/g;
25716 var reSelectorId = /#[\w\d_-]+/g;
25717
25718 var globalCSSRuleIndex;
25719
25720 var processAllStyleSheetsTimeout = null;
25721
25722 var externalStyleSheetURLs = [];
25723
25724 var ElementCache = Firebug.Lite.Cache.Element;
25725 var StyleSheetCache = Firebug.Lite.Cache.StyleSheet;
25726
25727 //************************************************************************************************
25728 // CSS Analyzer templates
25729
25730 CssAnalyzer.externalStyleSheetWarning = domplate(Firebug.Rep,
25731 {
25732     tag:
25733         DIV({"class": "warning focusRow", style: "font-weight:normal;", role: 'listitem'},
25734             SPAN("$object|STR"),
25735             A({"href": "$href", target:"_blank"}, "$link|STR")
25736         )
25737 });
25738
25739 // ************************************************************************************************
25740 // CSS Analyzer methods
25741
25742 CssAnalyzer.processAllStyleSheets = function(doc, styleSheetIterator)
25743 {
25744     try
25745     {
25746         processAllStyleSheets(doc, styleSheetIterator);
25747     }
25748     catch(e)
25749     {
25750         // TODO: FBTrace condition
25751         FBTrace.sysout("CssAnalyzer.processAllStyleSheets fails: ", e);
25752     }
25753 };
25754
25755 /**
25756  *
25757  * @param element
25758  * @returns {String[]} Array of IDs of CSS Rules
25759  */
25760 CssAnalyzer.getElementCSSRules = function(element)
25761 {
25762     try
25763     {
25764         return getElementCSSRules(element);
25765     }
25766     catch(e)
25767     {
25768         // TODO: FBTrace condition
25769         FBTrace.sysout("CssAnalyzer.getElementCSSRules fails: ", e);
25770     }
25771 };
25772
25773 CssAnalyzer.getRuleData = function(ruleId)
25774 {
25775     return CSSRuleMap[ruleId];
25776 };
25777
25778 // TODO: do we need this?
25779 CssAnalyzer.getRuleLine = function()
25780 {
25781 };
25782
25783 CssAnalyzer.hasExternalStyleSheet = function()
25784 {
25785     return externalStyleSheetURLs.length > 0;
25786 };
25787
25788 CssAnalyzer.parseStyleSheet = function(href)
25789 {
25790     var sourceData = extractSourceData(href);
25791     var parsedObj = CssParser.read(sourceData.source, sourceData.startLine);
25792     var parsedRules = parsedObj.children;
25793
25794     // See: Issue 4776: [Firebug lite] CSS Media Types
25795     //
25796     // Ignore all special selectors like @media and @page
25797     for(var i=0; i < parsedRules.length; )
25798     {
25799         if (parsedRules[i].selector.indexOf("@") != -1)
25800         {
25801             parsedRules.splice(i, 1);
25802         }
25803         else
25804             i++;
25805     }
25806
25807     return parsedRules;
25808 };
25809
25810 //************************************************************************************************
25811 // Internals
25812 //************************************************************************************************
25813
25814 // ************************************************************************************************
25815 // StyleSheet processing
25816
25817 var processAllStyleSheets = function(doc, styleSheetIterator)
25818 {
25819     styleSheetIterator = styleSheetIterator || processStyleSheet;
25820
25821     globalCSSRuleIndex = -1;
25822
25823     var styleSheets = doc.styleSheets;
25824     var importedStyleSheets = [];
25825
25826     if (FBTrace.DBG_CSS)
25827         var start = new Date().getTime();
25828
25829     for(var i=0, length=styleSheets.length; i<length; i++)
25830     {
25831         try
25832         {
25833             var styleSheet = styleSheets[i];
25834
25835             if ("firebugIgnore" in styleSheet) continue;
25836
25837             // we must read the length to make sure we have permission to read
25838             // the stylesheet's content. If an error occurs here, we cannot
25839             // read the stylesheet due to access restriction policy
25840             var rules = isIE ? styleSheet.rules : styleSheet.cssRules;
25841             rules.length;
25842         }
25843         catch(e)
25844         {
25845             externalStyleSheetURLs.push(styleSheet.href);
25846             styleSheet.restricted = true;
25847             var ssid = StyleSheetCache(styleSheet);
25848
25849             /// TODO: xxxpedro external css
25850             //loadExternalStylesheet(doc, styleSheetIterator, styleSheet);
25851         }
25852
25853         // process internal and external styleSheets
25854         styleSheetIterator(doc, styleSheet);
25855
25856         var importedStyleSheet, importedRules;
25857
25858         // process imported styleSheets in IE
25859         if (isIE)
25860         {
25861             var imports = styleSheet.imports;
25862
25863             for(var j=0, importsLength=imports.length; j<importsLength; j++)
25864             {
25865                 try
25866                 {
25867                     importedStyleSheet = imports[j];
25868                     // we must read the length to make sure we have permission
25869                     // to read the imported stylesheet's content.
25870                     importedRules = importedStyleSheet.rules;
25871                     importedRules.length;
25872                 }
25873                 catch(e)
25874                 {
25875                     externalStyleSheetURLs.push(styleSheet.href);
25876                     importedStyleSheet.restricted = true;
25877                     var ssid = StyleSheetCache(importedStyleSheet);
25878                 }
25879
25880                 styleSheetIterator(doc, importedStyleSheet);
25881             }
25882         }
25883         // process imported styleSheets in other browsers
25884         else if (rules)
25885         {
25886             for(var j=0, rulesLength=rules.length; j<rulesLength; j++)
25887             {
25888                 try
25889                 {
25890                     var rule = rules[j];
25891
25892                     importedStyleSheet = rule.styleSheet;
25893
25894                     if (importedStyleSheet)
25895                     {
25896                         // we must read the length to make sure we have permission
25897                         // to read the imported stylesheet's content.
25898                         importedRules = importedStyleSheet.cssRules;
25899                         importedRules.length;
25900                     }
25901                     else
25902                         break;
25903                 }
25904                 catch(e)
25905                 {
25906                     externalStyleSheetURLs.push(styleSheet.href);
25907                     importedStyleSheet.restricted = true;
25908                     var ssid = StyleSheetCache(importedStyleSheet);
25909                 }
25910
25911                 styleSheetIterator(doc, importedStyleSheet);
25912             }
25913         }
25914     };
25915
25916     if (FBTrace.DBG_CSS)
25917     {
25918         FBTrace.sysout("FBL.processAllStyleSheets", "all stylesheet rules processed in " + (new Date().getTime() - start) + "ms");
25919     }
25920 };
25921
25922 // ************************************************************************************************
25923
25924 var processStyleSheet = function(doc, styleSheet)
25925 {
25926     if (styleSheet.restricted)
25927         return;
25928
25929     var rules = isIE ? styleSheet.rules : styleSheet.cssRules;
25930
25931     var ssid = StyleSheetCache(styleSheet);
25932
25933     var href = styleSheet.href;
25934
25935     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
25936     // CSS Parser
25937     var shouldParseCSS = typeof CssParser != "undefined" && !Firebug.disableResourceFetching;
25938     if (shouldParseCSS)
25939     {
25940         try
25941         {
25942             var parsedRules = CssAnalyzer.parseStyleSheet(href);
25943         }
25944         catch(e)
25945         {
25946             if (FBTrace.DBG_ERRORS) FBTrace.sysout("processStyleSheet FAILS", e.message || e);
25947             shouldParseCSS = false;
25948         }
25949         finally
25950         {
25951             var parsedRulesIndex = 0;
25952
25953             var dontSupportGroupedRules = isIE && browserVersion < 9;
25954             var group = [];
25955             var groupItem;
25956         }
25957     }
25958     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
25959
25960     for (var i=0, length=rules.length; i<length; i++)
25961     {
25962         // TODO: xxxpedro is there a better way to cache CSS Rules? The problem is that
25963         // we cannot add expando properties in the rule object in IE
25964         var rid = ssid + ":" + i;
25965         var rule = rules[i];
25966         var selector = rule.selectorText || "";
25967         var lineNo = null;
25968
25969         // See: Issue 4776: [Firebug lite] CSS Media Types
25970         //
25971         // Ignore all special selectors like @media and @page
25972         if (!selector || selector.indexOf("@") != -1)
25973             continue;
25974
25975         if (isIE)
25976             selector = selector.replace(reSelectorTag, function(s){return s.toLowerCase();});
25977
25978         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
25979         // CSS Parser
25980         if (shouldParseCSS)
25981         {
25982             var parsedRule = parsedRules[parsedRulesIndex];
25983             var parsedSelector = parsedRule.selector;
25984
25985             if (dontSupportGroupedRules && parsedSelector.indexOf(",") != -1 && group.length == 0)
25986                 group = parsedSelector.split(",");
25987
25988             if (dontSupportGroupedRules && group.length > 0)
25989             {
25990                 groupItem = group.shift();
25991
25992                 if (CssParser.normalizeSelector(selector) == groupItem)
25993                     lineNo = parsedRule.line;
25994
25995                 if (group.length == 0)
25996                     parsedRulesIndex++;
25997             }
25998             else if (CssParser.normalizeSelector(selector) == parsedRule.selector)
25999             {
26000                 lineNo = parsedRule.line;
26001                 parsedRulesIndex++;
26002             }
26003         }
26004         // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26005
26006         CSSRuleMap[rid] =
26007         {
26008             styleSheetId: ssid,
26009             styleSheetIndex: i,
26010             order: ++globalCSSRuleIndex,
26011             specificity:
26012                 // See: Issue 4777: [Firebug lite] Specificity of CSS Rules
26013                 //
26014                 // if it is a normal selector then calculate the specificity
26015                 selector && selector.indexOf(",") == -1 ?
26016                 getCSSRuleSpecificity(selector) :
26017                 // See: Issue 3262: [Firebug lite] Specificity of grouped CSS Rules
26018                 //
26019                 // if it is a grouped selector, do not calculate the specificity
26020                 // because the correct value will depend of the matched element.
26021                 // The proper specificity value for grouped selectors are calculated
26022                 // via getElementCSSRules(element)
26023                 0,
26024
26025             rule: rule,
26026             lineNo: lineNo,
26027             selector: selector,
26028             cssText: rule.style ? rule.style.cssText : rule.cssText ? rule.cssText : ""
26029         };
26030
26031         // TODO: what happens with elements added after this? Need to create a test case.
26032         // Maybe we should place this at getElementCSSRules() but it will make the function
26033         // a lot more expensive.
26034         //
26035         // Maybe add a "refresh" button?
26036         var elements = Firebug.Selector(selector, doc);
26037
26038         for (var j=0, elementsLength=elements.length; j<elementsLength; j++)
26039         {
26040             var element = elements[j];
26041             var eid = ElementCache(element);
26042
26043             if (!ElementCSSRulesMap[eid])
26044                 ElementCSSRulesMap[eid] = [];
26045
26046             ElementCSSRulesMap[eid].push(rid);
26047         }
26048
26049         //console.log(selector, elements);
26050     }
26051 };
26052
26053 // ************************************************************************************************
26054 // External StyleSheet Loader
26055
26056 var loadExternalStylesheet = function(doc, styleSheetIterator, styleSheet)
26057 {
26058     var url = styleSheet.href;
26059     styleSheet.firebugIgnore = true;
26060
26061     var source = Firebug.Lite.Proxy.load(url);
26062
26063     // TODO: check for null and error responses
26064
26065     // remove comments
26066     //var reMultiComment = /(\/\*([^\*]|\*(?!\/))*\*\/)/g;
26067     //source = source.replace(reMultiComment, "");
26068
26069     // convert relative addresses to absolute ones
26070     source = source.replace(/url\(([^\)]+)\)/g, function(a,name){
26071
26072         var hasDomain = /\w+:\/\/./.test(name);
26073
26074         if (!hasDomain)
26075         {
26076             name = name.replace(/^(["'])(.+)\1$/, "$2");
26077             var first = name.charAt(0);
26078
26079             // relative path, based on root
26080             if (first == "/")
26081             {
26082                 // TODO: xxxpedro move to lib or Firebug.Lite.something
26083                 // getURLRoot
26084                 var m = /^([^:]+:\/{1,3}[^\/]+)/.exec(url);
26085
26086                 return m ?
26087                     "url(" + m[1] + name + ")" :
26088                     "url(" + name + ")";
26089             }
26090             // relative path, based on current location
26091             else
26092             {
26093                 // TODO: xxxpedro move to lib or Firebug.Lite.something
26094                 // getURLPath
26095                 var path = url.replace(/[^\/]+\.[\w\d]+(\?.+|#.+)?$/g, "");
26096
26097                 path = path + name;
26098
26099                 var reBack = /[^\/]+\/\.\.\//;
26100                 while(reBack.test(path))
26101                 {
26102                     path = path.replace(reBack, "");
26103                 }
26104
26105                 //console.log("url(" + path + ")");
26106
26107                 return "url(" + path + ")";
26108             }
26109         }
26110
26111         // if it is an absolute path, there is nothing to do
26112         return a;
26113     });
26114
26115     var oldStyle = styleSheet.ownerNode;
26116
26117     if (!oldStyle) return;
26118
26119     if (!oldStyle.parentNode) return;
26120
26121     var style = createGlobalElement("style");
26122     style.setAttribute("charset","utf-8");
26123     style.setAttribute("type", "text/css");
26124     style.innerHTML = source;
26125
26126     //debugger;
26127     oldStyle.parentNode.insertBefore(style, oldStyle.nextSibling);
26128     oldStyle.parentNode.removeChild(oldStyle);
26129
26130     doc.styleSheets[doc.styleSheets.length-1].externalURL = url;
26131
26132     console.log(url, "call " + externalStyleSheetURLs.length, source);
26133
26134     externalStyleSheetURLs.pop();
26135
26136     if (processAllStyleSheetsTimeout)
26137     {
26138         clearTimeout(processAllStyleSheetsTimeout);
26139     }
26140
26141     processAllStyleSheetsTimeout = setTimeout(function(){
26142         console.log("processing");
26143         FBL.processAllStyleSheets(doc, styleSheetIterator);
26144         processAllStyleSheetsTimeout = null;
26145     },200);
26146
26147 };
26148
26149 //************************************************************************************************
26150 // getElementCSSRules
26151
26152 var getElementCSSRules = function(element)
26153 {
26154     var eid = ElementCache(element);
26155     var rules = ElementCSSRulesMap[eid];
26156
26157     if (!rules) return;
26158
26159     var arr = [element];
26160     var Selector = Firebug.Selector;
26161     var ruleId, rule;
26162
26163     // for the case of grouped selectors, we need to calculate the highest
26164     // specificity within the selectors of the group that matches the element,
26165     // so we can sort the rules properly without over estimating the specificity
26166     // of grouped selectors
26167     for (var i = 0, length = rules.length; i < length; i++)
26168     {
26169         ruleId = rules[i];
26170         rule = CSSRuleMap[ruleId];
26171
26172         // check if it is a grouped selector
26173         if (rule.selector.indexOf(",") != -1)
26174         {
26175             var selectors = rule.selector.split(",");
26176             var maxSpecificity = -1;
26177             var sel, spec, mostSpecificSelector;
26178
26179             // loop over all selectors in the group
26180             for (var j, len = selectors.length; j < len; j++)
26181             {
26182                 sel = selectors[j];
26183
26184                 // find if the selector matches the element
26185                 if (Selector.matches(sel, arr).length == 1)
26186                 {
26187                     spec = getCSSRuleSpecificity(sel);
26188
26189                     // find the most specific selector that macthes the element
26190                     if (spec > maxSpecificity)
26191                     {
26192                         maxSpecificity = spec;
26193                         mostSpecificSelector = sel;
26194                     }
26195                 }
26196             }
26197
26198             rule.specificity = maxSpecificity;
26199         }
26200     }
26201
26202     rules.sort(sortElementRules);
26203     //rules.sort(solveRulesTied);
26204
26205     return rules;
26206 };
26207
26208 // ************************************************************************************************
26209 // Rule Specificity
26210
26211 var sortElementRules = function(a, b)
26212 {
26213     var ruleA = CSSRuleMap[a];
26214     var ruleB = CSSRuleMap[b];
26215
26216     var specificityA = ruleA.specificity;
26217     var specificityB = ruleB.specificity;
26218
26219     if (specificityA > specificityB)
26220         return 1;
26221
26222     else if (specificityA < specificityB)
26223         return -1;
26224
26225     else
26226         return ruleA.order > ruleB.order ? 1 : -1;
26227 };
26228
26229 var solveRulesTied = function(a, b)
26230 {
26231     var ruleA = CSSRuleMap[a];
26232     var ruleB = CSSRuleMap[b];
26233
26234     if (ruleA.specificity == ruleB.specificity)
26235         return ruleA.order > ruleB.order ? 1 : -1;
26236
26237     return null;
26238 };
26239
26240 var getCSSRuleSpecificity = function(selector)
26241 {
26242     var match = selector.match(reSelectorTag);
26243     var tagCount = match ? match.length : 0;
26244
26245     match = selector.match(reSelectorClass);
26246     var classCount = match ? match.length : 0;
26247
26248     match = selector.match(reSelectorId);
26249     var idCount = match ? match.length : 0;
26250
26251     return tagCount + 10*classCount + 100*idCount;
26252 };
26253
26254 // ************************************************************************************************
26255 // StyleSheet data
26256
26257 var extractSourceData = function(href)
26258 {
26259     var sourceData =
26260     {
26261         source: null,
26262         startLine: 0
26263     };
26264
26265     if (href)
26266     {
26267         sourceData.source = Firebug.Lite.Proxy.load(href);
26268     }
26269     else
26270     {
26271         // TODO: create extractInternalSourceData(index)
26272         // TODO: pre process the position of the inline styles so this will happen only once
26273         // in case of having multiple inline styles
26274         var index = 0;
26275         var ssIndex = ++internalStyleSheetIndex;
26276         var reStyleTag = /\<\s*style.*\>/gi;
26277         var reEndStyleTag = /\<\/\s*style.*\>/gi;
26278
26279         var source = Firebug.Lite.Proxy.load(Env.browser.location.href);
26280         source = source.replace(/\n\r|\r\n/g, "\n"); // normalize line breaks
26281
26282         var startLine = 0;
26283
26284         do
26285         {
26286             var matchStyleTag = source.match(reStyleTag);
26287             var i0 = source.indexOf(matchStyleTag[0]) + matchStyleTag[0].length;
26288
26289             for (var i=0; i < i0; i++)
26290             {
26291                 if (source.charAt(i) == "\n")
26292                     startLine++;
26293             }
26294
26295             source = source.substr(i0);
26296
26297             index++;
26298         }
26299         while (index <= ssIndex);
26300
26301         var matchEndStyleTag = source.match(reEndStyleTag);
26302         var i1 = source.indexOf(matchEndStyleTag[0]);
26303
26304         var extractedSource = source.substr(0, i1);
26305
26306         sourceData.source = extractedSource;
26307         sourceData.startLine = startLine;
26308     }
26309
26310     return sourceData;
26311 };
26312
26313 // ************************************************************************************************
26314 // Registration
26315
26316 FBL.CssAnalyzer = CssAnalyzer;
26317
26318 // ************************************************************************************************
26319 }});
26320
26321
26322 /* See license.txt for terms of usage */
26323
26324 // move to FBL
26325 (function() {
26326
26327 // ************************************************************************************************
26328 // XPath
26329
26330 /**
26331  * Gets an XPath for an element which describes its hierarchical location.
26332  */
26333 this.getElementXPath = function(element)
26334 {
26335     try
26336     {
26337         if (element && element.id)
26338             return '//*[@id="' + element.id + '"]';
26339         else
26340             return this.getElementTreeXPath(element);
26341     }
26342     catch(E)
26343     {
26344         // xxxpedro: trying to detect the mysterious error:
26345         // Security error" code: "1000
26346         //debugger;
26347     }
26348 };
26349
26350 this.getElementTreeXPath = function(element)
26351 {
26352     var paths = [];
26353
26354     for (; element && element.nodeType == 1; element = element.parentNode)
26355     {
26356         var index = 0;
26357         var nodeName = element.nodeName;
26358
26359         for (var sibling = element.previousSibling; sibling; sibling = sibling.previousSibling)
26360         {
26361             if (sibling.nodeType != 1) continue;
26362
26363             if (sibling.nodeName == nodeName)
26364                 ++index;
26365         }
26366
26367         var tagName = element.nodeName.toLowerCase();
26368         var pathIndex = (index ? "[" + (index+1) + "]" : "");
26369         paths.splice(0, 0, tagName + pathIndex);
26370     }
26371
26372     return paths.length ? "/" + paths.join("/") : null;
26373 };
26374
26375 this.getElementsByXPath = function(doc, xpath)
26376 {
26377     var nodes = [];
26378
26379     try {
26380         var result = doc.evaluate(xpath, doc, null, XPathResult.ANY_TYPE, null);
26381         for (var item = result.iterateNext(); item; item = result.iterateNext())
26382             nodes.push(item);
26383     }
26384     catch (exc)
26385     {
26386         // Invalid xpath expressions make their way here sometimes.  If that happens,
26387         // we still want to return an empty set without an exception.
26388     }
26389
26390     return nodes;
26391 };
26392
26393 this.getRuleMatchingElements = function(rule, doc)
26394 {
26395     var css = rule.selectorText;
26396     var xpath = this.cssToXPath(css);
26397     return this.getElementsByXPath(doc, xpath);
26398 };
26399
26400
26401 }).call(FBL);
26402
26403
26404
26405
26406 FBL.ns(function() { with (FBL) {
26407
26408 // ************************************************************************************************
26409 // ************************************************************************************************
26410 // ************************************************************************************************
26411 // ************************************************************************************************
26412 // ************************************************************************************************
26413
26414 var toCamelCase = function toCamelCase(s)
26415 {
26416     return s.replace(reSelectorCase, toCamelCaseReplaceFn);
26417 };
26418
26419 var toSelectorCase = function toSelectorCase(s)
26420 {
26421   return s.replace(reCamelCase, "-$1").toLowerCase();
26422
26423 };
26424
26425 var reCamelCase = /([A-Z])/g;
26426 var reSelectorCase = /\-(.)/g;
26427 var toCamelCaseReplaceFn = function toCamelCaseReplaceFn(m,g)
26428 {
26429     return g.toUpperCase();
26430 };
26431
26432 // ************************************************************************************************
26433
26434 var ElementCache = Firebug.Lite.Cache.Element;
26435 var StyleSheetCache = Firebug.Lite.Cache.StyleSheet;
26436
26437 // ************************************************************************************************
26438 // ************************************************************************************************
26439 // ************************************************************************************************
26440 // ************************************************************************************************
26441 // ************************************************************************************************
26442 // ************************************************************************************************
26443
26444
26445 // ************************************************************************************************
26446 // Constants
26447
26448 //const Cc = Components.classes;
26449 //const Ci = Components.interfaces;
26450 //const nsIDOMCSSStyleRule = Ci.nsIDOMCSSStyleRule;
26451 //const nsIInterfaceRequestor = Ci.nsIInterfaceRequestor;
26452 //const nsISelectionDisplay = Ci.nsISelectionDisplay;
26453 //const nsISelectionController = Ci.nsISelectionController;
26454
26455 // See: http://mxr.mozilla.org/mozilla1.9.2/source/content/events/public/nsIEventStateManager.h#153
26456 //const STATE_ACTIVE  = 0x01;
26457 //const STATE_FOCUS   = 0x02;
26458 //const STATE_HOVER   = 0x04;
26459
26460 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26461 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26462 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26463 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26464 Firebug.SourceBoxPanel = Firebug.Panel;
26465
26466 var reSelectorTag = /(^|\s)(?:\w+)/g;
26467
26468 var domUtils = null;
26469
26470 var textContent = isIE ? "innerText" : "textContent";
26471 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26472 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26473 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26474 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26475
26476 var CSSDomplateBase = {
26477     isEditable: function(rule)
26478     {
26479         return !rule.isSystemSheet;
26480     },
26481     isSelectorEditable: function(rule)
26482     {
26483         return rule.isSelectorEditable && this.isEditable(rule);
26484     }
26485 };
26486
26487 var CSSPropTag = domplate(CSSDomplateBase, {
26488     tag: DIV({"class": "cssProp focusRow", $disabledStyle: "$prop.disabled",
26489           $editGroup: "$rule|isEditable",
26490           $cssOverridden: "$prop.overridden", role : "option"},
26491         A({"class": "cssPropDisable"}, "&nbsp;&nbsp;"),
26492         SPAN({"class": "cssPropName", $editable: "$rule|isEditable"}, "$prop.name"),
26493         SPAN({"class": "cssColon"}, ":"),
26494         SPAN({"class": "cssPropValue", $editable: "$rule|isEditable"}, "$prop.value$prop.important"),
26495         SPAN({"class": "cssSemi"}, ";")
26496     )
26497 });
26498
26499 var CSSRuleTag =
26500     TAG("$rule.tag", {rule: "$rule"});
26501
26502 var CSSImportRuleTag = domplate({
26503     tag: DIV({"class": "cssRule insertInto focusRow importRule", _repObject: "$rule.rule"},
26504         "@import &quot;",
26505         A({"class": "objectLink", _repObject: "$rule.rule.styleSheet"}, "$rule.rule.href"),
26506         "&quot;;"
26507     )
26508 });
26509
26510 var CSSStyleRuleTag = domplate(CSSDomplateBase, {
26511     tag: DIV({"class": "cssRule insertInto",
26512             $cssEditableRule: "$rule|isEditable",
26513             $editGroup: "$rule|isSelectorEditable",
26514             _repObject: "$rule.rule",
26515             "ruleId": "$rule.id", role : 'presentation'},
26516         DIV({"class": "cssHead focusRow", role : 'listitem'},
26517             SPAN({"class": "cssSelector", $editable: "$rule|isSelectorEditable"}, "$rule.selector"), " {"
26518         ),
26519         DIV({role : 'group'},
26520             DIV({"class": "cssPropertyListBox", role : 'listbox'},
26521                 FOR("prop", "$rule.props",
26522                     TAG(CSSPropTag.tag, {rule: "$rule", prop: "$prop"})
26523                 )
26524             )
26525         ),
26526         DIV({"class": "editable insertBefore", role:"presentation"}, "}")
26527     )
26528 });
26529
26530 var reSplitCSS =  /(url\("?[^"\)]+?"?\))|(rgb\(.*?\))|(#[\dA-Fa-f]+)|(-?\d+(\.\d+)?(%|[a-z]{1,2})?)|([^,\s]+)|"(.*?)"/;
26531
26532 var reURL = /url\("?([^"\)]+)?"?\)/;
26533
26534 var reRepeat = /no-repeat|repeat-x|repeat-y|repeat/;
26535
26536 //const sothinkInstalled = !!$("swfcatcherKey_sidebar");
26537 var sothinkInstalled = false;
26538 var styleGroups =
26539 {
26540     text: [
26541         "font-family",
26542         "font-size",
26543         "font-weight",
26544         "font-style",
26545         "color",
26546         "text-transform",
26547         "text-decoration",
26548         "letter-spacing",
26549         "word-spacing",
26550         "line-height",
26551         "text-align",
26552         "vertical-align",
26553         "direction",
26554         "column-count",
26555         "column-gap",
26556         "column-width"
26557     ],
26558
26559     background: [
26560         "background-color",
26561         "background-image",
26562         "background-repeat",
26563         "background-position",
26564         "background-attachment",
26565         "opacity"
26566     ],
26567
26568     box: [
26569         "width",
26570         "height",
26571         "top",
26572         "right",
26573         "bottom",
26574         "left",
26575         "margin-top",
26576         "margin-right",
26577         "margin-bottom",
26578         "margin-left",
26579         "padding-top",
26580         "padding-right",
26581         "padding-bottom",
26582         "padding-left",
26583         "border-top-width",
26584         "border-right-width",
26585         "border-bottom-width",
26586         "border-left-width",
26587         "border-top-color",
26588         "border-right-color",
26589         "border-bottom-color",
26590         "border-left-color",
26591         "border-top-style",
26592         "border-right-style",
26593         "border-bottom-style",
26594         "border-left-style",
26595         "-moz-border-top-radius",
26596         "-moz-border-right-radius",
26597         "-moz-border-bottom-radius",
26598         "-moz-border-left-radius",
26599         "outline-top-width",
26600         "outline-right-width",
26601         "outline-bottom-width",
26602         "outline-left-width",
26603         "outline-top-color",
26604         "outline-right-color",
26605         "outline-bottom-color",
26606         "outline-left-color",
26607         "outline-top-style",
26608         "outline-right-style",
26609         "outline-bottom-style",
26610         "outline-left-style"
26611     ],
26612
26613     layout: [
26614         "position",
26615         "display",
26616         "visibility",
26617         "z-index",
26618         "overflow-x",  // http://www.w3.org/TR/2002/WD-css3-box-20021024/#overflow
26619         "overflow-y",
26620         "overflow-clip",
26621         "white-space",
26622         "clip",
26623         "float",
26624         "clear",
26625         "-moz-box-sizing"
26626     ],
26627
26628     other: [
26629         "cursor",
26630         "list-style-image",
26631         "list-style-position",
26632         "list-style-type",
26633         "marker-offset",
26634         "user-focus",
26635         "user-select",
26636         "user-modify",
26637         "user-input"
26638     ]
26639 };
26640
26641 var styleGroupTitles =
26642 {
26643     text: "Text",
26644     background: "Background",
26645     box: "Box Model",
26646     layout: "Layout",
26647     other: "Other"
26648 };
26649
26650 Firebug.CSSModule = extend(Firebug.Module,
26651 {
26652     freeEdit: function(styleSheet, value)
26653     {
26654         if (!styleSheet.editStyleSheet)
26655         {
26656             var ownerNode = getStyleSheetOwnerNode(styleSheet);
26657             styleSheet.disabled = true;
26658
26659             var url = CCSV("@mozilla.org/network/standard-url;1", Components.interfaces.nsIURL);
26660             url.spec = styleSheet.href;
26661
26662             var editStyleSheet = ownerNode.ownerDocument.createElementNS(
26663                 "http://www.w3.org/1999/xhtml",
26664                 "style");
26665             unwrapObject(editStyleSheet).firebugIgnore = true;
26666             editStyleSheet.setAttribute("type", "text/css");
26667             editStyleSheet.setAttributeNS(
26668                 "http://www.w3.org/XML/1998/namespace",
26669                 "base",
26670                 url.directory);
26671             if (ownerNode.hasAttribute("media"))
26672             {
26673               editStyleSheet.setAttribute("media", ownerNode.getAttribute("media"));
26674             }
26675
26676             // Insert the edited stylesheet directly after the old one to ensure the styles
26677             // cascade properly.
26678             ownerNode.parentNode.insertBefore(editStyleSheet, ownerNode.nextSibling);
26679
26680             styleSheet.editStyleSheet = editStyleSheet;
26681         }
26682
26683         styleSheet.editStyleSheet.innerHTML = value;
26684         if (FBTrace.DBG_CSS)
26685             FBTrace.sysout("css.saveEdit styleSheet.href:"+styleSheet.href+" got innerHTML:"+value+"\n");
26686
26687         dispatch(this.fbListeners, "onCSSFreeEdit", [styleSheet, value]);
26688     },
26689
26690     insertRule: function(styleSheet, cssText, ruleIndex)
26691     {
26692         if (FBTrace.DBG_CSS) FBTrace.sysout("Insert: " + ruleIndex + " " + cssText);
26693         var insertIndex = styleSheet.insertRule(cssText, ruleIndex);
26694
26695         dispatch(this.fbListeners, "onCSSInsertRule", [styleSheet, cssText, ruleIndex]);
26696
26697         return insertIndex;
26698     },
26699
26700     deleteRule: function(styleSheet, ruleIndex)
26701     {
26702         if (FBTrace.DBG_CSS) FBTrace.sysout("deleteRule: " + ruleIndex + " " + styleSheet.cssRules.length, styleSheet.cssRules);
26703         dispatch(this.fbListeners, "onCSSDeleteRule", [styleSheet, ruleIndex]);
26704
26705         styleSheet.deleteRule(ruleIndex);
26706     },
26707
26708     setProperty: function(rule, propName, propValue, propPriority)
26709     {
26710         var style = rule.style || rule;
26711
26712         // Record the original CSS text for the inline case so we can reconstruct at a later
26713         // point for diffing purposes
26714         var baseText = style.cssText;
26715
26716         // good browsers
26717         if (style.getPropertyValue)
26718         {
26719             var prevValue = style.getPropertyValue(propName);
26720             var prevPriority = style.getPropertyPriority(propName);
26721
26722             // XXXjoe Gecko bug workaround: Just changing priority doesn't have any effect
26723             // unless we remove the property first
26724             style.removeProperty(propName);
26725
26726             style.setProperty(propName, propValue, propPriority);
26727         }
26728         // sad browsers
26729         else
26730         {
26731             // TODO: xxxpedro parse CSS rule to find property priority in IE?
26732             //console.log(propName, propValue);
26733             style[toCamelCase(propName)] = propValue;
26734         }
26735
26736         if (propName) {
26737             dispatch(this.fbListeners, "onCSSSetProperty", [style, propName, propValue, propPriority, prevValue, prevPriority, rule, baseText]);
26738         }
26739     },
26740
26741     removeProperty: function(rule, propName, parent)
26742     {
26743         var style = rule.style || rule;
26744
26745         // Record the original CSS text for the inline case so we can reconstruct at a later
26746         // point for diffing purposes
26747         var baseText = style.cssText;
26748
26749         if (style.getPropertyValue)
26750         {
26751
26752             var prevValue = style.getPropertyValue(propName);
26753             var prevPriority = style.getPropertyPriority(propName);
26754
26755             style.removeProperty(propName);
26756         }
26757         else
26758         {
26759             style[toCamelCase(propName)] = "";
26760         }
26761
26762         if (propName) {
26763             dispatch(this.fbListeners, "onCSSRemoveProperty", [style, propName, prevValue, prevPriority, rule, baseText]);
26764         }
26765     }/*,
26766
26767     cleanupSheets: function(doc, context)
26768     {
26769         // Due to the manner in which the layout engine handles multiple
26770         // references to the same sheet we need to kick it a little bit.
26771         // The injecting a simple stylesheet then removing it will force
26772         // Firefox to regenerate it's CSS hierarchy.
26773         //
26774         // WARN: This behavior was determined anecdotally.
26775         // See http://code.google.com/p/fbug/issues/detail?id=2440
26776         var style = doc.createElementNS("http://www.w3.org/1999/xhtml", "style");
26777         style.setAttribute("charset","utf-8");
26778         unwrapObject(style).firebugIgnore = true;
26779         style.setAttribute("type", "text/css");
26780         style.innerHTML = "#fbIgnoreStyleDO_NOT_USE {}";
26781         addStyleSheet(doc, style);
26782         style.parentNode.removeChild(style);
26783
26784         // https://bugzilla.mozilla.org/show_bug.cgi?id=500365
26785         // This voodoo touches each style sheet to force some Firefox internal change to allow edits.
26786         var styleSheets = getAllStyleSheets(context);
26787         for(var i = 0; i < styleSheets.length; i++)
26788         {
26789             try
26790             {
26791                 var rules = styleSheets[i].cssRules;
26792                 if (rules.length > 0)
26793                     var touch = rules[0];
26794                 if (FBTrace.DBG_CSS && touch)
26795                     FBTrace.sysout("css.show() touch "+typeof(touch)+" in "+(styleSheets[i].href?styleSheets[i].href:context.getName()));
26796             }
26797             catch(e)
26798             {
26799                 if (FBTrace.DBG_ERRORS)
26800                     FBTrace.sysout("css.show: sheet.cssRules FAILS for "+(styleSheets[i]?styleSheets[i].href:"null sheet")+e, e);
26801             }
26802         }
26803     },
26804     cleanupSheetHandler: function(event, context)
26805     {
26806         var target = event.target || event.srcElement,
26807             tagName = (target.tagName || "").toLowerCase();
26808         if (tagName == "link")
26809         {
26810             this.cleanupSheets(target.ownerDocument, context);
26811         }
26812     },
26813     watchWindow: function(context, win)
26814     {
26815         var cleanupSheets = bind(this.cleanupSheets, this),
26816             cleanupSheetHandler = bind(this.cleanupSheetHandler, this, context),
26817             doc = win.document;
26818
26819         //doc.addEventListener("DOMAttrModified", cleanupSheetHandler, false);
26820         //doc.addEventListener("DOMNodeInserted", cleanupSheetHandler, false);
26821     },
26822     loadedContext: function(context)
26823     {
26824         var self = this;
26825         iterateWindows(context.browser.contentWindow, function(subwin)
26826         {
26827             self.cleanupSheets(subwin.document, context);
26828         });
26829     }
26830     /**/
26831 });
26832
26833 // ************************************************************************************************
26834
26835 Firebug.CSSStyleSheetPanel = function() {};
26836
26837 Firebug.CSSStyleSheetPanel.prototype = extend(Firebug.SourceBoxPanel,
26838 {
26839     template: domplate(
26840     {
26841         tag:
26842             DIV({"class": "cssSheet insertInto a11yCSSView"},
26843                 FOR("rule", "$rules",
26844                     CSSRuleTag
26845                 ),
26846                 DIV({"class": "cssSheet editable insertBefore"}, "")
26847                 )
26848     }),
26849
26850     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26851
26852     refresh: function()
26853     {
26854         if (this.location)
26855             this.updateLocation(this.location);
26856         else if (this.selection)
26857             this.updateSelection(this.selection);
26858     },
26859
26860     toggleEditing: function()
26861     {
26862         if (!this.stylesheetEditor)
26863             this.stylesheetEditor = new StyleSheetEditor(this.document);
26864
26865         if (this.editing)
26866             Firebug.Editor.stopEditing();
26867         else
26868         {
26869             if (!this.location)
26870                 return;
26871
26872             var styleSheet = this.location.editStyleSheet
26873                 ? this.location.editStyleSheet.sheet
26874                 : this.location;
26875
26876             var css = getStyleSheetCSS(styleSheet, this.context);
26877             //var topmost = getTopmostRuleLine(this.panelNode);
26878
26879             this.stylesheetEditor.styleSheet = this.location;
26880             Firebug.Editor.startEditing(this.panelNode, css, this.stylesheetEditor);
26881             //this.stylesheetEditor.scrollToLine(topmost.line, topmost.offset);
26882         }
26883     },
26884
26885     getStylesheetURL: function(rule)
26886     {
26887         if (this.location.href)
26888             return this.location.href;
26889         else
26890             return this.context.window.location.href;
26891     },
26892
26893     getRuleByLine: function(styleSheet, line)
26894     {
26895         if (!domUtils)
26896             return null;
26897
26898         var cssRules = styleSheet.cssRules;
26899         for (var i = 0; i < cssRules.length; ++i)
26900         {
26901             var rule = cssRules[i];
26902             if (rule instanceof CSSStyleRule)
26903             {
26904                 var ruleLine = domUtils.getRuleLine(rule);
26905                 if (ruleLine >= line)
26906                     return rule;
26907             }
26908         }
26909     },
26910
26911     highlightRule: function(rule)
26912     {
26913         var ruleElement = Firebug.getElementByRepObject(this.panelNode.firstChild, rule);
26914         if (ruleElement)
26915         {
26916             scrollIntoCenterView(ruleElement, this.panelNode);
26917             setClassTimed(ruleElement, "jumpHighlight", this.context);
26918         }
26919     },
26920
26921     getStyleSheetRules: function(context, styleSheet)
26922     {
26923         var isSystemSheet = isSystemStyleSheet(styleSheet);
26924
26925         function appendRules(cssRules)
26926         {
26927             for (var i = 0; i < cssRules.length; ++i)
26928             {
26929                 var rule = cssRules[i];
26930
26931                 // TODO: xxxpedro opera instanceof stylesheet remove the following comments when
26932                 // the issue with opera and style sheet Classes has been solved.
26933
26934                 //if (rule instanceof CSSStyleRule)
26935                 if (instanceOf(rule, "CSSStyleRule"))
26936                 {
26937                     var props = this.getRuleProperties(context, rule);
26938                     //var line = domUtils.getRuleLine(rule);
26939                     var line = null;
26940
26941                     var selector = rule.selectorText;
26942
26943                     if (isIE)
26944                     {
26945                         selector = selector.replace(reSelectorTag,
26946                                 function(s){return s.toLowerCase();});
26947                     }
26948
26949                     var ruleId = rule.selectorText+"/"+line;
26950                     rules.push({tag: CSSStyleRuleTag.tag, rule: rule, id: ruleId,
26951                                 selector: selector, props: props,
26952                                 isSystemSheet: isSystemSheet,
26953                                 isSelectorEditable: true});
26954                 }
26955                 //else if (rule instanceof CSSImportRule)
26956                 else if (instanceOf(rule, "CSSImportRule"))
26957                     rules.push({tag: CSSImportRuleTag.tag, rule: rule});
26958                 //else if (rule instanceof CSSMediaRule)
26959                 else if (instanceOf(rule, "CSSMediaRule"))
26960                     appendRules.apply(this, [rule.cssRules]);
26961                 else
26962                 {
26963                     if (FBTrace.DBG_ERRORS || FBTrace.DBG_CSS)
26964                         FBTrace.sysout("css getStyleSheetRules failed to classify a rule ", rule);
26965                 }
26966             }
26967         }
26968
26969         var rules = [];
26970         appendRules.apply(this, [styleSheet.cssRules || styleSheet.rules]);
26971         return rules;
26972     },
26973
26974     parseCSSProps: function(style, inheritMode)
26975     {
26976         var props = [];
26977
26978         if (Firebug.expandShorthandProps)
26979         {
26980             var count = style.length-1,
26981                 index = style.length;
26982             while (index--)
26983             {
26984                 var propName = style.item(count - index);
26985                 this.addProperty(propName, style.getPropertyValue(propName), !!style.getPropertyPriority(propName), false, inheritMode, props);
26986             }
26987         }
26988         else
26989         {
26990             var lines = style.cssText.match(/(?:[^;\(]*(?:\([^\)]*?\))?[^;\(]*)*;?/g);
26991             var propRE = /\s*([^:\s]*)\s*:\s*(.*?)\s*(! important)?;?$/;
26992             var line,i=0;
26993             // TODO: xxxpedro port to firebug: variable leaked into global namespace
26994             var m;
26995
26996             while(line=lines[i++]){
26997                 m = propRE.exec(line);
26998                 if(!m)
26999                     continue;
27000                 //var name = m[1], value = m[2], important = !!m[3];
27001                 if (m[2])
27002                     this.addProperty(m[1], m[2], !!m[3], false, inheritMode, props);
27003             };
27004         }
27005
27006         return props;
27007     },
27008
27009     getRuleProperties: function(context, rule, inheritMode)
27010     {
27011         var props = this.parseCSSProps(rule.style, inheritMode);
27012
27013         // TODO: xxxpedro port to firebug: variable leaked into global namespace
27014         //var line = domUtils.getRuleLine(rule);
27015         var line;
27016         var ruleId = rule.selectorText+"/"+line;
27017         this.addOldProperties(context, ruleId, inheritMode, props);
27018         sortProperties(props);
27019
27020         return props;
27021     },
27022
27023     addOldProperties: function(context, ruleId, inheritMode, props)
27024     {
27025         if (context.selectorMap && context.selectorMap.hasOwnProperty(ruleId) )
27026         {
27027             var moreProps = context.selectorMap[ruleId];
27028             for (var i = 0; i < moreProps.length; ++i)
27029             {
27030                 var prop = moreProps[i];
27031                 this.addProperty(prop.name, prop.value, prop.important, true, inheritMode, props);
27032             }
27033         }
27034     },
27035
27036     addProperty: function(name, value, important, disabled, inheritMode, props)
27037     {
27038         name = name.toLowerCase();
27039
27040         if (inheritMode && !inheritedStyleNames[name])
27041             return;
27042
27043         name = this.translateName(name, value);
27044         if (name)
27045         {
27046             value = stripUnits(rgbToHex(value));
27047             important = important ? " !important" : "";
27048
27049             var prop = {name: name, value: value, important: important, disabled: disabled};
27050             props.push(prop);
27051         }
27052     },
27053
27054     translateName: function(name, value)
27055     {
27056         // Don't show these proprietary Mozilla properties
27057         if ((value == "-moz-initial"
27058             && (name == "-moz-background-clip" || name == "-moz-background-origin"
27059                 || name == "-moz-background-inline-policy"))
27060         || (value == "physical"
27061             && (name == "margin-left-ltr-source" || name == "margin-left-rtl-source"
27062                 || name == "margin-right-ltr-source" || name == "margin-right-rtl-source"))
27063         || (value == "physical"
27064             && (name == "padding-left-ltr-source" || name == "padding-left-rtl-source"
27065                 || name == "padding-right-ltr-source" || name == "padding-right-rtl-source")))
27066             return null;
27067
27068         // Translate these back to the form the user probably expects
27069         if (name == "margin-left-value")
27070             return "margin-left";
27071         else if (name == "margin-right-value")
27072             return "margin-right";
27073         else if (name == "margin-top-value")
27074             return "margin-top";
27075         else if (name == "margin-bottom-value")
27076             return "margin-bottom";
27077         else if (name == "padding-left-value")
27078             return "padding-left";
27079         else if (name == "padding-right-value")
27080             return "padding-right";
27081         else if (name == "padding-top-value")
27082             return "padding-top";
27083         else if (name == "padding-bottom-value")
27084             return "padding-bottom";
27085         // XXXjoe What about border!
27086         else
27087             return name;
27088     },
27089
27090     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
27091
27092     editElementStyle: function()
27093     {
27094         ///var rulesBox = this.panelNode.getElementsByClassName("cssElementRuleContainer")[0];
27095         var rulesBox = $$(".cssElementRuleContainer", this.panelNode)[0];
27096         var styleRuleBox = rulesBox && Firebug.getElementByRepObject(rulesBox, this.selection);
27097         if (!styleRuleBox)
27098         {
27099             var rule = {rule: this.selection, inherited: false, selector: "element.style", props: []};
27100             if (!rulesBox)
27101             {
27102                 // The element did not have any displayed styles. We need to create the whole tree and remove
27103                 // the no styles message
27104                 styleRuleBox = this.template.cascadedTag.replace({
27105                     rules: [rule], inherited: [], inheritLabel: "Inherited from" // $STR("InheritedFrom")
27106                 }, this.panelNode);
27107
27108                 ///styleRuleBox = styleRuleBox.getElementsByClassName("cssElementRuleContainer")[0];
27109                 styleRuleBox = $$(".cssElementRuleContainer", styleRuleBox)[0];
27110             }
27111             else
27112                 styleRuleBox = this.template.ruleTag.insertBefore({rule: rule}, rulesBox);
27113
27114             ///styleRuleBox = styleRuleBox.getElementsByClassName("insertInto")[0];
27115             styleRuleBox = $$(".insertInto", styleRuleBox)[0];
27116         }
27117
27118         Firebug.Editor.insertRowForObject(styleRuleBox);
27119     },
27120
27121     insertPropertyRow: function(row)
27122     {
27123         Firebug.Editor.insertRowForObject(row);
27124     },
27125
27126     insertRule: function(row)
27127     {
27128         var location = getAncestorByClass(row, "cssRule");
27129         if (!location)
27130         {
27131             location = getChildByClass(this.panelNode, "cssSheet");
27132             Firebug.Editor.insertRowForObject(location);
27133         }
27134         else
27135         {
27136             Firebug.Editor.insertRow(location, "before");
27137         }
27138     },
27139
27140     editPropertyRow: function(row)
27141     {
27142         var propValueBox = getChildByClass(row, "cssPropValue");
27143         Firebug.Editor.startEditing(propValueBox);
27144     },
27145
27146     deletePropertyRow: function(row)
27147     {
27148         var rule = Firebug.getRepObject(row);
27149         var propName = getChildByClass(row, "cssPropName")[textContent];
27150         Firebug.CSSModule.removeProperty(rule, propName);
27151
27152         // Remove the property from the selector map, if it was disabled
27153         var ruleId = Firebug.getRepNode(row).getAttribute("ruleId");
27154         if ( this.context.selectorMap && this.context.selectorMap.hasOwnProperty(ruleId) )
27155         {
27156             var map = this.context.selectorMap[ruleId];
27157             for (var i = 0; i < map.length; ++i)
27158             {
27159                 if (map[i].name == propName)
27160                 {
27161                     map.splice(i, 1);
27162                     break;
27163                 }
27164             }
27165         }
27166         if (this.name == "stylesheet")
27167             dispatch([Firebug.A11yModel], 'onInlineEditorClose', [this, row.firstChild, true]);
27168         row.parentNode.removeChild(row);
27169
27170         this.markChange(this.name == "stylesheet");
27171     },
27172
27173     disablePropertyRow: function(row)
27174     {
27175         toggleClass(row, "disabledStyle");
27176
27177         var rule = Firebug.getRepObject(row);
27178         var propName = getChildByClass(row, "cssPropName")[textContent];
27179
27180         if (!this.context.selectorMap)
27181             this.context.selectorMap = {};
27182
27183         // XXXjoe Generate unique key for elements too
27184         var ruleId = Firebug.getRepNode(row).getAttribute("ruleId");
27185         if (!(this.context.selectorMap.hasOwnProperty(ruleId)))
27186             this.context.selectorMap[ruleId] = [];
27187
27188         var map = this.context.selectorMap[ruleId];
27189         var propValue = getChildByClass(row, "cssPropValue")[textContent];
27190         var parsedValue = parsePriority(propValue);
27191         if (hasClass(row, "disabledStyle"))
27192         {
27193             Firebug.CSSModule.removeProperty(rule, propName);
27194
27195             map.push({"name": propName, "value": parsedValue.value,
27196                 "important": parsedValue.priority});
27197         }
27198         else
27199         {
27200             Firebug.CSSModule.setProperty(rule, propName, parsedValue.value, parsedValue.priority);
27201
27202             var index = findPropByName(map, propName);
27203             map.splice(index, 1);
27204         }
27205
27206         this.markChange(this.name == "stylesheet");
27207     },
27208
27209     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
27210
27211     onMouseDown: function(event)
27212     {
27213         //console.log("onMouseDown", event.target || event.srcElement, event);
27214
27215         // xxxpedro adjusting coordinates because the panel isn't a window yet
27216         var offset = event.clientX - this.panelNode.parentNode.offsetLeft;
27217
27218         // XXjoe Hack to only allow clicking on the checkbox
27219         if (!isLeftClick(event) || offset > 20)
27220             return;
27221
27222         var target = event.target || event.srcElement;
27223         if (hasClass(target, "textEditor"))
27224             return;
27225
27226         var row = getAncestorByClass(target, "cssProp");
27227         if (row && hasClass(row, "editGroup"))
27228         {
27229             this.disablePropertyRow(row);
27230             cancelEvent(event);
27231         }
27232     },
27233
27234     onDoubleClick: function(event)
27235     {
27236         //console.log("onDoubleClick", event.target || event.srcElement, event);
27237
27238         // xxxpedro adjusting coordinates because the panel isn't a window yet
27239         var offset = event.clientX - this.panelNode.parentNode.offsetLeft;
27240
27241         if (!isLeftClick(event) || offset <= 20)
27242             return;
27243
27244         var target = event.target || event.srcElement;
27245
27246         //console.log("ok", target, hasClass(target, "textEditorInner"), !isLeftClick(event), offset <= 20);
27247
27248         // if the inline editor was clicked, don't insert a new rule
27249         if (hasClass(target, "textEditorInner"))
27250             return;
27251
27252         var row = getAncestorByClass(target, "cssRule");
27253         if (row && !getAncestorByClass(target, "cssPropName")
27254             && !getAncestorByClass(target, "cssPropValue"))
27255         {
27256             this.insertPropertyRow(row);
27257             cancelEvent(event);
27258         }
27259     },
27260
27261     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
27262     // extends Panel
27263
27264     name: "stylesheet",
27265     title: "CSS",
27266     parentPanel: null,
27267     searchable: true,
27268     dependents: ["css", "stylesheet", "dom", "domSide", "layout"],
27269
27270     options:
27271     {
27272         hasToolButtons: true
27273     },
27274
27275     create: function()
27276     {
27277         Firebug.Panel.create.apply(this, arguments);
27278
27279         this.onMouseDown = bind(this.onMouseDown, this);
27280         this.onDoubleClick = bind(this.onDoubleClick, this);
27281
27282         if (this.name == "stylesheet")
27283         {
27284             this.onChangeSelect = bind(this.onChangeSelect, this);
27285
27286             var doc = Firebug.browser.document;
27287             var selectNode = this.selectNode = createElement("select");
27288
27289             CssAnalyzer.processAllStyleSheets(doc, function(doc, styleSheet)
27290             {
27291                 var key = StyleSheetCache.key(styleSheet);
27292                 var fileName = getFileName(styleSheet.href) || getFileName(doc.location.href);
27293                 var option = createElement("option", {value: key});
27294
27295                 option.appendChild(Firebug.chrome.document.createTextNode(fileName));
27296                 selectNode.appendChild(option);
27297             });
27298
27299             this.toolButtonsNode.appendChild(selectNode);
27300         }
27301         /**/
27302     },
27303
27304     onChangeSelect: function(event)
27305     {
27306         event = event || window.event;
27307         var target = event.srcElement || event.currentTarget;
27308         var key = target.value;
27309         var styleSheet = StyleSheetCache.get(key);
27310
27311         this.updateLocation(styleSheet);
27312     },
27313
27314     initialize: function()
27315     {
27316         Firebug.Panel.initialize.apply(this, arguments);
27317
27318         //if (!domUtils)
27319         //{
27320         //    try {
27321         //        domUtils = CCSV("@mozilla.org/inspector/dom-utils;1", "inIDOMUtils");
27322         //    } catch (exc) {
27323         //        if (FBTrace.DBG_ERRORS)
27324         //            FBTrace.sysout("@mozilla.org/inspector/dom-utils;1 FAILED to load: "+exc, exc);
27325         //    }
27326         //}
27327
27328         //TODO: xxxpedro
27329         this.context = Firebug.chrome; // TODO: xxxpedro css2
27330         this.document = Firebug.chrome.document; // TODO: xxxpedro css2
27331
27332         this.initializeNode();
27333
27334         if (this.name == "stylesheet")
27335         {
27336             var styleSheets = Firebug.browser.document.styleSheets;
27337
27338             if (styleSheets.length > 0)
27339             {
27340                 addEvent(this.selectNode, "change", this.onChangeSelect);
27341
27342                 this.updateLocation(styleSheets[0]);
27343             }
27344         }
27345
27346         //Firebug.SourceBoxPanel.initialize.apply(this, arguments);
27347     },
27348
27349     shutdown: function()
27350     {
27351         // must destroy the editor when we leave the panel to avoid problems (Issue 2981)
27352         Firebug.Editor.stopEditing();
27353
27354         if (this.name == "stylesheet")
27355         {
27356             removeEvent(this.selectNode, "change", this.onChangeSelect);
27357         }
27358
27359         this.destroyNode();
27360
27361         Firebug.Panel.shutdown.apply(this, arguments);
27362     },
27363
27364     destroy: function(state)
27365     {
27366         //state.scrollTop = this.panelNode.scrollTop ? this.panelNode.scrollTop : this.lastScrollTop;
27367
27368         //persistObjects(this, state);
27369
27370         // xxxpedro we are stopping the editor in the shutdown method already
27371         //Firebug.Editor.stopEditing();
27372         Firebug.Panel.destroy.apply(this, arguments);
27373     },
27374
27375     initializeNode: function(oldPanelNode)
27376     {
27377         addEvent(this.panelNode, "mousedown", this.onMouseDown);
27378         addEvent(this.panelNode, "dblclick", this.onDoubleClick);
27379         //Firebug.SourceBoxPanel.initializeNode.apply(this, arguments);
27380         //dispatch([Firebug.A11yModel], 'onInitializeNode', [this, 'css']);
27381     },
27382
27383     destroyNode: function()
27384     {
27385         removeEvent(this.panelNode, "mousedown", this.onMouseDown);
27386         removeEvent(this.panelNode, "dblclick", this.onDoubleClick);
27387         //Firebug.SourceBoxPanel.destroyNode.apply(this, arguments);
27388         //dispatch([Firebug.A11yModel], 'onDestroyNode', [this, 'css']);
27389     },
27390
27391     ishow: function(state)
27392     {
27393         Firebug.Inspector.stopInspecting(true);
27394
27395         this.showToolbarButtons("fbCSSButtons", true);
27396
27397         if (this.context.loaded && !this.location) // wait for loadedContext to restore the panel
27398         {
27399             restoreObjects(this, state);
27400
27401             if (!this.location)
27402                 this.location = this.getDefaultLocation();
27403
27404             if (state && state.scrollTop)
27405                 this.panelNode.scrollTop = state.scrollTop;
27406         }
27407     },
27408
27409     ihide: function()
27410     {
27411         this.showToolbarButtons("fbCSSButtons", false);
27412
27413         this.lastScrollTop = this.panelNode.scrollTop;
27414     },
27415
27416     supportsObject: function(object)
27417     {
27418         if (object instanceof CSSStyleSheet)
27419             return 1;
27420         else if (object instanceof CSSStyleRule)
27421             return 2;
27422         else if (object instanceof CSSStyleDeclaration)
27423             return 2;
27424         else if (object instanceof SourceLink && object.type == "css" && reCSS.test(object.href))
27425             return 2;
27426         else
27427             return 0;
27428     },
27429
27430     updateLocation: function(styleSheet)
27431     {
27432         if (!styleSheet)
27433             return;
27434         if (styleSheet.editStyleSheet)
27435             styleSheet = styleSheet.editStyleSheet.sheet;
27436
27437         // if it is a restricted stylesheet, show the warning message and abort the update process
27438         if (styleSheet.restricted)
27439         {
27440             FirebugReps.Warning.tag.replace({object: "AccessRestricted"}, this.panelNode);
27441
27442             // TODO: xxxpedro remove when there the external resource problem is fixed
27443             CssAnalyzer.externalStyleSheetWarning.tag.append({
27444                 object: "The stylesheet could not be loaded due to access restrictions. ",
27445                 link: "more...",
27446                 href: "http://getfirebug.com/wiki/index.php/Firebug_Lite_FAQ#I_keep_seeing_.22Access_to_restricted_URI_denied.22"
27447             }, this.panelNode);
27448
27449             return;
27450         }
27451
27452         var rules = this.getStyleSheetRules(this.context, styleSheet);
27453
27454         var result;
27455         if (rules.length)
27456             // FIXME xxxpedro chromenew this is making iPad's Safari to crash
27457             result = this.template.tag.replace({rules: rules}, this.panelNode);
27458         else
27459             result = FirebugReps.Warning.tag.replace({object: "EmptyStyleSheet"}, this.panelNode);
27460
27461         // TODO: xxxpedro need to fix showToolbarButtons function
27462         //this.showToolbarButtons("fbCSSButtons", !isSystemStyleSheet(this.location));
27463
27464         //dispatch([Firebug.A11yModel], 'onCSSRulesAdded', [this, this.panelNode]);
27465     },
27466
27467     updateSelection: function(object)
27468     {
27469         this.selection = null;
27470
27471         if (object instanceof CSSStyleDeclaration) {
27472             object = object.parentRule;
27473         }
27474
27475         if (object instanceof CSSStyleRule)
27476         {
27477             this.navigate(object.parentStyleSheet);
27478             this.highlightRule(object);
27479         }
27480         else if (object instanceof CSSStyleSheet)
27481         {
27482             this.navigate(object);
27483         }
27484         else if (object instanceof SourceLink)
27485         {
27486             try
27487             {
27488                 var sourceLink = object;
27489
27490                 var sourceFile = getSourceFileByHref(sourceLink.href, this.context);
27491                 if (sourceFile)
27492                 {
27493                     clearNode(this.panelNode);  // replace rendered stylesheets
27494                     this.showSourceFile(sourceFile);
27495
27496                     var lineNo = object.line;
27497                     if (lineNo)
27498                         this.scrollToLine(lineNo, this.jumpHighlightFactory(lineNo, this.context));
27499                 }
27500                 else // XXXjjb we should not be taking this path
27501                 {
27502                     var stylesheet = getStyleSheetByHref(sourceLink.href, this.context);
27503                     if (stylesheet)
27504                         this.navigate(stylesheet);
27505                     else
27506                     {
27507                         if (FBTrace.DBG_CSS)
27508                             FBTrace.sysout("css.updateSelection no sourceFile for "+sourceLink.href, sourceLink);
27509                     }
27510                 }
27511             }
27512             catch(exc) {
27513                 if (FBTrace.DBG_CSS)
27514                     FBTrace.sysout("css.upDateSelection FAILS "+exc, exc);
27515             }
27516         }
27517     },
27518
27519     updateOption: function(name, value)
27520     {
27521         if (name == "expandShorthandProps")
27522             this.refresh();
27523     },
27524
27525     getLocationList: function()
27526     {
27527         var styleSheets = getAllStyleSheets(this.context);
27528         return styleSheets;
27529     },
27530
27531     getOptionsMenuItems: function()
27532     {
27533         return [
27534             {label: "Expand Shorthand Properties", type: "checkbox", checked: Firebug.expandShorthandProps,
27535                     command: bindFixed(Firebug.togglePref, Firebug, "expandShorthandProps") },
27536             "-",
27537             {label: "Refresh", command: bind(this.refresh, this) }
27538         ];
27539     },
27540
27541     getContextMenuItems: function(style, target)
27542     {
27543         var items = [];
27544
27545         if (this.infoTipType == "color")
27546         {
27547             items.push(
27548                 {label: "CopyColor",
27549                     command: bindFixed(copyToClipboard, FBL, this.infoTipObject) }
27550             );
27551         }
27552         else if (this.infoTipType == "image")
27553         {
27554             items.push(
27555                 {label: "CopyImageLocation",
27556                     command: bindFixed(copyToClipboard, FBL, this.infoTipObject) },
27557                 {label: "OpenImageInNewTab",
27558                     command: bindFixed(openNewTab, FBL, this.infoTipObject) }
27559             );
27560         }
27561
27562         ///if (this.selection instanceof Element)
27563         if (isElement(this.selection))
27564         {
27565             items.push(
27566                 //"-",
27567                 {label: "EditStyle",
27568                     command: bindFixed(this.editElementStyle, this) }
27569             );
27570         }
27571         else if (!isSystemStyleSheet(this.selection))
27572         {
27573             items.push(
27574                     //"-",
27575                     {label: "NewRule",
27576                         command: bindFixed(this.insertRule, this, target) }
27577                 );
27578         }
27579
27580         var cssRule = getAncestorByClass(target, "cssRule");
27581         if (cssRule && hasClass(cssRule, "cssEditableRule"))
27582         {
27583             items.push(
27584                 "-",
27585                 {label: "NewProp",
27586                     command: bindFixed(this.insertPropertyRow, this, target) }
27587             );
27588
27589             var propRow = getAncestorByClass(target, "cssProp");
27590             if (propRow)
27591             {
27592                 var propName = getChildByClass(propRow, "cssPropName")[textContent];
27593                 var isDisabled = hasClass(propRow, "disabledStyle");
27594
27595                 items.push(
27596                     {label: $STRF("EditProp", [propName]), nol10n: true,
27597                         command: bindFixed(this.editPropertyRow, this, propRow) },
27598                     {label: $STRF("DeleteProp", [propName]), nol10n: true,
27599                         command: bindFixed(this.deletePropertyRow, this, propRow) },
27600                     {label: $STRF("DisableProp", [propName]), nol10n: true,
27601                         type: "checkbox", checked: isDisabled,
27602                         command: bindFixed(this.disablePropertyRow, this, propRow) }
27603                 );
27604             }
27605         }
27606
27607         items.push(
27608             "-",
27609             {label: "Refresh", command: bind(this.refresh, this) }
27610         );
27611
27612         return items;
27613     },
27614
27615     browseObject: function(object)
27616     {
27617         if (this.infoTipType == "image")
27618         {
27619             openNewTab(this.infoTipObject);
27620             return true;
27621         }
27622     },
27623
27624     showInfoTip: function(infoTip, target, x, y)
27625     {
27626         var propValue = getAncestorByClass(target, "cssPropValue");
27627         if (propValue)
27628         {
27629             var offset = getClientOffset(propValue);
27630             var offsetX = x-offset.x;
27631
27632             var text = propValue[textContent];
27633             var charWidth = propValue.offsetWidth/text.length;
27634             var charOffset = Math.floor(offsetX/charWidth);
27635
27636             var cssValue = parseCSSValue(text, charOffset);
27637             if (cssValue)
27638             {
27639                 if (cssValue.value == this.infoTipValue)
27640                     return true;
27641
27642                 this.infoTipValue = cssValue.value;
27643
27644                 if (cssValue.type == "rgb" || (!cssValue.type && isColorKeyword(cssValue.value)))
27645                 {
27646                     this.infoTipType = "color";
27647                     this.infoTipObject = cssValue.value;
27648
27649                     return Firebug.InfoTip.populateColorInfoTip(infoTip, cssValue.value);
27650                 }
27651                 else if (cssValue.type == "url")
27652                 {
27653                     ///var propNameNode = target.parentNode.getElementsByClassName("cssPropName").item(0);
27654                     var propNameNode = getElementByClass(target.parentNode, "cssPropName");
27655                     if (propNameNode && isImageRule(propNameNode[textContent]))
27656                     {
27657                         var rule = Firebug.getRepObject(target);
27658                         var baseURL = this.getStylesheetURL(rule);
27659                         var relURL = parseURLValue(cssValue.value);
27660                         var absURL = isDataURL(relURL) ? relURL:absoluteURL(relURL, baseURL);
27661                         var repeat = parseRepeatValue(text);
27662
27663                         this.infoTipType = "image";
27664                         this.infoTipObject = absURL;
27665
27666                         return Firebug.InfoTip.populateImageInfoTip(infoTip, absURL, repeat);
27667                     }
27668                 }
27669             }
27670         }
27671
27672         delete this.infoTipType;
27673         delete this.infoTipValue;
27674         delete this.infoTipObject;
27675     },
27676
27677     getEditor: function(target, value)
27678     {
27679         if (target == this.panelNode
27680             || hasClass(target, "cssSelector") || hasClass(target, "cssRule")
27681             || hasClass(target, "cssSheet"))
27682         {
27683             if (!this.ruleEditor)
27684                 this.ruleEditor = new CSSRuleEditor(this.document);
27685
27686             return this.ruleEditor;
27687         }
27688         else
27689         {
27690             if (!this.editor)
27691                 this.editor = new CSSEditor(this.document);
27692
27693             return this.editor;
27694         }
27695     },
27696
27697     getDefaultLocation: function()
27698     {
27699         try
27700         {
27701             var styleSheets = this.context.window.document.styleSheets;
27702             if (styleSheets.length)
27703             {
27704                 var sheet = styleSheets[0];
27705                 return (Firebug.filterSystemURLs && isSystemURL(getURLForStyleSheet(sheet))) ? null : sheet;
27706             }
27707         }
27708         catch (exc)
27709         {
27710             if (FBTrace.DBG_LOCATIONS)
27711                 FBTrace.sysout("css.getDefaultLocation FAILS "+exc, exc);
27712         }
27713     },
27714
27715     getObjectDescription: function(styleSheet)
27716     {
27717         var url = getURLForStyleSheet(styleSheet);
27718         var instance = getInstanceForStyleSheet(styleSheet);
27719
27720         var baseDescription = splitURLBase(url);
27721         if (instance) {
27722           baseDescription.name = baseDescription.name + " #" + (instance + 1);
27723         }
27724         return baseDescription;
27725     },
27726
27727     search: function(text, reverse)
27728     {
27729         var curDoc = this.searchCurrentDoc(!Firebug.searchGlobal, text, reverse);
27730         if (!curDoc && Firebug.searchGlobal)
27731         {
27732             return this.searchOtherDocs(text, reverse);
27733         }
27734         return curDoc;
27735     },
27736
27737     searchOtherDocs: function(text, reverse)
27738     {
27739         var scanRE = Firebug.Search.getTestingRegex(text);
27740         function scanDoc(styleSheet) {
27741             // we don't care about reverse here as we are just looking for existence,
27742             // if we do have a result we will handle the reverse logic on display
27743             for (var i = 0; i < styleSheet.cssRules.length; i++)
27744             {
27745                 if (scanRE.test(styleSheet.cssRules[i].cssText))
27746                 {
27747                     return true;
27748                 }
27749             }
27750         }
27751
27752         if (this.navigateToNextDocument(scanDoc, reverse))
27753         {
27754             return this.searchCurrentDoc(true, text, reverse);
27755         }
27756     },
27757
27758     searchCurrentDoc: function(wrapSearch, text, reverse)
27759     {
27760         if (!text)
27761         {
27762             delete this.currentSearch;
27763             return false;
27764         }
27765
27766         var row;
27767         if (this.currentSearch && text == this.currentSearch.text)
27768         {
27769             row = this.currentSearch.findNext(wrapSearch, false, reverse, Firebug.Search.isCaseSensitive(text));
27770         }
27771         else
27772         {
27773             if (this.editing)
27774             {
27775                 this.currentSearch = new TextSearch(this.stylesheetEditor.box);
27776                 row = this.currentSearch.find(text, reverse, Firebug.Search.isCaseSensitive(text));
27777
27778                 if (row)
27779                 {
27780                     var sel = this.document.defaultView.getSelection();
27781                     sel.removeAllRanges();
27782                     sel.addRange(this.currentSearch.range);
27783                     scrollSelectionIntoView(this);
27784                     return true;
27785                 }
27786                 else
27787                     return false;
27788             }
27789             else
27790             {
27791                 function findRow(node) { return node.nodeType == 1 ? node : node.parentNode; }
27792                 this.currentSearch = new TextSearch(this.panelNode, findRow);
27793                 row = this.currentSearch.find(text, reverse, Firebug.Search.isCaseSensitive(text));
27794             }
27795         }
27796
27797         if (row)
27798         {
27799             this.document.defaultView.getSelection().selectAllChildren(row);
27800             scrollIntoCenterView(row, this.panelNode);
27801             dispatch([Firebug.A11yModel], 'onCSSSearchMatchFound', [this, text, row]);
27802             return true;
27803         }
27804         else
27805         {
27806             dispatch([Firebug.A11yModel], 'onCSSSearchMatchFound', [this, text, null]);
27807             return false;
27808         }
27809     },
27810
27811     getSearchOptionsMenuItems: function()
27812     {
27813         return [
27814             Firebug.Search.searchOptionMenu("search.Case_Sensitive", "searchCaseSensitive"),
27815             Firebug.Search.searchOptionMenu("search.Multiple_Files", "searchGlobal")
27816         ];
27817     }
27818 });
27819 /**/
27820 // ************************************************************************************************
27821
27822 function CSSElementPanel() {}
27823
27824 CSSElementPanel.prototype = extend(Firebug.CSSStyleSheetPanel.prototype,
27825 {
27826     template: domplate(
27827     {
27828         cascadedTag:
27829             DIV({"class": "a11yCSSView",  role : 'presentation'},
27830                 DIV({role : 'list', 'aria-label' : $STR('aria.labels.style rules') },
27831                     FOR("rule", "$rules",
27832                         TAG("$ruleTag", {rule: "$rule"})
27833                     )
27834                 ),
27835                 DIV({role : "list", 'aria-label' :$STR('aria.labels.inherited style rules')},
27836                     FOR("section", "$inherited",
27837                         H1({"class": "cssInheritHeader groupHeader focusRow", role : 'listitem' },
27838                             SPAN({"class": "cssInheritLabel"}, "$inheritLabel"),
27839                             TAG(FirebugReps.Element.shortTag, {object: "$section.element"})
27840                         ),
27841                         DIV({role : 'group'},
27842                             FOR("rule", "$section.rules",
27843                                 TAG("$ruleTag", {rule: "$rule"})
27844                             )
27845                         )
27846                     )
27847                  )
27848             ),
27849
27850         ruleTag:
27851             isIE ?
27852             // IE needs the sourceLink first, otherwise it will be rendered outside the panel
27853             DIV({"class": "cssElementRuleContainer"},
27854                 TAG(FirebugReps.SourceLink.tag, {object: "$rule.sourceLink"}),
27855                 TAG(CSSStyleRuleTag.tag, {rule: "$rule"})
27856             )
27857             :
27858             // other browsers need the sourceLink last, otherwise it will cause an extra space
27859             // before the rule representation
27860             DIV({"class": "cssElementRuleContainer"},
27861                 TAG(CSSStyleRuleTag.tag, {rule: "$rule"}),
27862                 TAG(FirebugReps.SourceLink.tag, {object: "$rule.sourceLink"})
27863             )
27864     }),
27865
27866     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
27867
27868     updateCascadeView: function(element)
27869     {
27870         //dispatch([Firebug.A11yModel], 'onBeforeCSSRulesAdded', [this]);
27871         var rules = [], sections = [], usedProps = {};
27872         this.getInheritedRules(element, sections, usedProps);
27873         this.getElementRules(element, rules, usedProps);
27874
27875         if (rules.length || sections.length)
27876         {
27877             var inheritLabel = "Inherited from"; // $STR("InheritedFrom");
27878             var result = this.template.cascadedTag.replace({rules: rules, inherited: sections,
27879                 inheritLabel: inheritLabel}, this.panelNode);
27880             //dispatch([Firebug.A11yModel], 'onCSSRulesAdded', [this, result]);
27881         }
27882         else
27883         {
27884             var result = FirebugReps.Warning.tag.replace({object: "EmptyElementCSS"}, this.panelNode);
27885             //dispatch([Firebug.A11yModel], 'onCSSRulesAdded', [this, result]);
27886         }
27887
27888         // TODO: xxxpedro remove when there the external resource problem is fixed
27889         if (CssAnalyzer.hasExternalStyleSheet())
27890             CssAnalyzer.externalStyleSheetWarning.tag.append({
27891                 object: "The results here may be inaccurate because some " +
27892                         "stylesheets could not be loaded due to access restrictions. ",
27893                 link: "more...",
27894                 href: "http://getfirebug.com/wiki/index.php/Firebug_Lite_FAQ#I_keep_seeing_.22This_element_has_no_style_rules.22"
27895             }, this.panelNode);
27896     },
27897
27898     getStylesheetURL: function(rule)
27899     {
27900         // if the parentStyleSheet.href is null, CSS std says its inline style.
27901         // TODO: xxxpedro IE doesn't have rule.parentStyleSheet so we must fall back to the doc.location
27902         if (rule && rule.parentStyleSheet && rule.parentStyleSheet.href)
27903             return rule.parentStyleSheet.href;
27904         else
27905             return this.selection.ownerDocument.location.href;
27906     },
27907
27908     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
27909
27910     getInheritedRules: function(element, sections, usedProps)
27911     {
27912         var parent = element.parentNode;
27913         if (parent && parent.nodeType == 1)
27914         {
27915             this.getInheritedRules(parent, sections, usedProps);
27916
27917             var rules = [];
27918             this.getElementRules(parent, rules, usedProps, true);
27919
27920             if (rules.length)
27921                 sections.splice(0, 0, {element: parent, rules: rules});
27922         }
27923     },
27924
27925     getElementRules: function(element, rules, usedProps, inheritMode)
27926     {
27927         var inspectedRules, displayedRules = {};
27928
27929         inspectedRules = CssAnalyzer.getElementCSSRules(element);
27930
27931         if (inspectedRules)
27932         {
27933             for (var i = 0, length=inspectedRules.length; i < length; ++i)
27934             {
27935                 var ruleId = inspectedRules[i];
27936                 var ruleData = CssAnalyzer.getRuleData(ruleId);
27937                 var rule = ruleData.rule;
27938
27939                 var ssid = ruleData.styleSheetId;
27940                 var parentStyleSheet = StyleSheetCache.get(ssid);
27941
27942                 var href = parentStyleSheet.externalURL ? parentStyleSheet.externalURL : parentStyleSheet.href;  // Null means inline
27943
27944                 var instance = null;
27945                 //var instance = getInstanceForStyleSheet(rule.parentStyleSheet, element.ownerDocument);
27946
27947                 var isSystemSheet = false;
27948                 //var isSystemSheet = isSystemStyleSheet(rule.parentStyleSheet);
27949
27950                 if (!Firebug.showUserAgentCSS && isSystemSheet) // This removes user agent rules
27951                     continue;
27952
27953                 if (!href)
27954                     href = element.ownerDocument.location.href; // http://code.google.com/p/fbug/issues/detail?id=452
27955
27956                 var props = this.getRuleProperties(this.context, rule, inheritMode);
27957                 if (inheritMode && !props.length)
27958                     continue;
27959
27960                 //
27961                 //var line = domUtils.getRuleLine(rule);
27962                 // TODO: xxxpedro CSS line number
27963                 var line = ruleData.lineNo;
27964
27965                 var ruleId = rule.selectorText+"/"+line;
27966                 var sourceLink = new SourceLink(href, line, "css", rule, instance);
27967
27968                 this.markOverridenProps(props, usedProps, inheritMode);
27969
27970                 rules.splice(0, 0, {rule: rule, id: ruleId,
27971                         selector: ruleData.selector, sourceLink: sourceLink,
27972                         props: props, inherited: inheritMode,
27973                         isSystemSheet: isSystemSheet});
27974             }
27975         }
27976
27977         if (element.style)
27978             this.getStyleProperties(element, rules, usedProps, inheritMode);
27979
27980         if (FBTrace.DBG_CSS)
27981             FBTrace.sysout("getElementRules "+rules.length+" rules for "+getElementXPath(element), rules);
27982     },
27983     /*
27984     getElementRules: function(element, rules, usedProps, inheritMode)
27985     {
27986         var inspectedRules, displayedRules = {};
27987         try
27988         {
27989             inspectedRules = domUtils ? domUtils.getCSSStyleRules(element) : null;
27990         } catch (exc) {}
27991
27992         if (inspectedRules)
27993         {
27994             for (var i = 0; i < inspectedRules.Count(); ++i)
27995             {
27996                 var rule = QI(inspectedRules.GetElementAt(i), nsIDOMCSSStyleRule);
27997
27998                 var href = rule.parentStyleSheet.href;  // Null means inline
27999
28000                 var instance = getInstanceForStyleSheet(rule.parentStyleSheet, element.ownerDocument);
28001
28002                 var isSystemSheet = isSystemStyleSheet(rule.parentStyleSheet);
28003                 if (!Firebug.showUserAgentCSS && isSystemSheet) // This removes user agent rules
28004                     continue;
28005                 if (!href)
28006                     href = element.ownerDocument.location.href; // http://code.google.com/p/fbug/issues/detail?id=452
28007
28008                 var props = this.getRuleProperties(this.context, rule, inheritMode);
28009                 if (inheritMode && !props.length)
28010                     continue;
28011
28012                 var line = domUtils.getRuleLine(rule);
28013                 var ruleId = rule.selectorText+"/"+line;
28014                 var sourceLink = new SourceLink(href, line, "css", rule, instance);
28015
28016                 this.markOverridenProps(props, usedProps, inheritMode);
28017
28018                 rules.splice(0, 0, {rule: rule, id: ruleId,
28019                         selector: rule.selectorText, sourceLink: sourceLink,
28020                         props: props, inherited: inheritMode,
28021                         isSystemSheet: isSystemSheet});
28022             }
28023         }
28024
28025         if (element.style)
28026             this.getStyleProperties(element, rules, usedProps, inheritMode);
28027
28028         if (FBTrace.DBG_CSS)
28029             FBTrace.sysout("getElementRules "+rules.length+" rules for "+getElementXPath(element), rules);
28030     },
28031     /**/
28032     markOverridenProps: function(props, usedProps, inheritMode)
28033     {
28034         for (var i = 0; i < props.length; ++i)
28035         {
28036             var prop = props[i];
28037             if ( usedProps.hasOwnProperty(prop.name) )
28038             {
28039                 var deadProps = usedProps[prop.name]; // all previous occurrences of this property
28040                 for (var j = 0; j < deadProps.length; ++j)
28041                 {
28042                     var deadProp = deadProps[j];
28043                     if (!deadProp.disabled && !deadProp.wasInherited && deadProp.important && !prop.important)
28044                         prop.overridden = true;  // new occurrence overridden
28045                     else if (!prop.disabled)
28046                         deadProp.overridden = true;  // previous occurrences overridden
28047                 }
28048             }
28049             else
28050                 usedProps[prop.name] = [];
28051
28052             prop.wasInherited = inheritMode ? true : false;
28053             usedProps[prop.name].push(prop);  // all occurrences of a property seen so far, by name
28054         }
28055     },
28056
28057     getStyleProperties: function(element, rules, usedProps, inheritMode)
28058     {
28059         var props = this.parseCSSProps(element.style, inheritMode);
28060         this.addOldProperties(this.context, getElementXPath(element), inheritMode, props);
28061
28062         sortProperties(props);
28063         this.markOverridenProps(props, usedProps, inheritMode);
28064
28065         if (props.length)
28066             rules.splice(0, 0,
28067                     {rule: element, id: getElementXPath(element),
28068                         selector: "element.style", props: props, inherited: inheritMode});
28069     },
28070
28071     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
28072     // extends Panel
28073
28074     name: "css",
28075     title: "Style",
28076     parentPanel: "HTML",
28077     order: 0,
28078
28079     initialize: function()
28080     {
28081         this.context = Firebug.chrome; // TODO: xxxpedro css2
28082         this.document = Firebug.chrome.document; // TODO: xxxpedro css2
28083
28084         Firebug.CSSStyleSheetPanel.prototype.initialize.apply(this, arguments);
28085
28086         // TODO: xxxpedro css2
28087         var selection = ElementCache.get(Firebug.context.persistedState.selectedHTMLElementId);
28088         if (selection)
28089             this.select(selection, true);
28090
28091         //this.updateCascadeView(document.getElementsByTagName("h1")[0]);
28092         //this.updateCascadeView(document.getElementById("build"));
28093
28094         /*
28095         this.onStateChange = bindFixed(this.contentStateCheck, this);
28096         this.onHoverChange = bindFixed(this.contentStateCheck, this, STATE_HOVER);
28097         this.onActiveChange = bindFixed(this.contentStateCheck, this, STATE_ACTIVE);
28098         /**/
28099     },
28100
28101     ishow: function(state)
28102     {
28103     },
28104
28105     watchWindow: function(win)
28106     {
28107         if (domUtils)
28108         {
28109             // Normally these would not be required, but in order to update after the state is set
28110             // using the options menu we need to monitor these global events as well
28111             var doc = win.document;
28112             ///addEvent(doc, "mouseover", this.onHoverChange);
28113             ///addEvent(doc, "mousedown", this.onActiveChange);
28114         }
28115     },
28116     unwatchWindow: function(win)
28117     {
28118         var doc = win.document;
28119         ///removeEvent(doc, "mouseover", this.onHoverChange);
28120         ///removeEvent(doc, "mousedown", this.onActiveChange);
28121
28122         if (isAncestor(this.stateChangeEl, doc))
28123         {
28124             this.removeStateChangeHandlers();
28125         }
28126     },
28127
28128     supportsObject: function(object)
28129     {
28130         return object instanceof Element ? 1 : 0;
28131     },
28132
28133     updateView: function(element)
28134     {
28135         this.updateCascadeView(element);
28136         if (domUtils)
28137         {
28138             this.contentState = safeGetContentState(element);
28139             this.addStateChangeHandlers(element);
28140         }
28141     },
28142
28143     updateSelection: function(element)
28144     {
28145         if ( !instanceOf(element , "Element") ) // html supports SourceLink
28146             return;
28147
28148         if (sothinkInstalled)
28149         {
28150             FirebugReps.Warning.tag.replace({object: "SothinkWarning"}, this.panelNode);
28151             return;
28152         }
28153
28154         /*
28155         if (!domUtils)
28156         {
28157             FirebugReps.Warning.tag.replace({object: "DOMInspectorWarning"}, this.panelNode);
28158             return;
28159         }
28160         /**/
28161
28162         if (!element)
28163             return;
28164
28165         this.updateView(element);
28166     },
28167
28168     updateOption: function(name, value)
28169     {
28170         if (name == "showUserAgentCSS" || name == "expandShorthandProps")
28171             this.refresh();
28172     },
28173
28174     getOptionsMenuItems: function()
28175     {
28176         var ret = [
28177             {label: "Show User Agent CSS", type: "checkbox", checked: Firebug.showUserAgentCSS,
28178                     command: bindFixed(Firebug.togglePref, Firebug, "showUserAgentCSS") },
28179             {label: "Expand Shorthand Properties", type: "checkbox", checked: Firebug.expandShorthandProps,
28180                     command: bindFixed(Firebug.togglePref, Firebug, "expandShorthandProps") }
28181         ];
28182         if (domUtils && this.selection)
28183         {
28184             var state = safeGetContentState(this.selection);
28185
28186             ret.push("-");
28187             ret.push({label: ":active", type: "checkbox", checked: state & STATE_ACTIVE,
28188               command: bindFixed(this.updateContentState, this, STATE_ACTIVE, state & STATE_ACTIVE)});
28189             ret.push({label: ":hover", type: "checkbox", checked: state & STATE_HOVER,
28190               command: bindFixed(this.updateContentState, this, STATE_HOVER, state & STATE_HOVER)});
28191         }
28192         return ret;
28193     },
28194
28195     updateContentState: function(state, remove)
28196     {
28197         domUtils.setContentState(remove ? this.selection.ownerDocument.documentElement : this.selection, state);
28198         this.refresh();
28199     },
28200
28201     addStateChangeHandlers: function(el)
28202     {
28203       this.removeStateChangeHandlers();
28204
28205       /*
28206       addEvent(el, "focus", this.onStateChange);
28207       addEvent(el, "blur", this.onStateChange);
28208       addEvent(el, "mouseup", this.onStateChange);
28209       addEvent(el, "mousedown", this.onStateChange);
28210       addEvent(el, "mouseover", this.onStateChange);
28211       addEvent(el, "mouseout", this.onStateChange);
28212       /**/
28213
28214       this.stateChangeEl = el;
28215     },
28216
28217     removeStateChangeHandlers: function()
28218     {
28219         var sel = this.stateChangeEl;
28220         if (sel)
28221         {
28222             /*
28223             removeEvent(sel, "focus", this.onStateChange);
28224             removeEvent(sel, "blur", this.onStateChange);
28225             removeEvent(sel, "mouseup", this.onStateChange);
28226             removeEvent(sel, "mousedown", this.onStateChange);
28227             removeEvent(sel, "mouseover", this.onStateChange);
28228             removeEvent(sel, "mouseout", this.onStateChange);
28229             /**/
28230         }
28231     },
28232
28233     contentStateCheck: function(state)
28234     {
28235         if (!state || this.contentState & state)
28236         {
28237             var timeoutRunner = bindFixed(function()
28238             {
28239                 var newState = safeGetContentState(this.selection);
28240                 if (newState != this.contentState)
28241                 {
28242                     this.context.invalidatePanels(this.name);
28243                 }
28244             }, this);
28245
28246             // Delay exec until after the event has processed and the state has been updated
28247             setTimeout(timeoutRunner, 0);
28248         }
28249     }
28250 });
28251
28252 function safeGetContentState(selection)
28253 {
28254     try
28255     {
28256         return domUtils.getContentState(selection);
28257     }
28258     catch (e)
28259     {
28260         if (FBTrace.DBG_ERRORS)
28261             FBTrace.sysout("css.safeGetContentState; EXCEPTION", e);
28262     }
28263 }
28264
28265 // ************************************************************************************************
28266
28267 function CSSComputedElementPanel() {}
28268
28269 CSSComputedElementPanel.prototype = extend(CSSElementPanel.prototype,
28270 {
28271     template: domplate(
28272     {
28273         computedTag:
28274             DIV({"class": "a11yCSSView", role : "list", "aria-label" : $STR('aria.labels.computed styles')},
28275                 FOR("group", "$groups",
28276                     H1({"class": "cssInheritHeader groupHeader focusRow", role : "listitem"},
28277                         SPAN({"class": "cssInheritLabel"}, "$group.title")
28278                     ),
28279                     TABLE({width: "100%", role : 'group'},
28280                         TBODY({role : 'presentation'},
28281                             FOR("prop", "$group.props",
28282                                 TR({"class": 'focusRow computedStyleRow', role : 'listitem'},
28283                                     TD({"class": "stylePropName", role : 'presentation'}, "$prop.name"),
28284                                     TD({"class": "stylePropValue", role : 'presentation'}, "$prop.value")
28285                                 )
28286                             )
28287                         )
28288                     )
28289                 )
28290             )
28291     }),
28292
28293     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
28294
28295     updateComputedView: function(element)
28296     {
28297         var win = isIE ?
28298                 element.ownerDocument.parentWindow :
28299                 element.ownerDocument.defaultView;
28300
28301         var style = isIE ?
28302                 element.currentStyle :
28303                 win.getComputedStyle(element, "");
28304
28305         var groups = [];
28306
28307         for (var groupName in styleGroups)
28308         {
28309             // TODO: xxxpedro i18n $STR
28310             //var title = $STR("StyleGroup-" + groupName);
28311             var title = styleGroupTitles[groupName];
28312             var group = {title: title, props: []};
28313             groups.push(group);
28314
28315             var props = styleGroups[groupName];
28316             for (var i = 0; i < props.length; ++i)
28317             {
28318                 var propName = props[i];
28319                 var propValue = style.getPropertyValue ?
28320                         style.getPropertyValue(propName) :
28321                         ""+style[toCamelCase(propName)];
28322
28323                 if (propValue === undefined || propValue === null)
28324                     continue;
28325
28326                 propValue = stripUnits(rgbToHex(propValue));
28327                 if (propValue)
28328                     group.props.push({name: propName, value: propValue});
28329             }
28330         }
28331
28332         var result = this.template.computedTag.replace({groups: groups}, this.panelNode);
28333         //dispatch([Firebug.A11yModel], 'onCSSRulesAdded', [this, result]);
28334     },
28335
28336     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
28337     // extends Panel
28338
28339     name: "computed",
28340     title: "Computed",
28341     parentPanel: "HTML",
28342     order: 1,
28343
28344     updateView: function(element)
28345     {
28346         this.updateComputedView(element);
28347     },
28348
28349     getOptionsMenuItems: function()
28350     {
28351         return [
28352             {label: "Refresh", command: bind(this.refresh, this) }
28353         ];
28354     }
28355 });
28356
28357 // ************************************************************************************************
28358 // CSSEditor
28359
28360 function CSSEditor(doc)
28361 {
28362     this.initializeInline(doc);
28363 }
28364
28365 CSSEditor.prototype = domplate(Firebug.InlineEditor.prototype,
28366 {
28367     insertNewRow: function(target, insertWhere)
28368     {
28369         var rule = Firebug.getRepObject(target);
28370         var emptyProp =
28371         {
28372             // TODO: xxxpedro - uses charCode(255) to force the element being rendered,
28373             // allowing webkit to get the correct position of the property name "span",
28374             // when inserting a new CSS rule?
28375             name: "",
28376             value: "",
28377             important: ""
28378         };
28379
28380         if (insertWhere == "before")
28381             return CSSPropTag.tag.insertBefore({prop: emptyProp, rule: rule}, target);
28382         else
28383             return CSSPropTag.tag.insertAfter({prop: emptyProp, rule: rule}, target);
28384     },
28385
28386     saveEdit: function(target, value, previousValue)
28387     {
28388         // We need to check the value first in order to avoid a problem in IE8
28389         // See Issue 3038: Empty (null) styles when adding CSS styles in Firebug Lite
28390         if (!value) return;
28391
28392         target.innerHTML = escapeForCss(value);
28393
28394         var row = getAncestorByClass(target, "cssProp");
28395         if (hasClass(row, "disabledStyle"))
28396             toggleClass(row, "disabledStyle");
28397
28398         var rule = Firebug.getRepObject(target);
28399
28400         if (hasClass(target, "cssPropName"))
28401         {
28402             if (value && previousValue != value)  // name of property has changed.
28403             {
28404                 var propValue = getChildByClass(row, "cssPropValue")[textContent];
28405                 var parsedValue = parsePriority(propValue);
28406
28407                 if (propValue && propValue != "undefined") {
28408                     if (FBTrace.DBG_CSS)
28409                         FBTrace.sysout("CSSEditor.saveEdit : "+previousValue+"->"+value+" = "+propValue+"\n");
28410                     if (previousValue)
28411                         Firebug.CSSModule.removeProperty(rule, previousValue);
28412                     Firebug.CSSModule.setProperty(rule, value, parsedValue.value, parsedValue.priority);
28413                 }
28414             }
28415             else if (!value) // name of the property has been deleted, so remove the property.
28416                 Firebug.CSSModule.removeProperty(rule, previousValue);
28417         }
28418         else if (getAncestorByClass(target, "cssPropValue"))
28419         {
28420             var propName = getChildByClass(row, "cssPropName")[textContent];
28421             var propValue = getChildByClass(row, "cssPropValue")[textContent];
28422
28423             if (FBTrace.DBG_CSS)
28424             {
28425                 FBTrace.sysout("CSSEditor.saveEdit propName=propValue: "+propName +" = "+propValue+"\n");
28426                // FBTrace.sysout("CSSEditor.saveEdit BEFORE style:",style);
28427             }
28428
28429             if (value && value != "null")
28430             {
28431                 var parsedValue = parsePriority(value);
28432                 Firebug.CSSModule.setProperty(rule, propName, parsedValue.value, parsedValue.priority);
28433             }
28434             else if (previousValue && previousValue != "null")
28435                 Firebug.CSSModule.removeProperty(rule, propName);
28436         }
28437
28438         this.panel.markChange(this.panel.name == "stylesheet");
28439     },
28440
28441     advanceToNext: function(target, charCode)
28442     {
28443         if (charCode == 58 /*":"*/ && hasClass(target, "cssPropName"))
28444             return true;
28445     },
28446
28447     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
28448
28449     getAutoCompleteRange: function(value, offset)
28450     {
28451         if (hasClass(this.target, "cssPropName"))
28452             return {start: 0, end: value.length-1};
28453         else
28454             return parseCSSValue(value, offset);
28455     },
28456
28457     getAutoCompleteList: function(preExpr, expr, postExpr)
28458     {
28459         if (hasClass(this.target, "cssPropName"))
28460         {
28461             return getCSSPropertyNames();
28462         }
28463         else
28464         {
28465             var row = getAncestorByClass(this.target, "cssProp");
28466             var propName = getChildByClass(row, "cssPropName")[textContent];
28467             return getCSSKeywordsByProperty(propName);
28468         }
28469     }
28470 });
28471
28472 //************************************************************************************************
28473 //CSSRuleEditor
28474
28475 function CSSRuleEditor(doc)
28476 {
28477     this.initializeInline(doc);
28478     this.completeAsYouType = false;
28479 }
28480 CSSRuleEditor.uniquifier = 0;
28481 CSSRuleEditor.prototype = domplate(Firebug.InlineEditor.prototype,
28482 {
28483     insertNewRow: function(target, insertWhere)
28484     {
28485          var emptyRule = {
28486                  selector: "",
28487                  id: "",
28488                  props: [],
28489                  isSelectorEditable: true
28490          };
28491
28492          if (insertWhere == "before")
28493              return CSSStyleRuleTag.tag.insertBefore({rule: emptyRule}, target);
28494          else
28495              return CSSStyleRuleTag.tag.insertAfter({rule: emptyRule}, target);
28496     },
28497
28498     saveEdit: function(target, value, previousValue)
28499     {
28500         if (FBTrace.DBG_CSS)
28501             FBTrace.sysout("CSSRuleEditor.saveEdit: '" + value + "'  '" + previousValue + "'", target);
28502
28503         target.innerHTML = escapeForCss(value);
28504
28505         if (value === previousValue)     return;
28506
28507         var row = getAncestorByClass(target, "cssRule");
28508         var styleSheet = this.panel.location;
28509         styleSheet = styleSheet.editStyleSheet ? styleSheet.editStyleSheet.sheet : styleSheet;
28510
28511         var cssRules = styleSheet.cssRules;
28512         var rule = Firebug.getRepObject(target), oldRule = rule;
28513         var ruleIndex = cssRules.length;
28514         if (rule || Firebug.getRepObject(row.nextSibling))
28515         {
28516             var searchRule = rule || Firebug.getRepObject(row.nextSibling);
28517             for (ruleIndex=0; ruleIndex<cssRules.length && searchRule!=cssRules[ruleIndex]; ruleIndex++) {}
28518         }
28519
28520         // Delete in all cases except for new add
28521         // We want to do this before the insert to ease change tracking
28522         if (oldRule)
28523         {
28524             Firebug.CSSModule.deleteRule(styleSheet, ruleIndex);
28525         }
28526
28527         // Firefox does not follow the spec for the update selector text case.
28528         // When attempting to update the value, firefox will silently fail.
28529         // See https://bugzilla.mozilla.org/show_bug.cgi?id=37468 for the quite
28530         // old discussion of this bug.
28531         // As a result we need to recreate the style every time the selector
28532         // changes.
28533         if (value)
28534         {
28535             var cssText = [ value, "{" ];
28536             var props = row.getElementsByClassName("cssProp");
28537             for (var i = 0; i < props.length; i++) {
28538                 var propEl = props[i];
28539                 if (!hasClass(propEl, "disabledStyle")) {
28540                     cssText.push(getChildByClass(propEl, "cssPropName")[textContent]);
28541                     cssText.push(":");
28542                     cssText.push(getChildByClass(propEl, "cssPropValue")[textContent]);
28543                     cssText.push(";");
28544                 }
28545             }
28546             cssText.push("}");
28547             cssText = cssText.join("");
28548
28549             try
28550             {
28551                 var insertLoc = Firebug.CSSModule.insertRule(styleSheet, cssText, ruleIndex);
28552                 rule = cssRules[insertLoc];
28553                 ruleIndex++;
28554             }
28555             catch (err)
28556             {
28557                 if (FBTrace.DBG_CSS || FBTrace.DBG_ERRORS)
28558                     FBTrace.sysout("CSS Insert Error: "+err, err);
28559
28560                 target.innerHTML = escapeForCss(previousValue);
28561                 row.repObject = undefined;
28562                 return;
28563             }
28564         } else {
28565             rule = undefined;
28566         }
28567
28568         // Update the rep object
28569         row.repObject = rule;
28570         if (!oldRule)
28571         {
28572             // Who knows what the domutils will return for rule line
28573             // for a recently created rule. To be safe we just generate
28574             // a unique value as this is only used as an internal key.
28575             var ruleId = "new/"+value+"/"+(++CSSRuleEditor.uniquifier);
28576             row.setAttribute("ruleId", ruleId);
28577         }
28578
28579         this.panel.markChange(this.panel.name == "stylesheet");
28580     }
28581 });
28582
28583 // ************************************************************************************************
28584 // StyleSheetEditor
28585
28586 function StyleSheetEditor(doc)
28587 {
28588     this.box = this.tag.replace({}, doc, this);
28589     this.input = this.box.firstChild;
28590 }
28591
28592 StyleSheetEditor.prototype = domplate(Firebug.BaseEditor,
28593 {
28594     multiLine: true,
28595
28596     tag: DIV(
28597         TEXTAREA({"class": "styleSheetEditor fullPanelEditor", oninput: "$onInput"})
28598     ),
28599
28600     getValue: function()
28601     {
28602         return this.input.value;
28603     },
28604
28605     setValue: function(value)
28606     {
28607         return this.input.value = value;
28608     },
28609
28610     show: function(target, panel, value, textSize, targetSize)
28611     {
28612         this.target = target;
28613         this.panel = panel;
28614
28615         this.panel.panelNode.appendChild(this.box);
28616
28617         this.input.value = value;
28618         this.input.focus();
28619
28620         var command = Firebug.chrome.$("cmd_toggleCSSEditing");
28621         command.setAttribute("checked", true);
28622     },
28623
28624     hide: function()
28625     {
28626         var command = Firebug.chrome.$("cmd_toggleCSSEditing");
28627         command.setAttribute("checked", false);
28628
28629         if (this.box.parentNode == this.panel.panelNode)
28630             this.panel.panelNode.removeChild(this.box);
28631
28632         delete this.target;
28633         delete this.panel;
28634         delete this.styleSheet;
28635     },
28636
28637     saveEdit: function(target, value, previousValue)
28638     {
28639         Firebug.CSSModule.freeEdit(this.styleSheet, value);
28640     },
28641
28642     endEditing: function()
28643     {
28644         this.panel.refresh();
28645         return true;
28646     },
28647
28648     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
28649
28650     onInput: function()
28651     {
28652         Firebug.Editor.update();
28653     },
28654
28655     scrollToLine: function(line, offset)
28656     {
28657         this.startMeasuring(this.input);
28658         var lineHeight = this.measureText().height;
28659         this.stopMeasuring();
28660
28661         this.input.scrollTop = (line * lineHeight) + offset;
28662     }
28663 });
28664
28665 // ************************************************************************************************
28666 // Local Helpers
28667
28668 var rgbToHex = function rgbToHex(value)
28669 {
28670     return value.replace(/\brgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)/gi, rgbToHexReplacer);
28671 };
28672
28673 var rgbToHexReplacer = function(_, r, g, b) {
28674     return '#' + ((1 << 24) + (r << 16) + (g << 8) + (b << 0)).toString(16).substr(-6).toUpperCase();
28675 };
28676
28677 var stripUnits = function stripUnits(value)
28678 {
28679     // remove units from '0px', '0em' etc. leave non-zero units in-tact.
28680     return value.replace(/(url\(.*?\)|[^0]\S*\s*)|0(%|em|ex|px|in|cm|mm|pt|pc)(\s|$)/gi, stripUnitsReplacer);
28681 };
28682
28683 var stripUnitsReplacer = function(_, skip, remove, whitespace) {
28684     return skip || ('0' + whitespace);
28685 };
28686
28687 function parsePriority(value)
28688 {
28689     var rePriority = /(.*?)\s*(!important)?$/;
28690     var m = rePriority.exec(value);
28691     var propValue = m ? m[1] : "";
28692     var priority = m && m[2] ? "important" : "";
28693     return {value: propValue, priority: priority};
28694 }
28695
28696 function parseURLValue(value)
28697 {
28698     var m = reURL.exec(value);
28699     return m ? m[1] : "";
28700 }
28701
28702 function parseRepeatValue(value)
28703 {
28704     var m = reRepeat.exec(value);
28705     return m ? m[0] : "";
28706 }
28707
28708 function parseCSSValue(value, offset)
28709 {
28710     var start = 0;
28711     var m;
28712     while (1)
28713     {
28714         m = reSplitCSS.exec(value);
28715         if (m && m.index+m[0].length < offset)
28716         {
28717             value = value.substr(m.index+m[0].length);
28718             start += m.index+m[0].length;
28719             offset -= m.index+m[0].length;
28720         }
28721         else
28722             break;
28723     }
28724
28725     if (m)
28726     {
28727         var type;
28728         if (m[1])
28729             type = "url";
28730         else if (m[2] || m[3])
28731             type = "rgb";
28732         else if (m[4])
28733             type = "int";
28734
28735         return {value: m[0], start: start+m.index, end: start+m.index+(m[0].length-1), type: type};
28736     }
28737 }
28738
28739 function findPropByName(props, name)
28740 {
28741     for (var i = 0; i < props.length; ++i)
28742     {
28743         if (props[i].name == name)
28744             return i;
28745     }
28746 }
28747
28748 function sortProperties(props)
28749 {
28750     props.sort(function(a, b)
28751     {
28752         return a.name > b.name ? 1 : -1;
28753     });
28754 }
28755
28756 function getTopmostRuleLine(panelNode)
28757 {
28758     for (var child = panelNode.firstChild; child; child = child.nextSibling)
28759     {
28760         if (child.offsetTop+child.offsetHeight > panelNode.scrollTop)
28761         {
28762             var rule = child.repObject;
28763             if (rule)
28764                 return {
28765                     line: domUtils.getRuleLine(rule),
28766                     offset: panelNode.scrollTop-child.offsetTop
28767                 };
28768         }
28769     }
28770     return 0;
28771 }
28772
28773 function getStyleSheetCSS(sheet, context)
28774 {
28775     if (sheet.ownerNode instanceof HTMLStyleElement)
28776         return sheet.ownerNode.innerHTML;
28777     else
28778         return context.sourceCache.load(sheet.href).join("");
28779 }
28780
28781 function getStyleSheetOwnerNode(sheet) {
28782     for (; sheet && !sheet.ownerNode; sheet = sheet.parentStyleSheet);
28783
28784     return sheet.ownerNode;
28785 }
28786
28787 function scrollSelectionIntoView(panel)
28788 {
28789     var selCon = getSelectionController(panel);
28790     selCon.scrollSelectionIntoView(
28791             nsISelectionController.SELECTION_NORMAL,
28792             nsISelectionController.SELECTION_FOCUS_REGION, true);
28793 }
28794
28795 function getSelectionController(panel)
28796 {
28797     var browser = Firebug.chrome.getPanelBrowser(panel);
28798     return browser.docShell.QueryInterface(nsIInterfaceRequestor)
28799         .getInterface(nsISelectionDisplay)
28800         .QueryInterface(nsISelectionController);
28801 }
28802
28803 // ************************************************************************************************
28804
28805 Firebug.registerModule(Firebug.CSSModule);
28806 Firebug.registerPanel(Firebug.CSSStyleSheetPanel);
28807 Firebug.registerPanel(CSSElementPanel);
28808 Firebug.registerPanel(CSSComputedElementPanel);
28809
28810 // ************************************************************************************************
28811
28812 }});
28813
28814
28815 /* See license.txt for terms of usage */
28816
28817 FBL.ns(function() { with (FBL) {
28818 // ************************************************************************************************
28819
28820 // ************************************************************************************************
28821 // Script Module
28822
28823 Firebug.Script = extend(Firebug.Module,
28824 {
28825     getPanel: function()
28826     {
28827         return Firebug.chrome ? Firebug.chrome.getPanel("Script") : null;
28828     },
28829
28830     selectSourceCode: function(index)
28831     {
28832         this.getPanel().selectSourceCode(index);
28833     }
28834 });
28835
28836 Firebug.registerModule(Firebug.Script);
28837
28838
28839 // ************************************************************************************************
28840 // Script Panel
28841
28842 function ScriptPanel(){};
28843
28844 ScriptPanel.prototype = extend(Firebug.Panel,
28845 {
28846     name: "Script",
28847     title: "Script",
28848
28849     selectIndex: 0, // index of the current selectNode's option
28850     sourceIndex: -1, // index of the script node, based in doc.getElementsByTagName("script")
28851
28852     options: {
28853         hasToolButtons: true
28854     },
28855
28856     create: function()
28857     {
28858         Firebug.Panel.create.apply(this, arguments);
28859
28860         this.onChangeSelect = bind(this.onChangeSelect, this);
28861
28862         var doc = Firebug.browser.document;
28863         var scripts = doc.getElementsByTagName("script");
28864         var selectNode = this.selectNode = createElement("select");
28865
28866         for(var i=0, script; script=scripts[i]; i++)
28867         {
28868             // Don't show Firebug Lite source code in the list of options
28869             if (Firebug.ignoreFirebugElements && script.getAttribute("firebugIgnore"))
28870                 continue;
28871
28872             var fileName = getFileName(script.src) || getFileName(doc.location.href);
28873             var option = createElement("option", {value:i});
28874
28875             option.appendChild(Firebug.chrome.document.createTextNode(fileName));
28876             selectNode.appendChild(option);
28877         };
28878
28879         this.toolButtonsNode.appendChild(selectNode);
28880     },
28881
28882     initialize: function()
28883     {
28884         // we must render the code first, so the persistent state can be restore
28885         this.selectSourceCode(this.selectIndex);
28886
28887         Firebug.Panel.initialize.apply(this, arguments);
28888
28889         addEvent(this.selectNode, "change", this.onChangeSelect);
28890     },
28891
28892     shutdown: function()
28893     {
28894         removeEvent(this.selectNode, "change", this.onChangeSelect);
28895
28896         Firebug.Panel.shutdown.apply(this, arguments);
28897     },
28898
28899     detach: function(oldChrome, newChrome)
28900     {
28901         Firebug.Panel.detach.apply(this, arguments);
28902
28903         var oldPanel = oldChrome.getPanel("Script");
28904         var index = oldPanel.selectIndex;
28905
28906         this.selectNode.selectedIndex = index;
28907         this.selectIndex = index;
28908         this.sourceIndex = -1;
28909     },
28910
28911     onChangeSelect: function(event)
28912     {
28913         var select = this.selectNode;
28914
28915         this.selectIndex = select.selectedIndex;
28916
28917         var option = select.options[select.selectedIndex];
28918         if (!option)
28919             return;
28920
28921         var selectedSourceIndex = parseInt(option.value);
28922
28923         this.renderSourceCode(selectedSourceIndex);
28924     },
28925
28926     selectSourceCode: function(index)
28927     {
28928         var select = this.selectNode;
28929         select.selectedIndex = index;
28930
28931         var option = select.options[index];
28932         if (!option)
28933             return;
28934
28935         var selectedSourceIndex = parseInt(option.value);
28936
28937         this.renderSourceCode(selectedSourceIndex);
28938     },
28939
28940     renderSourceCode: function(index)
28941     {
28942         if (this.sourceIndex != index)
28943         {
28944             var renderProcess = function renderProcess(src)
28945             {
28946                 var html = [],
28947                     hl = 0;
28948
28949                 src = isIE && !isExternal ?
28950                         src+'\n' :  // IE put an extra line when reading source of local resources
28951                         '\n'+src;
28952
28953                 // find the number of lines of code
28954                 src = src.replace(/\n\r|\r\n/g, "\n");
28955                 var match = src.match(/[\n]/g);
28956                 var lines=match ? match.length : 0;
28957
28958                 // render the full source code + line numbers html
28959                 html[hl++] = '<div><div class="sourceBox" style="left:';
28960                 html[hl++] = 35 + 7*(lines+'').length;
28961                 html[hl++] = 'px;"><pre class="sourceCode">';
28962                 html[hl++] = escapeHTML(src);
28963                 html[hl++] = '</pre></div><div class="lineNo">';
28964
28965                 // render the line number divs
28966                 for(var l=1, lines; l<=lines; l++)
28967                 {
28968                     html[hl++] = '<div line="';
28969                     html[hl++] = l;
28970                     html[hl++] = '">';
28971                     html[hl++] = l;
28972                     html[hl++] = '</div>';
28973                 }
28974
28975                 html[hl++] = '</div></div>';
28976
28977                 updatePanel(html);
28978             };
28979
28980             var updatePanel = function(html)
28981             {
28982                 self.panelNode.innerHTML = html.join("");
28983
28984                 // IE needs this timeout, otherwise the panel won't scroll
28985                 setTimeout(function(){
28986                     self.synchronizeUI();
28987                 },0);
28988             };
28989
28990             var onFailure = function()
28991             {
28992                 FirebugReps.Warning.tag.replace({object: "AccessRestricted"}, self.panelNode);
28993             };
28994
28995             var self = this;
28996
28997             var doc = Firebug.browser.document;
28998             var script = doc.getElementsByTagName("script")[index];
28999             var url = getScriptURL(script);
29000             var isExternal = url && url != doc.location.href;
29001
29002             try
29003             {
29004                 if (Firebug.disableResourceFetching)
29005                 {
29006                     renderProcess(Firebug.Lite.Proxy.fetchResourceDisabledMessage);
29007                 }
29008                 else if (isExternal)
29009                 {
29010                     Ajax.request({url: url, onSuccess: renderProcess, onFailure: onFailure});
29011                 }
29012                 else
29013                 {
29014                     var src = script.innerHTML;
29015                     renderProcess(src);
29016                 }
29017             }
29018             catch(e)
29019             {
29020                 onFailure();
29021             }
29022
29023             this.sourceIndex = index;
29024         }
29025     }
29026 });
29027
29028 Firebug.registerPanel(ScriptPanel);
29029
29030
29031 // ************************************************************************************************
29032
29033
29034 var getScriptURL = function getScriptURL(script)
29035 {
29036     var reFile = /([^\/\?#]+)(#.+)?$/;
29037     var rePath = /^(.*\/)/;
29038     var reProtocol = /^\w+:\/\//;
29039     var path = null;
29040     var doc = Firebug.browser.document;
29041
29042     var file = reFile.exec(script.src);
29043
29044     if (file)
29045     {
29046         var fileName = file[1];
29047         var fileOptions = file[2];
29048
29049         // absolute path
29050         if (reProtocol.test(script.src)) {
29051             path = rePath.exec(script.src)[1];
29052
29053         }
29054         // relative path
29055         else
29056         {
29057             var r = rePath.exec(script.src);
29058             var src = r ? r[1] : script.src;
29059             var backDir = /^((?:\.\.\/)+)(.*)/.exec(src);
29060             var reLastDir = /^(.*\/)[^\/]+\/$/;
29061             path = rePath.exec(doc.location.href)[1];
29062
29063             // "../some/path"
29064             if (backDir)
29065             {
29066                 var j = backDir[1].length/3;
29067                 var p;
29068                 while (j-- > 0)
29069                     path = reLastDir.exec(path)[1];
29070
29071                 path += backDir[2];
29072             }
29073
29074             else if(src.indexOf("/") != -1)
29075             {
29076                 // "./some/path"
29077                 if(/^\.\/./.test(src))
29078                 {
29079                     path += src.substring(2);
29080                 }
29081                 // "/some/path"
29082                 else if(/^\/./.test(src))
29083                 {
29084                     var domain = /^(\w+:\/\/[^\/]+)/.exec(path);
29085                     path = domain[1] + src;
29086                 }
29087                 // "some/path"
29088                 else
29089                 {
29090                     path += src;
29091                 }
29092             }
29093         }
29094     }
29095
29096     var m = path && path.match(/([^\/]+)\/$/) || null;
29097
29098     if (path && m)
29099     {
29100         return path + fileName;
29101     }
29102 };
29103
29104 var getFileName = function getFileName(path)
29105 {
29106     if (!path) return "";
29107
29108     var match = path && path.match(/[^\/]+(\?.*)?(#.*)?$/);
29109
29110     return match && match[0] || path;
29111 };
29112
29113
29114 // ************************************************************************************************
29115 }});
29116
29117 /* See license.txt for terms of usage */
29118
29119 FBL.ns(function() { with (FBL) {
29120 // ************************************************************************************************
29121
29122 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
29123
29124 var ElementCache = Firebug.Lite.Cache.Element;
29125
29126 var insertSliceSize = 18;
29127 var insertInterval = 40;
29128
29129 var ignoreVars =
29130 {
29131     "__firebug__": 1,
29132     "eval": 1,
29133
29134     // We are forced to ignore Java-related variables, because
29135     // trying to access them causes browser freeze
29136     "java": 1,
29137     "sun": 1,
29138     "Packages": 1,
29139     "JavaArray": 1,
29140     "JavaMember": 1,
29141     "JavaObject": 1,
29142     "JavaClass": 1,
29143     "JavaPackage": 1,
29144     "_firebug": 1,
29145     "_FirebugConsole": 1,
29146     "_FirebugCommandLine": 1
29147 };
29148
29149 if (Firebug.ignoreFirebugElements)
29150     ignoreVars[Firebug.Lite.Cache.ID] = 1;
29151
29152 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
29153
29154 var memberPanelRep =
29155     isIE6 ?
29156     {"class": "memberLabel $member.type\\Label", href: "javacript:void(0)"}
29157     :
29158     {"class": "memberLabel $member.type\\Label"};
29159
29160 var RowTag =
29161     TR({"class": "memberRow $member.open $member.type\\Row", $hasChildren: "$member.hasChildren", role : 'presentation',
29162         level: "$member.level"},
29163         TD({"class": "memberLabelCell", style: "padding-left: $member.indent\\px", role : 'presentation'},
29164             A(memberPanelRep,
29165                 SPAN({}, "$member.name")
29166             )
29167         ),
29168         TD({"class": "memberValueCell", role : 'presentation'},
29169             TAG("$member.tag", {object: "$member.value"})
29170         )
29171     );
29172
29173 var WatchRowTag =
29174     TR({"class": "watchNewRow", level: 0},
29175         TD({"class": "watchEditCell", colspan: 2},
29176             DIV({"class": "watchEditBox a11yFocusNoTab", role: "button", 'tabindex' : '0',
29177                 'aria-label' : $STR('press enter to add new watch expression')},
29178                     $STR("NewWatch")
29179             )
29180         )
29181     );
29182
29183 var SizerRow =
29184     TR({role : 'presentation'},
29185         TD({width: "30%"}),
29186         TD({width: "70%"})
29187     );
29188
29189 var domTableClass = isIElt8 ? "domTable domTableIE" : "domTable";
29190 var DirTablePlate = domplate(Firebug.Rep,
29191 {
29192     tag:
29193         TABLE({"class": domTableClass, cellpadding: 0, cellspacing: 0, onclick: "$onClick", role :"tree"},
29194             TBODY({role: 'presentation'},
29195                 SizerRow,
29196                 FOR("member", "$object|memberIterator", RowTag)
29197             )
29198         ),
29199
29200     watchTag:
29201         TABLE({"class": domTableClass, cellpadding: 0, cellspacing: 0,
29202                _toggles: "$toggles", _domPanel: "$domPanel", onclick: "$onClick", role : 'tree'},
29203             TBODY({role : 'presentation'},
29204                 SizerRow,
29205                 WatchRowTag
29206             )
29207         ),
29208
29209     tableTag:
29210         TABLE({"class": domTableClass, cellpadding: 0, cellspacing: 0,
29211             _toggles: "$toggles", _domPanel: "$domPanel", onclick: "$onClick", role : 'tree'},
29212             TBODY({role : 'presentation'},
29213                 SizerRow
29214             )
29215         ),
29216
29217     rowTag:
29218         FOR("member", "$members", RowTag),
29219
29220     memberIterator: function(object, level)
29221     {
29222         return getMembers(object, level);
29223     },
29224
29225     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
29226
29227     onClick: function(event)
29228     {
29229         if (!isLeftClick(event))
29230             return;
29231
29232         var target = event.target || event.srcElement;
29233
29234         var row = getAncestorByClass(target, "memberRow");
29235         var label = getAncestorByClass(target, "memberLabel");
29236         if (label && hasClass(row, "hasChildren"))
29237         {
29238             var row = label.parentNode.parentNode;
29239             this.toggleRow(row);
29240         }
29241         else
29242         {
29243             var object = Firebug.getRepObject(target);
29244             if (typeof(object) == "function")
29245             {
29246                 Firebug.chrome.select(object, "script");
29247                 cancelEvent(event);
29248             }
29249             else if (event.detail == 2 && !object)
29250             {
29251                 var panel = row.parentNode.parentNode.domPanel;
29252                 if (panel)
29253                 {
29254                     var rowValue = panel.getRowPropertyValue(row);
29255                     if (typeof(rowValue) == "boolean")
29256                         panel.setPropertyValue(row, !rowValue);
29257                     else
29258                         panel.editProperty(row);
29259
29260                     cancelEvent(event);
29261                 }
29262             }
29263         }
29264
29265         return false;
29266     },
29267
29268     toggleRow: function(row)
29269     {
29270         var level = parseInt(row.getAttribute("level"));
29271         var toggles = row.parentNode.parentNode.toggles;
29272
29273         if (hasClass(row, "opened"))
29274         {
29275             removeClass(row, "opened");
29276
29277             if (toggles)
29278             {
29279                 var path = getPath(row);
29280
29281                 // Remove the path from the toggle tree
29282                 for (var i = 0; i < path.length; ++i)
29283                 {
29284                     if (i == path.length-1)
29285                         delete toggles[path[i]];
29286                     else
29287                         toggles = toggles[path[i]];
29288                 }
29289             }
29290
29291             var rowTag = this.rowTag;
29292             var tbody = row.parentNode;
29293
29294             setTimeout(function()
29295             {
29296                 for (var firstRow = row.nextSibling; firstRow; firstRow = row.nextSibling)
29297                 {
29298                     if (parseInt(firstRow.getAttribute("level")) <= level)
29299                         break;
29300
29301                     tbody.removeChild(firstRow);
29302                 }
29303             }, row.insertTimeout ? row.insertTimeout : 0);
29304         }
29305         else
29306         {
29307             setClass(row, "opened");
29308
29309             if (toggles)
29310             {
29311                 var path = getPath(row);
29312
29313                 // Mark the path in the toggle tree
29314                 for (var i = 0; i < path.length; ++i)
29315                 {
29316                     var name = path[i];
29317                     if (toggles.hasOwnProperty(name))
29318                         toggles = toggles[name];
29319                     else
29320                         toggles = toggles[name] = {};
29321                 }
29322             }
29323
29324             var value = row.lastChild.firstChild.repObject;
29325             var members = getMembers(value, level+1);
29326
29327             var rowTag = this.rowTag;
29328             var lastRow = row;
29329
29330             var delay = 0;
29331             //var setSize = members.length;
29332             //var rowCount = 1;
29333             while (members.length)
29334             {
29335                 with({slice: members.splice(0, insertSliceSize), isLast: !members.length})
29336                 {
29337                     setTimeout(function()
29338                     {
29339                         if (lastRow.parentNode)
29340                         {
29341                             var result = rowTag.insertRows({members: slice}, lastRow);
29342                             lastRow = result[1];
29343                             //dispatch([Firebug.A11yModel], 'onMemberRowSliceAdded', [null, result, rowCount, setSize]);
29344                             //rowCount += insertSliceSize;
29345                         }
29346                         if (isLast)
29347                             row.removeAttribute("insertTimeout");
29348                     }, delay);
29349                 }
29350
29351                 delay += insertInterval;
29352             }
29353
29354             row.insertTimeout = delay;
29355         }
29356     }
29357 });
29358
29359
29360
29361 // ************************************************************************************************
29362
29363 Firebug.DOMBasePanel = function() {};
29364
29365 Firebug.DOMBasePanel.prototype = extend(Firebug.Panel,
29366 {
29367     tag: DirTablePlate.tableTag,
29368
29369     getRealObject: function(object)
29370     {
29371         // TODO: Move this to some global location
29372         // TODO: Unwrapping should be centralized rather than sprinkling it around ad hoc.
29373         // TODO: We might be able to make this check more authoritative with QueryInterface.
29374         if (!object) return object;
29375         if (object.wrappedJSObject) return object.wrappedJSObject;
29376         return object;
29377     },
29378
29379     rebuild: function(update, scrollTop)
29380     {
29381         //dispatch([Firebug.A11yModel], 'onBeforeDomUpdateSelection', [this]);
29382         var members = getMembers(this.selection);
29383         expandMembers(members, this.toggles, 0, 0);
29384
29385         this.showMembers(members, update, scrollTop);
29386
29387         //TODO: xxxpedro statusbar
29388         if (!this.parentPanel)
29389             updateStatusBar(this);
29390     },
29391
29392     showMembers: function(members, update, scrollTop)
29393     {
29394         // If we are still in the midst of inserting rows, cancel all pending
29395         // insertions here - this is a big speedup when stepping in the debugger
29396         if (this.timeouts)
29397         {
29398             for (var i = 0; i < this.timeouts.length; ++i)
29399                 this.context.clearTimeout(this.timeouts[i]);
29400             delete this.timeouts;
29401         }
29402
29403         if (!members.length)
29404             return this.showEmptyMembers();
29405
29406         var panelNode = this.panelNode;
29407         var priorScrollTop = scrollTop == undefined ? panelNode.scrollTop : scrollTop;
29408
29409         // If we are asked to "update" the current view, then build the new table
29410         // offscreen and swap it in when it's done
29411         var offscreen = update && panelNode.firstChild;
29412         var dest = offscreen ? panelNode.ownerDocument : panelNode;
29413
29414         var table = this.tag.replace({domPanel: this, toggles: this.toggles}, dest);
29415         var tbody = table.lastChild;
29416         var rowTag = DirTablePlate.rowTag;
29417
29418         // Insert the first slice immediately
29419         //var slice = members.splice(0, insertSliceSize);
29420         //var result = rowTag.insertRows({members: slice}, tbody.lastChild);
29421
29422         //var setSize = members.length;
29423         //var rowCount = 1;
29424
29425         var panel = this;
29426         var result;
29427
29428         //dispatch([Firebug.A11yModel], 'onMemberRowSliceAdded', [panel, result, rowCount, setSize]);
29429         var timeouts = [];
29430
29431         var delay = 0;
29432
29433         // enable to measure rendering performance
29434         var renderStart = new Date().getTime();
29435         while (members.length)
29436         {
29437             with({slice: members.splice(0, insertSliceSize), isLast: !members.length})
29438             {
29439                 timeouts.push(this.context.setTimeout(function()
29440                 {
29441                     // TODO: xxxpedro can this be a timing error related to the
29442                     // "iteration number" approach insted of "duration time"?
29443                     // avoid error in IE8
29444                     if (!tbody.lastChild) return;
29445
29446                     result = rowTag.insertRows({members: slice}, tbody.lastChild);
29447
29448                     //rowCount += insertSliceSize;
29449                     //dispatch([Firebug.A11yModel], 'onMemberRowSliceAdded', [panel, result, rowCount, setSize]);
29450
29451                     if ((panelNode.scrollHeight+panelNode.offsetHeight) >= priorScrollTop)
29452                         panelNode.scrollTop = priorScrollTop;
29453
29454
29455                     // enable to measure rendering performance
29456                     //if (isLast) alert(new Date().getTime() - renderStart + "ms");
29457
29458
29459                 }, delay));
29460
29461                 delay += insertInterval;
29462             }
29463         }
29464
29465         if (offscreen)
29466         {
29467             timeouts.push(this.context.setTimeout(function()
29468             {
29469                 if (panelNode.firstChild)
29470                     panelNode.replaceChild(table, panelNode.firstChild);
29471                 else
29472                     panelNode.appendChild(table);
29473
29474                 // Scroll back to where we were before
29475                 panelNode.scrollTop = priorScrollTop;
29476             }, delay));
29477         }
29478         else
29479         {
29480             timeouts.push(this.context.setTimeout(function()
29481             {
29482                 panelNode.scrollTop = scrollTop == undefined ? 0 : scrollTop;
29483             }, delay));
29484         }
29485         this.timeouts = timeouts;
29486     },
29487
29488     /*
29489     // new
29490     showMembers: function(members, update, scrollTop)
29491     {
29492         // If we are still in the midst of inserting rows, cancel all pending
29493         // insertions here - this is a big speedup when stepping in the debugger
29494         if (this.timeouts)
29495         {
29496             for (var i = 0; i < this.timeouts.length; ++i)
29497                 this.context.clearTimeout(this.timeouts[i]);
29498             delete this.timeouts;
29499         }
29500
29501         if (!members.length)
29502             return this.showEmptyMembers();
29503
29504         var panelNode = this.panelNode;
29505         var priorScrollTop = scrollTop == undefined ? panelNode.scrollTop : scrollTop;
29506
29507         // If we are asked to "update" the current view, then build the new table
29508         // offscreen and swap it in when it's done
29509         var offscreen = update && panelNode.firstChild;
29510         var dest = offscreen ? panelNode.ownerDocument : panelNode;
29511
29512         var table = this.tag.replace({domPanel: this, toggles: this.toggles}, dest);
29513         var tbody = table.lastChild;
29514         var rowTag = DirTablePlate.rowTag;
29515
29516         // Insert the first slice immediately
29517         //var slice = members.splice(0, insertSliceSize);
29518         //var result = rowTag.insertRows({members: slice}, tbody.lastChild);
29519
29520         //var setSize = members.length;
29521         //var rowCount = 1;
29522
29523         var panel = this;
29524         var result;
29525
29526         //dispatch([Firebug.A11yModel], 'onMemberRowSliceAdded', [panel, result, rowCount, setSize]);
29527         var timeouts = [];
29528
29529         var delay = 0;
29530         var _insertSliceSize = insertSliceSize;
29531         var _insertInterval = insertInterval;
29532
29533         // enable to measure rendering performance
29534         var renderStart = new Date().getTime();
29535         var lastSkip = renderStart, now;
29536
29537         while (members.length)
29538         {
29539             with({slice: members.splice(0, _insertSliceSize), isLast: !members.length})
29540             {
29541                 var _tbody = tbody;
29542                 var _rowTag = rowTag;
29543                 var _panelNode = panelNode;
29544                 var _priorScrollTop = priorScrollTop;
29545
29546                 timeouts.push(this.context.setTimeout(function()
29547                 {
29548                     // TODO: xxxpedro can this be a timing error related to the
29549                     // "iteration number" approach insted of "duration time"?
29550                     // avoid error in IE8
29551                     if (!_tbody.lastChild) return;
29552
29553                     result = _rowTag.insertRows({members: slice}, _tbody.lastChild);
29554
29555                     //rowCount += _insertSliceSize;
29556                     //dispatch([Firebug.A11yModel], 'onMemberRowSliceAdded', [panel, result, rowCount, setSize]);
29557
29558                     if ((_panelNode.scrollHeight + _panelNode.offsetHeight) >= _priorScrollTop)
29559                         _panelNode.scrollTop = _priorScrollTop;
29560
29561
29562                     // enable to measure rendering performance
29563                     //alert("gap: " + (new Date().getTime() - lastSkip));
29564                     //lastSkip = new Date().getTime();
29565
29566                     //if (isLast) alert("new: " + (new Date().getTime() - renderStart) + "ms");
29567
29568                 }, delay));
29569
29570                 delay += _insertInterval;
29571             }
29572         }
29573
29574         if (offscreen)
29575         {
29576             timeouts.push(this.context.setTimeout(function()
29577             {
29578                 if (panelNode.firstChild)
29579                     panelNode.replaceChild(table, panelNode.firstChild);
29580                 else
29581                     panelNode.appendChild(table);
29582
29583                 // Scroll back to where we were before
29584                 panelNode.scrollTop = priorScrollTop;
29585             }, delay));
29586         }
29587         else
29588         {
29589             timeouts.push(this.context.setTimeout(function()
29590             {
29591                 panelNode.scrollTop = scrollTop == undefined ? 0 : scrollTop;
29592             }, delay));
29593         }
29594         this.timeouts = timeouts;
29595     },
29596     /**/
29597
29598     showEmptyMembers: function()
29599     {
29600         FirebugReps.Warning.tag.replace({object: "NoMembersWarning"}, this.panelNode);
29601     },
29602
29603     findPathObject: function(object)
29604     {
29605         var pathIndex = -1;
29606         for (var i = 0; i < this.objectPath.length; ++i)
29607         {
29608             // IE needs === instead of == or otherwise some objects will
29609             // be considered equal to different objects, returning the
29610             // wrong index of the objectPath array
29611             if (this.getPathObject(i) === object)
29612                 return i;
29613         }
29614
29615         return -1;
29616     },
29617
29618     getPathObject: function(index)
29619     {
29620         var object = this.objectPath[index];
29621
29622         if (object instanceof Property)
29623             return object.getObject();
29624         else
29625             return object;
29626     },
29627
29628     getRowObject: function(row)
29629     {
29630         var object = getRowOwnerObject(row);
29631         return object ? object : this.selection;
29632     },
29633
29634     getRowPropertyValue: function(row)
29635     {
29636         var object = this.getRowObject(row);
29637         object = this.getRealObject(object);
29638         if (object)
29639         {
29640             var propName = getRowName(row);
29641
29642             if (object instanceof jsdIStackFrame)
29643                 return Firebug.Debugger.evaluate(propName, this.context);
29644             else
29645                 return object[propName];
29646         }
29647     },
29648     /*
29649     copyProperty: function(row)
29650     {
29651         var value = this.getRowPropertyValue(row);
29652         copyToClipboard(value);
29653     },
29654
29655     editProperty: function(row, editValue)
29656     {
29657         if (hasClass(row, "watchNewRow"))
29658         {
29659             if (this.context.stopped)
29660                 Firebug.Editor.startEditing(row, "");
29661             else if (Firebug.Console.isAlwaysEnabled())  // not stopped in debugger, need command line
29662             {
29663                 if (Firebug.CommandLine.onCommandLineFocus())
29664                     Firebug.Editor.startEditing(row, "");
29665                 else
29666                     row.innerHTML = $STR("warning.Command line blocked?");
29667             }
29668             else
29669                 row.innerHTML = $STR("warning.Console must be enabled");
29670         }
29671         else if (hasClass(row, "watchRow"))
29672             Firebug.Editor.startEditing(row, getRowName(row));
29673         else
29674         {
29675             var object = this.getRowObject(row);
29676             this.context.thisValue = object;
29677
29678             if (!editValue)
29679             {
29680                 var propValue = this.getRowPropertyValue(row);
29681
29682                 var type = typeof(propValue);
29683                 if (type == "undefined" || type == "number" || type == "boolean")
29684                     editValue = propValue;
29685                 else if (type == "string")
29686                     editValue = "\"" + escapeJS(propValue) + "\"";
29687                 else if (propValue == null)
29688                     editValue = "null";
29689                 else if (object instanceof Window || object instanceof jsdIStackFrame)
29690                     editValue = getRowName(row);
29691                 else
29692                     editValue = "this." + getRowName(row);
29693             }
29694
29695
29696             Firebug.Editor.startEditing(row, editValue);
29697         }
29698     },
29699
29700     deleteProperty: function(row)
29701     {
29702         if (hasClass(row, "watchRow"))
29703             this.deleteWatch(row);
29704         else
29705         {
29706             var object = getRowOwnerObject(row);
29707             if (!object)
29708                 object = this.selection;
29709             object = this.getRealObject(object);
29710
29711             if (object)
29712             {
29713                 var name = getRowName(row);
29714                 try
29715                 {
29716                     delete object[name];
29717                 }
29718                 catch (exc)
29719                 {
29720                     return;
29721                 }
29722
29723                 this.rebuild(true);
29724                 this.markChange();
29725             }
29726         }
29727     },
29728
29729     setPropertyValue: function(row, value)  // value must be string
29730     {
29731         if(FBTrace.DBG_DOM)
29732         {
29733             FBTrace.sysout("row: "+row);
29734             FBTrace.sysout("value: "+value+" type "+typeof(value), value);
29735         }
29736
29737         var name = getRowName(row);
29738         if (name == "this")
29739             return;
29740
29741         var object = this.getRowObject(row);
29742         object = this.getRealObject(object);
29743         if (object && !(object instanceof jsdIStackFrame))
29744         {
29745              // unwrappedJSObject.property = unwrappedJSObject
29746              Firebug.CommandLine.evaluate(value, this.context, object, this.context.getGlobalScope(),
29747                  function success(result, context)
29748                  {
29749                      if (FBTrace.DBG_DOM)
29750                          FBTrace.sysout("setPropertyValue evaluate success object["+name+"]="+result+" type "+typeof(result), result);
29751                      object[name] = result;
29752                  },
29753                  function failed(exc, context)
29754                  {
29755                      try
29756                      {
29757                          if (FBTrace.DBG_DOM)
29758                               FBTrace.sysout("setPropertyValue evaluate failed with exc:"+exc+" object["+name+"]="+value+" type "+typeof(value), exc);
29759                          // If the value doesn't parse, then just store it as a string.  Some users will
29760                          // not realize they're supposed to enter a JavaScript expression and just type
29761                          // literal text
29762                          object[name] = String(value);  // unwrappedJSobject.property = string
29763                      }
29764                      catch (exc)
29765                      {
29766                          return;
29767                      }
29768                   }
29769              );
29770         }
29771         else if (this.context.stopped)
29772         {
29773             try
29774             {
29775                 Firebug.CommandLine.evaluate(name+"="+value, this.context);
29776             }
29777             catch (exc)
29778             {
29779                 try
29780                 {
29781                     // See catch block above...
29782                     object[name] = String(value); // unwrappedJSobject.property = string
29783                 }
29784                 catch (exc)
29785                 {
29786                     return;
29787                 }
29788             }
29789         }
29790
29791         this.rebuild(true);
29792         this.markChange();
29793     },
29794
29795     highlightRow: function(row)
29796     {
29797         if (this.highlightedRow)
29798             cancelClassTimed(this.highlightedRow, "jumpHighlight", this.context);
29799
29800         this.highlightedRow = row;
29801
29802         if (row)
29803             setClassTimed(row, "jumpHighlight", this.context);
29804     },/**/
29805
29806     onMouseMove: function(event)
29807     {
29808         var target = event.srcElement || event.target;
29809
29810         var object = getAncestorByClass(target, "objectLink-element");
29811         object = object ? object.repObject : null;
29812
29813         if(object && instanceOf(object, "Element") && object.nodeType == 1)
29814         {
29815             if(object != lastHighlightedObject)
29816             {
29817                 Firebug.Inspector.drawBoxModel(object);
29818                 object = lastHighlightedObject;
29819             }
29820         }
29821         else
29822             Firebug.Inspector.hideBoxModel();
29823
29824     },
29825
29826     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
29827     // extends Panel
29828
29829     create: function()
29830     {
29831         // TODO: xxxpedro
29832         this.context = Firebug.browser;
29833
29834         this.objectPath = [];
29835         this.propertyPath = [];
29836         this.viewPath = [];
29837         this.pathIndex = -1;
29838         this.toggles = {};
29839
29840         Firebug.Panel.create.apply(this, arguments);
29841
29842         this.panelNode.style.padding = "0 1px";
29843     },
29844
29845     initialize: function(){
29846         Firebug.Panel.initialize.apply(this, arguments);
29847
29848         addEvent(this.panelNode, "mousemove", this.onMouseMove);
29849     },
29850
29851     shutdown: function()
29852     {
29853         removeEvent(this.panelNode, "mousemove", this.onMouseMove);
29854
29855         Firebug.Panel.shutdown.apply(this, arguments);
29856     },
29857
29858     /*
29859     destroy: function(state)
29860     {
29861         var view = this.viewPath[this.pathIndex];
29862         if (view && this.panelNode.scrollTop)
29863             view.scrollTop = this.panelNode.scrollTop;
29864
29865         if (this.pathIndex)
29866             state.pathIndex = this.pathIndex;
29867         if (this.viewPath)
29868             state.viewPath = this.viewPath;
29869         if (this.propertyPath)
29870             state.propertyPath = this.propertyPath;
29871
29872         if (this.propertyPath.length > 0 && !this.propertyPath[1])
29873             state.firstSelection = persistObject(this.getPathObject(1), this.context);
29874
29875         Firebug.Panel.destroy.apply(this, arguments);
29876     },
29877     /**/
29878
29879     ishow: function(state)
29880     {
29881         if (this.context.loaded && !this.selection)
29882         {
29883             if (!state)
29884             {
29885                 this.select(null);
29886                 return;
29887             }
29888             if (state.viewPath)
29889                 this.viewPath = state.viewPath;
29890             if (state.propertyPath)
29891                 this.propertyPath = state.propertyPath;
29892
29893             var defaultObject = this.getDefaultSelection(this.context);
29894             var selectObject = defaultObject;
29895
29896             if (state.firstSelection)
29897             {
29898                 var restored = state.firstSelection(this.context);
29899                 if (restored)
29900                 {
29901                     selectObject = restored;
29902                     this.objectPath = [defaultObject, restored];
29903                 }
29904                 else
29905                     this.objectPath = [defaultObject];
29906             }
29907             else
29908                 this.objectPath = [defaultObject];
29909
29910             if (this.propertyPath.length > 1)
29911             {
29912                 for (var i = 1; i < this.propertyPath.length; ++i)
29913                 {
29914                     var name = this.propertyPath[i];
29915                     if (!name)
29916                         continue;
29917
29918                     var object = selectObject;
29919                     try
29920                     {
29921                         selectObject = object[name];
29922                     }
29923                     catch (exc)
29924                     {
29925                         selectObject = null;
29926                     }
29927
29928                     if (selectObject)
29929                     {
29930                         this.objectPath.push(new Property(object, name));
29931                     }
29932                     else
29933                     {
29934                         // If we can't access a property, just stop
29935                         this.viewPath.splice(i);
29936                         this.propertyPath.splice(i);
29937                         this.objectPath.splice(i);
29938                         selectObject = this.getPathObject(this.objectPath.length-1);
29939                         break;
29940                     }
29941                 }
29942             }
29943
29944             var selection = state.pathIndex <= this.objectPath.length-1
29945                 ? this.getPathObject(state.pathIndex)
29946                 : this.getPathObject(this.objectPath.length-1);
29947
29948             this.select(selection);
29949         }
29950     },
29951     /*
29952     hide: function()
29953     {
29954         var view = this.viewPath[this.pathIndex];
29955         if (view && this.panelNode.scrollTop)
29956             view.scrollTop = this.panelNode.scrollTop;
29957     },
29958     /**/
29959
29960     supportsObject: function(object)
29961     {
29962         if (object == null)
29963             return 1000;
29964
29965         if (typeof(object) == "undefined")
29966             return 1000;
29967         else if (object instanceof SourceLink)
29968             return 0;
29969         else
29970             return 1; // just agree to support everything but not agressively.
29971     },
29972
29973     refresh: function()
29974     {
29975         this.rebuild(true);
29976     },
29977
29978     updateSelection: function(object)
29979     {
29980         var previousIndex = this.pathIndex;
29981         var previousView = previousIndex == -1 ? null : this.viewPath[previousIndex];
29982
29983         var newPath = this.pathToAppend;
29984         delete this.pathToAppend;
29985
29986         var pathIndex = this.findPathObject(object);
29987         if (newPath || pathIndex == -1)
29988         {
29989             this.toggles = {};
29990
29991             if (newPath)
29992             {
29993                 // Remove everything after the point where we are inserting, so we
29994                 // essentially replace it with the new path
29995                 if (previousView)
29996                 {
29997                     if (this.panelNode.scrollTop)
29998                         previousView.scrollTop = this.panelNode.scrollTop;
29999
30000                     var start = previousIndex + 1,
30001                         // Opera needs the length argument in splice(), otherwise
30002                         // it will consider that only one element should be removed
30003                         length = this.objectPath.length - start;
30004
30005                     this.objectPath.splice(start, length);
30006                     this.propertyPath.splice(start, length);
30007                     this.viewPath.splice(start, length);
30008                 }
30009
30010                 var value = this.getPathObject(previousIndex);
30011                 if (!value)
30012                 {
30013                     if (FBTrace.DBG_ERRORS)
30014                         FBTrace.sysout("dom.updateSelection no pathObject for "+previousIndex+"\n");
30015                     return;
30016                 }
30017
30018                 for (var i = 0, length = newPath.length; i < length; ++i)
30019                 {
30020                     var name = newPath[i];
30021                     var object = value;
30022                     try
30023                     {
30024                         value = value[name];
30025                     }
30026                     catch(exc)
30027                     {
30028                         if (FBTrace.DBG_ERRORS)
30029                                 FBTrace.sysout("dom.updateSelection FAILS at path_i="+i+" for name:"+name+"\n");
30030                         return;
30031                     }
30032
30033                     ++this.pathIndex;
30034                     this.objectPath.push(new Property(object, name));
30035                     this.propertyPath.push(name);
30036                     this.viewPath.push({toggles: this.toggles, scrollTop: 0});
30037                 }
30038             }
30039             else
30040             {
30041                 this.toggles = {};
30042
30043                 var win = Firebug.browser.window;
30044                 //var win = this.context.getGlobalScope();
30045                 if (object === win)
30046                 {
30047                     this.pathIndex = 0;
30048                     this.objectPath = [win];
30049                     this.propertyPath = [null];
30050                     this.viewPath = [{toggles: this.toggles, scrollTop: 0}];
30051                 }
30052                 else
30053                 {
30054                     this.pathIndex = 1;
30055                     this.objectPath = [win, object];
30056                     this.propertyPath = [null, null];
30057                     this.viewPath = [
30058                         {toggles: {}, scrollTop: 0},
30059                         {toggles: this.toggles, scrollTop: 0}
30060                     ];
30061                 }
30062             }
30063
30064             this.panelNode.scrollTop = 0;
30065             this.rebuild();
30066         }
30067         else
30068         {
30069             this.pathIndex = pathIndex;
30070
30071             var view = this.viewPath[pathIndex];
30072             this.toggles = view.toggles;
30073
30074             // Persist the current scroll location
30075             if (previousView && this.panelNode.scrollTop)
30076                 previousView.scrollTop = this.panelNode.scrollTop;
30077
30078             this.rebuild(false, view.scrollTop);
30079         }
30080     },
30081
30082     getObjectPath: function(object)
30083     {
30084         return this.objectPath;
30085     },
30086
30087     getDefaultSelection: function()
30088     {
30089         return Firebug.browser.window;
30090         //return this.context.getGlobalScope();
30091     }/*,
30092
30093     updateOption: function(name, value)
30094     {
30095         const optionMap = {showUserProps: 1, showUserFuncs: 1, showDOMProps: 1,
30096             showDOMFuncs: 1, showDOMConstants: 1};
30097         if ( optionMap.hasOwnProperty(name) )
30098             this.rebuild(true);
30099     },
30100
30101     getOptionsMenuItems: function()
30102     {
30103         return [
30104             optionMenu("ShowUserProps", "showUserProps"),
30105             optionMenu("ShowUserFuncs", "showUserFuncs"),
30106             optionMenu("ShowDOMProps", "showDOMProps"),
30107             optionMenu("ShowDOMFuncs", "showDOMFuncs"),
30108             optionMenu("ShowDOMConstants", "showDOMConstants"),
30109             "-",
30110             {label: "Refresh", command: bindFixed(this.rebuild, this, true) }
30111         ];
30112     },
30113
30114     getContextMenuItems: function(object, target)
30115     {
30116         var row = getAncestorByClass(target, "memberRow");
30117
30118         var items = [];
30119
30120         if (row)
30121         {
30122             var rowName = getRowName(row);
30123             var rowObject = this.getRowObject(row);
30124             var rowValue = this.getRowPropertyValue(row);
30125
30126             var isWatch = hasClass(row, "watchRow");
30127             var isStackFrame = rowObject instanceof jsdIStackFrame;
30128
30129             if (typeof(rowValue) == "string" || typeof(rowValue) == "number")
30130             {
30131                 // Functions already have a copy item in their context menu
30132                 items.push(
30133                     "-",
30134                     {label: "CopyValue",
30135                         command: bindFixed(this.copyProperty, this, row) }
30136                 );
30137             }
30138
30139             items.push(
30140                 "-",
30141                 {label: isWatch ? "EditWatch" : (isStackFrame ? "EditVariable" : "EditProperty"),
30142                     command: bindFixed(this.editProperty, this, row) }
30143             );
30144
30145             if (isWatch || (!isStackFrame && !isDOMMember(rowObject, rowName)))
30146             {
30147                 items.push(
30148                     {label: isWatch ? "DeleteWatch" : "DeleteProperty",
30149                         command: bindFixed(this.deleteProperty, this, row) }
30150                 );
30151             }
30152         }
30153
30154         items.push(
30155             "-",
30156             {label: "Refresh", command: bindFixed(this.rebuild, this, true) }
30157         );
30158
30159         return items;
30160     },
30161
30162     getEditor: function(target, value)
30163     {
30164         if (!this.editor)
30165             this.editor = new DOMEditor(this.document);
30166
30167         return this.editor;
30168     }/**/
30169 });
30170
30171 // ************************************************************************************************
30172
30173 // TODO: xxxpedro statusbar
30174 var updateStatusBar = function(panel)
30175 {
30176     var path = panel.propertyPath;
30177     var index = panel.pathIndex;
30178
30179     var r = [];
30180
30181     for (var i=0, l=path.length; i<l; i++)
30182     {
30183         r.push(i==index ? '<a class="fbHover fbButton fbBtnSelected" ' : '<a class="fbHover fbButton" ');
30184         r.push('pathIndex=');
30185         r.push(i);
30186
30187         if(isIE6)
30188             r.push(' href="javascript:void(0)"');
30189
30190         r.push('>');
30191         r.push(i==0 ? "window" : path[i] || "Object");
30192         r.push('</a>');
30193
30194         if(i < l-1)
30195             r.push('<span class="fbStatusSeparator">&gt;</span>');
30196     }
30197     panel.statusBarNode.innerHTML = r.join("");
30198 };
30199
30200
30201 var DOMMainPanel = Firebug.DOMPanel = function () {};
30202
30203 Firebug.DOMPanel.DirTable = DirTablePlate;
30204
30205 DOMMainPanel.prototype = extend(Firebug.DOMBasePanel.prototype,
30206 {
30207     onClickStatusBar: function(event)
30208     {
30209         var target = event.srcElement || event.target;
30210         var element = getAncestorByClass(target, "fbHover");
30211
30212         if(element)
30213         {
30214             var pathIndex = element.getAttribute("pathIndex");
30215
30216             if(pathIndex)
30217             {
30218                 this.select(this.getPathObject(pathIndex));
30219             }
30220         }
30221     },
30222
30223     selectRow: function(row, target)
30224     {
30225         if (!target)
30226             target = row.lastChild.firstChild;
30227
30228         if (!target || !target.repObject)
30229             return;
30230
30231         this.pathToAppend = getPath(row);
30232
30233         // If the object is inside an array, look up its index
30234         var valueBox = row.lastChild.firstChild;
30235         if (hasClass(valueBox, "objectBox-array"))
30236         {
30237             var arrayIndex = FirebugReps.Arr.getItemIndex(target);
30238             this.pathToAppend.push(arrayIndex);
30239         }
30240
30241         // Make sure we get a fresh status path for the object, since otherwise
30242         // it might find the object in the existing path and not refresh it
30243         //Firebug.chrome.clearStatusPath();
30244
30245         this.select(target.repObject, true);
30246     },
30247
30248     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30249
30250     onClick: function(event)
30251     {
30252         var target = event.srcElement || event.target;
30253         var repNode = Firebug.getRepNode(target);
30254         if (repNode)
30255         {
30256             var row = getAncestorByClass(target, "memberRow");
30257             if (row)
30258             {
30259                 this.selectRow(row, repNode);
30260                 cancelEvent(event);
30261             }
30262         }
30263     },
30264
30265     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30266     // extends Panel
30267
30268     name: "DOM",
30269     title: "DOM",
30270     searchable: true,
30271     statusSeparator: ">",
30272
30273     options: {
30274         hasToolButtons: true,
30275         hasStatusBar: true
30276     },
30277
30278     create: function()
30279     {
30280         Firebug.DOMBasePanel.prototype.create.apply(this, arguments);
30281
30282         this.onClick = bind(this.onClick, this);
30283
30284         //TODO: xxxpedro
30285         this.onClickStatusBar = bind(this.onClickStatusBar, this);
30286
30287         this.panelNode.style.padding = "0 1px";
30288     },
30289
30290     initialize: function(oldPanelNode)
30291     {
30292         //this.panelNode.addEventListener("click", this.onClick, false);
30293         //dispatch([Firebug.A11yModel], 'onInitializeNode', [this, 'console']);
30294
30295         Firebug.DOMBasePanel.prototype.initialize.apply(this, arguments);
30296
30297         addEvent(this.panelNode, "click", this.onClick);
30298
30299         // TODO: xxxpedro dom
30300         this.ishow();
30301
30302         //TODO: xxxpedro
30303         addEvent(this.statusBarNode, "click", this.onClickStatusBar);
30304     },
30305
30306     shutdown: function()
30307     {
30308         //this.panelNode.removeEventListener("click", this.onClick, false);
30309         //dispatch([Firebug.A11yModel], 'onDestroyNode', [this, 'console']);
30310
30311         removeEvent(this.panelNode, "click", this.onClick);
30312
30313         Firebug.DOMBasePanel.prototype.shutdown.apply(this, arguments);
30314     }/*,
30315
30316     search: function(text, reverse)
30317     {
30318         if (!text)
30319         {
30320             delete this.currentSearch;
30321             this.highlightRow(null);
30322             return false;
30323         }
30324
30325         var row;
30326         if (this.currentSearch && text == this.currentSearch.text)
30327             row = this.currentSearch.findNext(true, undefined, reverse, Firebug.searchCaseSensitive);
30328         else
30329         {
30330             function findRow(node) { return getAncestorByClass(node, "memberRow"); }
30331             this.currentSearch = new TextSearch(this.panelNode, findRow);
30332             row = this.currentSearch.find(text, reverse, Firebug.searchCaseSensitive);
30333         }
30334
30335         if (row)
30336         {
30337             var sel = this.document.defaultView.getSelection();
30338             sel.removeAllRanges();
30339             sel.addRange(this.currentSearch.range);
30340
30341             scrollIntoCenterView(row, this.panelNode);
30342
30343             this.highlightRow(row);
30344             dispatch([Firebug.A11yModel], 'onDomSearchMatchFound', [this, text, row]);
30345             return true;
30346         }
30347         else
30348         {
30349             dispatch([Firebug.A11yModel], 'onDomSearchMatchFound', [this, text, null]);
30350             return false;
30351         }
30352     }/**/
30353 });
30354
30355 Firebug.registerPanel(DOMMainPanel);
30356
30357
30358 // ************************************************************************************************
30359
30360
30361
30362 // ************************************************************************************************
30363 // Local Helpers
30364
30365 var getMembers = function getMembers(object, level)  // we expect object to be user-level object wrapped in security blanket
30366 {
30367     if (!level)
30368         level = 0;
30369
30370     var ordinals = [], userProps = [], userClasses = [], userFuncs = [],
30371         domProps = [], domFuncs = [], domConstants = [];
30372
30373     try
30374     {
30375         var domMembers = getDOMMembers(object);
30376         //var domMembers = {}; // TODO: xxxpedro
30377         //var domConstantMap = {};  // TODO: xxxpedro
30378
30379         if (object.wrappedJSObject)
30380             var insecureObject = object.wrappedJSObject;
30381         else
30382             var insecureObject = object;
30383
30384         // IE function prototype is not listed in (for..in)
30385         if (isIE && isFunction(object))
30386             addMember("user", userProps, "prototype", object.prototype, level);
30387
30388         for (var name in insecureObject)  // enumeration is safe
30389         {
30390             if (ignoreVars[name] == 1)  // javascript.options.strict says ignoreVars is undefined.
30391                 continue;
30392
30393             var val;
30394             try
30395             {
30396                 val = insecureObject[name];  // getter is safe
30397             }
30398             catch (exc)
30399             {
30400                 // Sometimes we get exceptions trying to access certain members
30401                 if (FBTrace.DBG_ERRORS && FBTrace.DBG_DOM)
30402                     FBTrace.sysout("dom.getMembers cannot access "+name, exc);
30403             }
30404
30405             var ordinal = parseInt(name);
30406             if (ordinal || ordinal == 0)
30407             {
30408                 addMember("ordinal", ordinals, name, val, level);
30409             }
30410             else if (isFunction(val))
30411             {
30412                 if (isClassFunction(val) && !(name in domMembers))
30413                     addMember("userClass", userClasses, name, val, level);
30414                 else if (name in domMembers)
30415                     addMember("domFunction", domFuncs, name, val, level, domMembers[name]);
30416                 else
30417                     addMember("userFunction", userFuncs, name, val, level);
30418             }
30419             else
30420             {
30421                 //TODO: xxxpedro
30422                 /*
30423                 var getterFunction = insecureObject.__lookupGetter__(name),
30424                     setterFunction = insecureObject.__lookupSetter__(name),
30425                     prefix = "";
30426
30427                 if(getterFunction && !setterFunction)
30428                     prefix = "get ";
30429                 /**/
30430
30431                 var prefix = "";
30432
30433                 if (name in domMembers && !(name in domConstantMap))
30434                     addMember("dom", domProps, (prefix+name), val, level, domMembers[name]);
30435                 else if (name in domConstantMap)
30436                     addMember("dom", domConstants, (prefix+name), val, level);
30437                 else
30438                     addMember("user", userProps, (prefix+name), val, level);
30439             }
30440         }
30441     }
30442     catch (exc)
30443     {
30444         // Sometimes we get exceptions just from trying to iterate the members
30445         // of certain objects, like StorageList, but don't let that gum up the works
30446         throw exc;
30447         if (FBTrace.DBG_ERRORS && FBTrace.DBG_DOM)
30448             FBTrace.sysout("dom.getMembers FAILS: ", exc);
30449         //throw exc;
30450     }
30451
30452     function sortName(a, b) { return a.name > b.name ? 1 : -1; }
30453     function sortOrder(a, b) { return a.order > b.order ? 1 : -1; }
30454
30455     var members = [];
30456
30457     members.push.apply(members, ordinals);
30458
30459     Firebug.showUserProps = true; // TODO: xxxpedro
30460     Firebug.showUserFuncs = true; // TODO: xxxpedro
30461     Firebug.showDOMProps = true;
30462     Firebug.showDOMFuncs = true;
30463     Firebug.showDOMConstants = true;
30464
30465     if (Firebug.showUserProps)
30466     {
30467         userProps.sort(sortName);
30468         members.push.apply(members, userProps);
30469     }
30470
30471     if (Firebug.showUserFuncs)
30472     {
30473         userClasses.sort(sortName);
30474         members.push.apply(members, userClasses);
30475
30476         userFuncs.sort(sortName);
30477         members.push.apply(members, userFuncs);
30478     }
30479
30480     if (Firebug.showDOMProps)
30481     {
30482         domProps.sort(sortName);
30483         members.push.apply(members, domProps);
30484     }
30485
30486     if (Firebug.showDOMFuncs)
30487     {
30488         domFuncs.sort(sortName);
30489         members.push.apply(members, domFuncs);
30490     }
30491
30492     if (Firebug.showDOMConstants)
30493         members.push.apply(members, domConstants);
30494
30495     return members;
30496 };
30497
30498 function expandMembers(members, toggles, offset, level)  // recursion starts with offset=0, level=0
30499 {
30500     var expanded = 0;
30501     for (var i = offset; i < members.length; ++i)
30502     {
30503         var member = members[i];
30504         if (member.level > level)
30505             break;
30506
30507         if ( toggles.hasOwnProperty(member.name) )
30508         {
30509             member.open = "opened";  // member.level <= level && member.name in toggles.
30510
30511             var newMembers = getMembers(member.value, level+1);  // sets newMembers.level to level+1
30512
30513             var args = [i+1, 0];
30514             args.push.apply(args, newMembers);
30515             members.splice.apply(members, args);
30516
30517             /*
30518             if (FBTrace.DBG_DOM)
30519             {
30520                 FBTrace.sysout("expandMembers member.name", member.name);
30521                 FBTrace.sysout("expandMembers toggles", toggles);
30522                 FBTrace.sysout("expandMembers toggles[member.name]", toggles[member.name]);
30523                 FBTrace.sysout("dom.expandedMembers level: "+level+" member", member);
30524             }
30525             /**/
30526
30527             expanded += newMembers.length;
30528             i += newMembers.length + expandMembers(members, toggles[member.name], i+1, level+1);
30529         }
30530     }
30531
30532     return expanded;
30533 }
30534
30535 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30536
30537
30538 function isClassFunction(fn)
30539 {
30540     try
30541     {
30542         for (var name in fn.prototype)
30543             return true;
30544     } catch (exc) {}
30545     return false;
30546 }
30547
30548 // FIXME: xxxpedro This function is already defined in Lib. If we keep this definition here, it
30549 // will crash IE9 when not running the IE Developer Tool with JavaScript Debugging enabled!!!
30550 // Check if this function is in fact defined in Firebug for Firefox. If so, we should remove
30551 // this from here. The only difference of this function is the IE hack to show up the prototype
30552 // of functions, but Firebug no longer shows the prototype for simple functions.
30553 //var hasProperties = function hasProperties(ob)
30554 //{
30555 //    try
30556 //    {
30557 //        for (var name in ob)
30558 //            return true;
30559 //    } catch (exc) {}
30560 //
30561 //    // IE function prototype is not listed in (for..in)
30562 //    if (isFunction(ob)) return true;
30563 //
30564 //    return false;
30565 //};
30566
30567 FBL.ErrorCopy = function(message)
30568 {
30569     this.message = message;
30570 };
30571
30572 var addMember = function addMember(type, props, name, value, level, order)
30573 {
30574     var rep = Firebug.getRep(value);    // do this first in case a call to instanceof reveals contents
30575     var tag = rep.shortTag ? rep.shortTag : rep.tag;
30576
30577     var ErrorCopy = function(){}; //TODO: xxxpedro
30578
30579     var valueType = typeof(value);
30580     var hasChildren = hasProperties(value) && !(value instanceof ErrorCopy) &&
30581         (isFunction(value) || (valueType == "object" && value != null)
30582         || (valueType == "string" && value.length > Firebug.stringCropLength));
30583
30584     props.push({
30585         name: name,
30586         value: value,
30587         type: type,
30588         rowClass: "memberRow-"+type,
30589         open: "",
30590         order: order,
30591         level: level,
30592         indent: level*16,
30593         hasChildren: hasChildren,
30594         tag: tag
30595     });
30596 };
30597
30598 var getWatchRowIndex = function getWatchRowIndex(row)
30599 {
30600     var index = -1;
30601     for (; row && hasClass(row, "watchRow"); row = row.previousSibling)
30602         ++index;
30603     return index;
30604 };
30605
30606 var getRowName = function getRowName(row)
30607 {
30608     var node = row.firstChild;
30609     return node.textContent ? node.textContent : node.innerText;
30610 };
30611
30612 var getRowValue = function getRowValue(row)
30613 {
30614     return row.lastChild.firstChild.repObject;
30615 };
30616
30617 var getRowOwnerObject = function getRowOwnerObject(row)
30618 {
30619     var parentRow = getParentRow(row);
30620     if (parentRow)
30621         return getRowValue(parentRow);
30622 };
30623
30624 var getParentRow = function getParentRow(row)
30625 {
30626     var level = parseInt(row.getAttribute("level"))-1;
30627     for (row = row.previousSibling; row; row = row.previousSibling)
30628     {
30629         if (parseInt(row.getAttribute("level")) == level)
30630             return row;
30631     }
30632 };
30633
30634 var getPath = function getPath(row)
30635 {
30636     var name = getRowName(row);
30637     var path = [name];
30638
30639     var level = parseInt(row.getAttribute("level"))-1;
30640     for (row = row.previousSibling; row; row = row.previousSibling)
30641     {
30642         if (parseInt(row.getAttribute("level")) == level)
30643         {
30644             var name = getRowName(row);
30645             path.splice(0, 0, name);
30646
30647             --level;
30648         }
30649     }
30650
30651     return path;
30652 };
30653
30654 // ************************************************************************************************
30655
30656
30657 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30658 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30659 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30660
30661
30662 // ************************************************************************************************
30663 // DOM Module
30664
30665 Firebug.DOM = extend(Firebug.Module,
30666 {
30667     getPanel: function()
30668     {
30669         return Firebug.chrome ? Firebug.chrome.getPanel("DOM") : null;
30670     }
30671 });
30672
30673 Firebug.registerModule(Firebug.DOM);
30674
30675
30676 // ************************************************************************************************
30677 // DOM Panel
30678
30679 var lastHighlightedObject;
30680
30681 function DOMSidePanel(){};
30682
30683 DOMSidePanel.prototype = extend(Firebug.DOMBasePanel.prototype,
30684 {
30685     selectRow: function(row, target)
30686     {
30687         if (!target)
30688             target = row.lastChild.firstChild;
30689
30690         if (!target || !target.repObject)
30691             return;
30692
30693         this.pathToAppend = getPath(row);
30694
30695         // If the object is inside an array, look up its index
30696         var valueBox = row.lastChild.firstChild;
30697         if (hasClass(valueBox, "objectBox-array"))
30698         {
30699             var arrayIndex = FirebugReps.Arr.getItemIndex(target);
30700             this.pathToAppend.push(arrayIndex);
30701         }
30702
30703         // Make sure we get a fresh status path for the object, since otherwise
30704         // it might find the object in the existing path and not refresh it
30705         //Firebug.chrome.clearStatusPath();
30706
30707         var object = target.repObject;
30708
30709         if (instanceOf(object, "Element"))
30710         {
30711             Firebug.HTML.selectTreeNode(ElementCache(object));
30712         }
30713         else
30714         {
30715             Firebug.chrome.selectPanel("DOM");
30716             Firebug.chrome.getPanel("DOM").select(object, true);
30717         }
30718     },
30719
30720     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30721
30722     onClick: function(event)
30723     {
30724         /*
30725         var target = event.srcElement || event.target;
30726
30727         var object = getAncestorByClass(target, "objectLink");
30728         object = object ? object.repObject : null;
30729
30730         if(!object) return;
30731
30732         if (instanceOf(object, "Element"))
30733         {
30734             Firebug.HTML.selectTreeNode(ElementCache(object));
30735         }
30736         else
30737         {
30738             Firebug.chrome.selectPanel("DOM");
30739             Firebug.chrome.getPanel("DOM").select(object, true);
30740         }
30741         /**/
30742
30743
30744         var target = event.srcElement || event.target;
30745         var repNode = Firebug.getRepNode(target);
30746         if (repNode)
30747         {
30748             var row = getAncestorByClass(target, "memberRow");
30749             if (row)
30750             {
30751                 this.selectRow(row, repNode);
30752                 cancelEvent(event);
30753             }
30754         }
30755         /**/
30756     },
30757
30758     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30759     // extends Panel
30760
30761     name: "DOMSidePanel",
30762     parentPanel: "HTML",
30763     title: "DOM",
30764
30765     options: {
30766         hasToolButtons: true
30767     },
30768
30769     isInitialized: false,
30770
30771     create: function()
30772     {
30773         Firebug.DOMBasePanel.prototype.create.apply(this, arguments);
30774
30775         this.onClick = bind(this.onClick, this);
30776     },
30777
30778     initialize: function(){
30779         Firebug.DOMBasePanel.prototype.initialize.apply(this, arguments);
30780
30781         addEvent(this.panelNode, "click", this.onClick);
30782
30783         // TODO: xxxpedro css2
30784         var selection = ElementCache.get(Firebug.context.persistedState.selectedHTMLElementId);
30785         if (selection)
30786             this.select(selection, true);
30787     },
30788
30789     shutdown: function()
30790     {
30791         removeEvent(this.panelNode, "click", this.onClick);
30792
30793         Firebug.DOMBasePanel.prototype.shutdown.apply(this, arguments);
30794     },
30795
30796     reattach: function(oldChrome)
30797     {
30798         //this.isInitialized = oldChrome.getPanel("DOM").isInitialized;
30799         this.toggles = oldChrome.getPanel("DOMSidePanel").toggles;
30800     }
30801
30802 });
30803
30804 Firebug.registerPanel(DOMSidePanel);
30805
30806
30807 // ************************************************************************************************
30808 }});
30809
30810 /* See license.txt for terms of usage */
30811
30812 FBL.FBTrace = {};
30813
30814 (function() {
30815 // ************************************************************************************************
30816
30817 var traceOptions = {
30818     DBG_TIMESTAMP: 1,
30819     DBG_INITIALIZE: 1,
30820     DBG_CHROME: 1,
30821     DBG_ERRORS: 1,
30822     DBG_DISPATCH: 1,
30823     DBG_CSS: 1
30824 };
30825
30826 this.module = null;
30827
30828 this.initialize = function()
30829 {
30830     if (!this.messageQueue)
30831         this.messageQueue = [];
30832
30833     for (var name in traceOptions)
30834         this[name] = traceOptions[name];
30835 };
30836
30837 // ************************************************************************************************
30838 // FBTrace API
30839
30840 this.sysout = function()
30841 {
30842     return this.logFormatted(arguments, "");
30843 };
30844
30845 this.dumpProperties = function(title, object)
30846 {
30847     return this.logFormatted("dumpProperties() not supported.", "warning");
30848 };
30849
30850 this.dumpStack = function()
30851 {
30852     return this.logFormatted("dumpStack() not supported.", "warning");
30853 };
30854
30855 this.flush = function(module)
30856 {
30857     this.module = module;
30858
30859     var queue = this.messageQueue;
30860     this.messageQueue = [];
30861
30862     for (var i = 0; i < queue.length; ++i)
30863         this.writeMessage(queue[i][0], queue[i][1], queue[i][2]);
30864 };
30865
30866 this.getPanel = function()
30867 {
30868     return this.module ? this.module.getPanel() : null;
30869 };
30870
30871 //*************************************************************************************************
30872
30873 this.logFormatted = function(objects, className)
30874 {
30875     var html = this.DBG_TIMESTAMP ? [getTimestamp(), " | "] : [];
30876     var length = objects.length;
30877
30878     for (var i = 0; i < length; ++i)
30879     {
30880         appendText(" ", html);
30881
30882         var object = objects[i];
30883
30884         if (i == 0)
30885         {
30886             html.push("<b>");
30887             appendText(object, html);
30888             html.push("</b>");
30889         }
30890         else
30891             appendText(object, html);
30892     }
30893
30894     return this.logRow(html, className);
30895 };
30896
30897 this.logRow = function(message, className)
30898 {
30899     var panel = this.getPanel();
30900
30901     if (panel && panel.panelNode)
30902         this.writeMessage(message, className);
30903     else
30904     {
30905         this.messageQueue.push([message, className]);
30906     }
30907
30908     return this.LOG_COMMAND;
30909 };
30910
30911 this.writeMessage = function(message, className)
30912 {
30913     var container = this.getPanel().containerNode;
30914     var isScrolledToBottom =
30915         container.scrollTop + container.offsetHeight >= container.scrollHeight;
30916
30917     this.writeRow.call(this, message, className);
30918
30919     if (isScrolledToBottom)
30920         container.scrollTop = container.scrollHeight - container.offsetHeight;
30921 };
30922
30923 this.appendRow = function(row)
30924 {
30925     var container = this.getPanel().panelNode;
30926     container.appendChild(row);
30927 };
30928
30929 this.writeRow = function(message, className)
30930 {
30931     var row = this.getPanel().panelNode.ownerDocument.createElement("div");
30932     row.className = "logRow" + (className ? " logRow-"+className : "");
30933     row.innerHTML = message.join("");
30934     this.appendRow(row);
30935 };
30936
30937 //*************************************************************************************************
30938
30939 function appendText(object, html)
30940 {
30941     html.push(escapeHTML(objectToString(object)));
30942 };
30943
30944 function getTimestamp()
30945 {
30946     var now = new Date();
30947     var ms = "" + (now.getMilliseconds() / 1000).toFixed(3);
30948     ms = ms.substr(2);
30949
30950     return now.toLocaleTimeString() + "." + ms;
30951 };
30952
30953 //*************************************************************************************************
30954
30955 var HTMLtoEntity =
30956 {
30957     "<": "&lt;",
30958     ">": "&gt;",
30959     "&": "&amp;",
30960     "'": "&#39;",
30961     '"': "&quot;"
30962 };
30963
30964 function replaceChars(ch)
30965 {
30966     return HTMLtoEntity[ch];
30967 };
30968
30969 function escapeHTML(value)
30970 {
30971     return (value+"").replace(/[<>&"']/g, replaceChars);
30972 };
30973
30974 //*************************************************************************************************
30975
30976 function objectToString(object)
30977 {
30978     try
30979     {
30980         return object+"";
30981     }
30982     catch (exc)
30983     {
30984         return null;
30985     }
30986 };
30987
30988 // ************************************************************************************************
30989 }).apply(FBL.FBTrace);
30990
30991 /* See license.txt for terms of usage */
30992
30993 FBL.ns(function() { with (FBL) {
30994 // ************************************************************************************************
30995
30996 // If application isn't in trace mode, the FBTrace panel won't be loaded
30997 if (!Env.Options.enableTrace) return;
30998
30999 // ************************************************************************************************
31000 // FBTrace Module
31001
31002 Firebug.Trace = extend(Firebug.Module,
31003 {
31004     getPanel: function()
31005     {
31006         return Firebug.chrome ? Firebug.chrome.getPanel("Trace") : null;
31007     },
31008
31009     clear: function()
31010     {
31011         this.getPanel().panelNode.innerHTML = "";
31012     }
31013 });
31014
31015 Firebug.registerModule(Firebug.Trace);
31016
31017
31018 // ************************************************************************************************
31019 // FBTrace Panel
31020
31021 function TracePanel(){};
31022
31023 TracePanel.prototype = extend(Firebug.Panel,
31024 {
31025     name: "Trace",
31026     title: "Trace",
31027
31028     options: {
31029         hasToolButtons: true,
31030         innerHTMLSync: true
31031     },
31032
31033     create: function(){
31034         Firebug.Panel.create.apply(this, arguments);
31035
31036         this.clearButton = new Button({
31037             caption: "Clear",
31038             title: "Clear FBTrace logs",
31039             owner: Firebug.Trace,
31040             onClick: Firebug.Trace.clear
31041         });
31042     },
31043
31044     initialize: function(){
31045         Firebug.Panel.initialize.apply(this, arguments);
31046
31047         this.clearButton.initialize();
31048     },
31049
31050     shutdown: function()
31051     {
31052         this.clearButton.shutdown();
31053
31054         Firebug.Panel.shutdown.apply(this, arguments);
31055     }
31056
31057 });
31058
31059 Firebug.registerPanel(TracePanel);
31060
31061 // ************************************************************************************************
31062 }});
31063
31064 /* See license.txt for terms of usage */
31065
31066 FBL.ns(function() { with (FBL) {
31067 // ************************************************************************************************
31068
31069 // ************************************************************************************************
31070 // Globals
31071
31072 var modules = [];
31073 var panelTypes = [];
31074 var panelTypeMap = {};
31075
31076 var parentPanelMap = {};
31077
31078
31079 var registerModule = Firebug.registerModule;
31080 var registerPanel = Firebug.registerPanel;
31081
31082 // ************************************************************************************************
31083 append(Firebug,
31084 {
31085     extend: function(fn)
31086     {
31087         if (Firebug.chrome && Firebug.chrome.addPanel)
31088         {
31089             var namespace = ns(fn);
31090             fn.call(namespace, FBL);
31091         }
31092         else
31093         {
31094             setTimeout(function(){Firebug.extend(fn);},100);
31095         }
31096     },
31097
31098     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
31099     // Registration
31100
31101     registerModule: function()
31102     {
31103         registerModule.apply(Firebug, arguments);
31104
31105         modules.push.apply(modules, arguments);
31106
31107         dispatch(modules, "initialize", []);
31108
31109         if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.registerModule");
31110     },
31111
31112     registerPanel: function()
31113     {
31114         registerPanel.apply(Firebug, arguments);
31115
31116         panelTypes.push.apply(panelTypes, arguments);
31117
31118         for (var i = 0, panelType; panelType = arguments[i]; ++i)
31119         {
31120             // TODO: xxxpedro investigate why Dev Panel throws an error
31121             if (panelType.prototype.name == "Dev") continue;
31122
31123             panelTypeMap[panelType.prototype.name] = arguments[i];
31124
31125             var parentPanelName = panelType.prototype.parentPanel;
31126             if (parentPanelName)
31127             {
31128                 parentPanelMap[parentPanelName] = 1;
31129             }
31130             else
31131             {
31132                 var panelName = panelType.prototype.name;
31133                 var chrome = Firebug.chrome;
31134                 chrome.addPanel(panelName);
31135
31136                 // tab click handler
31137                 var onTabClick = function onTabClick()
31138                 {
31139                     chrome.selectPanel(panelName);
31140                     return false;
31141                 };
31142
31143                 chrome.addController([chrome.panelMap[panelName].tabNode, "mousedown", onTabClick]);
31144             }
31145         }
31146
31147         if (FBTrace.DBG_INITIALIZE)
31148             for (var i = 0; i < arguments.length; ++i)
31149                 FBTrace.sysout("Firebug.registerPanel", arguments[i].prototype.name);
31150     }
31151
31152 });
31153
31154
31155
31156
31157 // ************************************************************************************************
31158 }});
31159
31160 FBL.ns(function() { with (FBL) {
31161 // ************************************************************************************************
31162
31163 FirebugChrome.Skin =
31164 {
31165     CSS: '.obscured{left:-999999px !important;}.collapsed{display:none;}[collapsed="true"]{display:none;}#fbCSS{padding:0 !important;}.cssPropDisable{float:left;display:block;width:2em;cursor:default;}.infoTip{z-index:2147483647;position:fixed;padding:2px 3px;border:1px solid #CBE087;background:LightYellow;font-family:Monaco,monospace;color:#000000;display:none;white-space:nowrap;pointer-events:none;}.infoTip[active="true"]{display:block;}.infoTipLoading{width:16px;height:16px;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/loading_16.gif) no-repeat;}.infoTipImageBox{font-size:11px;min-width:100px;text-align:center;}.infoTipCaption{font-size:11px;font:Monaco,monospace;}.infoTipLoading > .infoTipImage,.infoTipLoading > .infoTipCaption{display:none;}h1.groupHeader{padding:2px 4px;margin:0 0 4px 0;border-top:1px solid #CCCCCC;border-bottom:1px solid #CCCCCC;background:#eee url(https://getfirebug.com/releases/lite/latest/skin/xp/group.gif) repeat-x;font-size:11px;font-weight:bold;_position:relative;}.inlineEditor,.fixedWidthEditor{z-index:2147483647;position:absolute;display:none;}.inlineEditor{margin-left:-6px;margin-top:-3px;}.textEditorInner,.fixedWidthEditor{margin:0 0 0 0 !important;padding:0;border:none !important;font:inherit;text-decoration:inherit;background-color:#FFFFFF;}.fixedWidthEditor{border-top:1px solid #888888 !important;border-bottom:1px solid #888888 !important;}.textEditorInner{position:relative;top:-7px;left:-5px;outline:none;resize:none;}.textEditorInner1{padding-left:11px;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/textEditorBorders.png) repeat-y;_background:url(https://getfirebug.com/releases/lite/latest/skin/xp/textEditorBorders.gif) repeat-y;_overflow:hidden;}.textEditorInner2{position:relative;padding-right:2px;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/textEditorBorders.png) repeat-y 100% 0;_background:url(https://getfirebug.com/releases/lite/latest/skin/xp/textEditorBorders.gif) repeat-y 100% 0;_position:fixed;}.textEditorTop1{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/textEditorCorners.png) no-repeat 100% 0;margin-left:11px;height:10px;_background:url(https://getfirebug.com/releases/lite/latest/skin/xp/textEditorCorners.gif) no-repeat 100% 0;_overflow:hidden;}.textEditorTop2{position:relative;left:-11px;width:11px;height:10px;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/textEditorCorners.png) no-repeat;_background:url(https://getfirebug.com/releases/lite/latest/skin/xp/textEditorCorners.gif) no-repeat;}.textEditorBottom1{position:relative;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/textEditorCorners.png) no-repeat 100% 100%;margin-left:11px;height:12px;_background:url(https://getfirebug.com/releases/lite/latest/skin/xp/textEditorCorners.gif) no-repeat 100% 100%;}.textEditorBottom2{position:relative;left:-11px;width:11px;height:12px;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/textEditorCorners.png) no-repeat 0 100%;_background:url(https://getfirebug.com/releases/lite/latest/skin/xp/textEditorCorners.gif) no-repeat 0 100%;}.panelNode-css{overflow-x:hidden;}.cssSheet > .insertBefore{height:1.5em;}.cssRule{position:relative;margin:0;padding:1em 0 0 6px;font-family:Monaco,monospace;color:#000000;}.cssRule:first-child{padding-top:6px;}.cssElementRuleContainer{position:relative;}.cssHead{padding-right:150px;}.cssProp{}.cssPropName{color:DarkGreen;}.cssPropValue{margin-left:8px;color:DarkBlue;}.cssOverridden span{text-decoration:line-through;}.cssInheritedRule{}.cssInheritLabel{margin-right:0.5em;font-weight:bold;}.cssRule .objectLink-sourceLink{top:0;}.cssProp.editGroup:hover{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/disable.png) no-repeat 2px 1px;_background:url(https://getfirebug.com/releases/lite/latest/skin/xp/disable.gif) no-repeat 2px 1px;}.cssProp.editGroup.editing{background:none;}.cssProp.disabledStyle{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/disableHover.png) no-repeat 2px 1px;_background:url(https://getfirebug.com/releases/lite/latest/skin/xp/disableHover.gif) no-repeat 2px 1px;opacity:1;color:#CCCCCC;}.disabledStyle .cssPropName,.disabledStyle .cssPropValue{color:#CCCCCC;}.cssPropValue.editing + .cssSemi,.inlineExpander + .cssSemi{display:none;}.cssPropValue.editing{white-space:nowrap;}.stylePropName{font-weight:bold;padding:0 4px 4px 4px;width:50%;}.stylePropValue{width:50%;}.panelNode-net{overflow-x:hidden;}.netTable{width:100%;}.hideCategory-undefined .category-undefined,.hideCategory-html .category-html,.hideCategory-css .category-css,.hideCategory-js .category-js,.hideCategory-image .category-image,.hideCategory-xhr .category-xhr,.hideCategory-flash .category-flash,.hideCategory-txt .category-txt,.hideCategory-bin .category-bin{display:none;}.netHeadRow{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/group.gif) repeat-x #FFFFFF;}.netHeadCol{border-bottom:1px solid #CCCCCC;padding:2px 4px 2px 18px;font-weight:bold;}.netHeadLabel{white-space:nowrap;overflow:hidden;}.netHeaderRow{height:16px;}.netHeaderCell{cursor:pointer;-moz-user-select:none;border-bottom:1px solid #9C9C9C;padding:0 !important;font-weight:bold;background:#BBBBBB url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/tableHeader.gif) repeat-x;white-space:nowrap;}.netHeaderRow > .netHeaderCell:first-child > .netHeaderCellBox{padding:2px 14px 2px 18px;}.netHeaderCellBox{padding:2px 14px 2px 10px;border-left:1px solid #D9D9D9;border-right:1px solid #9C9C9C;}.netHeaderCell:hover:active{background:#959595 url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/tableHeaderActive.gif) repeat-x;}.netHeaderSorted{background:#7D93B2 url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/tableHeaderSorted.gif) repeat-x;}.netHeaderSorted > .netHeaderCellBox{border-right-color:#6B7C93;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/arrowDown.png) no-repeat right;}.netHeaderSorted.sortedAscending > .netHeaderCellBox{background-image:url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/arrowUp.png);}.netHeaderSorted:hover:active{background:#536B90 url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/tableHeaderSortedActive.gif) repeat-x;}.panelNode-net .netRowHeader{display:block;}.netRowHeader{cursor:pointer;display:none;height:15px;margin-right:0 !important;}.netRow .netRowHeader{background-position:5px 1px;}.netRow[breakpoint="true"] .netRowHeader{background-image:url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/breakpoint.png);}.netRow[breakpoint="true"][disabledBreakpoint="true"] .netRowHeader{background-image:url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/breakpointDisabled.png);}.netRow.category-xhr:hover .netRowHeader{background-color:#F6F6F6;}#netBreakpointBar{max-width:38px;}#netHrefCol > .netHeaderCellBox{border-left:0px;}.netRow .netRowHeader{width:3px;}.netInfoRow .netRowHeader{display:table-cell;}.netTable[hiddenCols~=netHrefCol] TD[id="netHrefCol"],.netTable[hiddenCols~=netHrefCol] TD.netHrefCol,.netTable[hiddenCols~=netStatusCol] TD[id="netStatusCol"],.netTable[hiddenCols~=netStatusCol] TD.netStatusCol,.netTable[hiddenCols~=netDomainCol] TD[id="netDomainCol"],.netTable[hiddenCols~=netDomainCol] TD.netDomainCol,.netTable[hiddenCols~=netSizeCol] TD[id="netSizeCol"],.netTable[hiddenCols~=netSizeCol] TD.netSizeCol,.netTable[hiddenCols~=netTimeCol] TD[id="netTimeCol"],.netTable[hiddenCols~=netTimeCol] TD.netTimeCol{display:none;}.netRow{background:LightYellow;}.netRow.loaded{background:#FFFFFF;}.netRow.loaded:hover{background:#EFEFEF;}.netCol{padding:0;vertical-align:top;border-bottom:1px solid #EFEFEF;white-space:nowrap;height:17px;}.netLabel{width:100%;}.netStatusCol{padding-left:10px;color:rgb(128,128,128);}.responseError > .netStatusCol{color:red;}.netDomainCol{padding-left:5px;}.netSizeCol{text-align:right;padding-right:10px;}.netHrefLabel{-moz-box-sizing:padding-box;overflow:hidden;z-index:10;position:absolute;padding-left:18px;padding-top:1px;max-width:15%;font-weight:bold;}.netFullHrefLabel{display:none;-moz-user-select:none;padding-right:10px;padding-bottom:3px;max-width:100%;background:#FFFFFF;z-index:200;}.netHrefCol:hover > .netFullHrefLabel{display:block;}.netRow.loaded:hover .netCol > .netFullHrefLabel{background-color:#EFEFEF;}.useA11y .a11yShowFullLabel{display:block;background-image:none !important;border:1px solid #CBE087;background-color:LightYellow;font-family:Monaco,monospace;color:#000000;font-size:10px;z-index:2147483647;}.netSizeLabel{padding-left:6px;}.netStatusLabel,.netDomainLabel,.netSizeLabel,.netBar{padding:1px 0 2px 0 !important;}.responseError{color:red;}.hasHeaders .netHrefLabel:hover{cursor:pointer;color:blue;text-decoration:underline;}.netLoadingIcon{position:absolute;border:0;margin-left:14px;width:16px;height:16px;background:transparent no-repeat 0 0;background-image:url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/loading_16.gif);display:inline-block;}.loaded .netLoadingIcon{display:none;}.netBar,.netSummaryBar{position:relative;border-right:50px solid transparent;}.netResolvingBar{position:absolute;left:0;top:0;bottom:0;background:#FFFFFF url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/netBarResolving.gif) repeat-x;z-index:60;}.netConnectingBar{position:absolute;left:0;top:0;bottom:0;background:#FFFFFF url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/netBarConnecting.gif) repeat-x;z-index:50;}.netBlockingBar{position:absolute;left:0;top:0;bottom:0;background:#FFFFFF url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/netBarWaiting.gif) repeat-x;z-index:40;}.netSendingBar{position:absolute;left:0;top:0;bottom:0;background:#FFFFFF url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/netBarSending.gif) repeat-x;z-index:30;}.netWaitingBar{position:absolute;left:0;top:0;bottom:0;background:#FFFFFF url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/netBarResponded.gif) repeat-x;z-index:20;min-width:1px;}.netReceivingBar{position:absolute;left:0;top:0;bottom:0;background:#38D63B url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/netBarLoading.gif) repeat-x;z-index:10;}.netWindowLoadBar,.netContentLoadBar{position:absolute;left:0;top:0;bottom:0;width:1px;background-color:red;z-index:70;opacity:0.5;display:none;margin-bottom:-1px;}.netContentLoadBar{background-color:Blue;}.netTimeLabel{-moz-box-sizing:padding-box;position:absolute;top:1px;left:100%;padding-left:6px;color:#444444;min-width:16px;}.loaded .netReceivingBar,.loaded.netReceivingBar{background:#B6B6B6 url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/netBarLoaded.gif) repeat-x;border-color:#B6B6B6;}.fromCache .netReceivingBar,.fromCache.netReceivingBar{background:#D6D6D6 url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/netBarCached.gif) repeat-x;border-color:#D6D6D6;}.netSummaryRow .netTimeLabel,.loaded .netTimeLabel{background:transparent;}.timeInfoTip{width:150px; height:40px}.timeInfoTipBar,.timeInfoTipEventBar{position:relative;display:block;margin:0;opacity:1;height:15px;width:4px;}.timeInfoTipEventBar{width:1px !important;}.timeInfoTipCell.startTime{padding-right:8px;}.timeInfoTipCell.elapsedTime{text-align:right;padding-right:8px;}.sizeInfoLabelCol{font-weight:bold;padding-right:10px;font-family:Lucida Grande,Tahoma,sans-serif;font-size:11px;}.sizeInfoSizeCol{font-weight:bold;}.sizeInfoDetailCol{color:gray;text-align:right;}.sizeInfoDescCol{font-style:italic;}.netSummaryRow .netReceivingBar{background:#BBBBBB;border:none;}.netSummaryLabel{color:#222222;}.netSummaryRow{background:#BBBBBB !important;font-weight:bold;}.netSummaryRow .netBar{border-right-color:#BBBBBB;}.netSummaryRow > .netCol{border-top:1px solid #999999;border-bottom:2px solid;-moz-border-bottom-colors:#EFEFEF #999999;padding-top:1px;padding-bottom:2px;}.netSummaryRow > .netHrefCol:hover{background:transparent !important;}.netCountLabel{padding-left:18px;}.netTotalSizeCol{text-align:right;padding-right:10px;}.netTotalTimeCol{text-align:right;}.netCacheSizeLabel{position:absolute;z-index:1000;left:0;top:0;}.netLimitRow{background:rgb(255,255,225) !important;font-weight:normal;color:black;font-weight:normal;}.netLimitLabel{padding-left:18px;}.netLimitRow > .netCol{border-bottom:2px solid;-moz-border-bottom-colors:#EFEFEF #999999;vertical-align:middle !important;padding-top:2px;padding-bottom:2px;}.netLimitButton{font-size:11px;padding-top:1px;padding-bottom:1px;}.netInfoCol{border-top:1px solid #EEEEEE;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/group.gif) repeat-x #FFFFFF;}.netInfoBody{margin:10px 0 4px 10px;}.netInfoTabs{position:relative;padding-left:17px;}.netInfoTab{position:relative;top:-3px;margin-top:10px;padding:4px 6px;border:1px solid transparent;border-bottom:none;_border:none;font-weight:bold;color:#565656;cursor:pointer;}.netInfoTabSelected{cursor:default !important;border:1px solid #D7D7D7 !important;border-bottom:none !important;-moz-border-radius:4px 4px 0 0;-webkit-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;background-color:#FFFFFF;}.logRow-netInfo.error .netInfoTitle{color:red;}.logRow-netInfo.loading .netInfoResponseText{font-style:italic;color:#888888;}.loading .netInfoResponseHeadersTitle{display:none;}.netInfoResponseSizeLimit{font-family:Lucida Grande,Tahoma,sans-serif;padding-top:10px;font-size:11px;}.netInfoText{display:none;margin:0;border:1px solid #D7D7D7;border-right:none;padding:8px;background-color:#FFFFFF;font-family:Monaco,monospace;white-space:pre-wrap;}.netInfoTextSelected{display:block;}.netInfoParamName{padding-right:10px;font-family:Lucida Grande,Tahoma,sans-serif;font-weight:bold;vertical-align:top;text-align:right;white-space:nowrap;}.netInfoPostText .netInfoParamName{width:1px;}.netInfoParamValue{width:100%;}.netInfoHeadersText,.netInfoPostText,.netInfoPutText{padding-top:0;}.netInfoHeadersGroup,.netInfoPostParams,.netInfoPostSource{margin-bottom:4px;border-bottom:1px solid #D7D7D7;padding-top:8px;padding-bottom:2px;font-family:Lucida Grande,Tahoma,sans-serif;font-weight:bold;color:#565656;}.netInfoPostParamsTable,.netInfoPostPartsTable,.netInfoPostJSONTable,.netInfoPostXMLTable,.netInfoPostSourceTable{margin-bottom:10px;width:100%;}.netInfoPostContentType{color:#bdbdbd;padding-left:50px;font-weight:normal;}.netInfoHtmlPreview{border:0;width:100%;height:100%;}.netHeadersViewSource{color:#bdbdbd;margin-left:200px;font-weight:normal;}.netHeadersViewSource:hover{color:blue;cursor:pointer;}.netActivationRow,.netPageSeparatorRow{background:rgb(229,229,229) !important;font-weight:normal;color:black;}.netActivationLabel{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/chrome://firebug/skin/infoIcon.png) no-repeat 3px 2px;padding-left:22px;}.netPageSeparatorRow{height:5px !important;}.netPageSeparatorLabel{padding-left:22px;height:5px !important;}.netPageRow{background-color:rgb(255,255,255);}.netPageRow:hover{background:#EFEFEF;}.netPageLabel{padding:1px 0 2px 18px !important;font-weight:bold;}.netActivationRow > .netCol{border-bottom:2px solid;-moz-border-bottom-colors:#EFEFEF #999999;padding-top:2px;padding-bottom:3px;}.twisty,.logRow-errorMessage > .hasTwisty > .errorTitle,.logRow-log > .objectBox-array.hasTwisty,.logRow-spy .spyHead .spyTitle,.logGroup > .logRow,.memberRow.hasChildren > .memberLabelCell > .memberLabel,.hasHeaders .netHrefLabel,.netPageRow > .netCol > .netPageTitle{background-image:url(https://getfirebug.com/releases/lite/latest/skin/xp/tree_open.gif);background-repeat:no-repeat;background-position:2px 2px;min-height:12px;}.logRow-errorMessage > .hasTwisty.opened > .errorTitle,.logRow-log > .objectBox-array.hasTwisty.opened,.logRow-spy.opened .spyHead .spyTitle,.logGroup.opened > .logRow,.memberRow.hasChildren.opened > .memberLabelCell > .memberLabel,.nodeBox.highlightOpen > .nodeLabel > .twisty,.nodeBox.open > .nodeLabel > .twisty,.netRow.opened > .netCol > .netHrefLabel,.netPageRow.opened > .netCol > .netPageTitle{background-image:url(https://getfirebug.com/releases/lite/latest/skin/xp/tree_close.gif);}.twisty{background-position:4px 4px;}* html .logRow-spy .spyHead .spyTitle,* html .logGroup .logGroupLabel,* html .hasChildren .memberLabelCell .memberLabel,* html .hasHeaders .netHrefLabel{background-image:url(https://getfirebug.com/releases/lite/latest/skin/xp/tree_open.gif);background-repeat:no-repeat;background-position:2px 2px;}* html .opened .spyHead .spyTitle,* html .opened .logGroupLabel,* html .opened .memberLabelCell .memberLabel{background-image:url(https://getfirebug.com/releases/lite/latest/skin/xp/tree_close.gif);background-repeat:no-repeat;background-position:2px 2px;}.panelNode-console{overflow-x:hidden;}.objectLink{text-decoration:none;}.objectLink:hover{cursor:pointer;text-decoration:underline;}.logRow{position:relative;margin:0;border-bottom:1px solid #D7D7D7;padding:2px 4px 1px 6px;background-color:#FFFFFF;overflow:hidden !important;}.useA11y .logRow:focus{border-bottom:1px solid #000000 !important;outline:none !important;background-color:#FFFFAD !important;}.useA11y .logRow:focus a.objectLink-sourceLink{background-color:#FFFFAD;}.useA11y .a11yFocus:focus,.useA11y .objectBox:focus{outline:2px solid #FF9933;background-color:#FFFFAD;}.useA11y .objectBox-null:focus,.useA11y .objectBox-undefined:focus{background-color:#888888 !important;}.useA11y .logGroup.opened > .logRow{border-bottom:1px solid #ffffff;}.logGroup{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/group.gif) repeat-x #FFFFFF;padding:0 !important;border:none !important;}.logGroupBody{display:none;margin-left:16px;border-left:1px solid #D7D7D7;border-top:1px solid #D7D7D7;background:#FFFFFF;}.logGroup > .logRow{background-color:transparent !important;font-weight:bold;}.logGroup.opened > .logRow{border-bottom:none;}.logGroup.opened > .logGroupBody{display:block;}.logRow-command > .objectBox-text{font-family:Monaco,monospace;color:#0000FF;white-space:pre-wrap;}.logRow-info,.logRow-warn,.logRow-error,.logRow-assert,.logRow-warningMessage,.logRow-errorMessage{padding-left:22px;background-repeat:no-repeat;background-position:4px 2px;}.logRow-assert,.logRow-warningMessage,.logRow-errorMessage{padding-top:0;padding-bottom:0;}.logRow-info,.logRow-info .objectLink-sourceLink{background-color:#FFFFFF;}.logRow-warn,.logRow-warningMessage,.logRow-warn .objectLink-sourceLink,.logRow-warningMessage .objectLink-sourceLink{background-color:cyan;}.logRow-error,.logRow-assert,.logRow-errorMessage,.logRow-error .objectLink-sourceLink,.logRow-errorMessage .objectLink-sourceLink{background-color:LightYellow;}.logRow-error,.logRow-assert,.logRow-errorMessage{color:#FF0000;}.logRow-info{}.logRow-warn,.logRow-warningMessage{}.logRow-error,.logRow-assert,.logRow-errorMessage{}.objectBox-string,.objectBox-text,.objectBox-number,.objectLink-element,.objectLink-textNode,.objectLink-function,.objectBox-stackTrace,.objectLink-profile{font-family:Monaco,monospace;}.objectBox-string,.objectBox-text,.objectLink-textNode{white-space:pre-wrap;}.objectBox-number,.objectLink-styleRule,.objectLink-element,.objectLink-textNode{color:#000088;}.objectBox-string{color:#FF0000;}.objectLink-function,.objectBox-stackTrace,.objectLink-profile{color:DarkGreen;}.objectBox-null,.objectBox-undefined{padding:0 2px;border:1px solid #666666;background-color:#888888;color:#FFFFFF;}.objectBox-exception{padding:0 2px 0 18px;color:red;}.objectLink-sourceLink{position:absolute;right:4px;top:2px;padding-left:8px;font-family:Lucida Grande,sans-serif;font-weight:bold;color:#0000FF;}.errorTitle{margin-top:0px;margin-bottom:1px;padding-top:2px;padding-bottom:2px;}.errorTrace{margin-left:17px;}.errorSourceBox{margin:2px 0;}.errorSource-none{display:none;}.errorSource-syntax > .errorBreak{visibility:hidden;}.errorSource{cursor:pointer;font-family:Monaco,monospace;color:DarkGreen;}.errorSource:hover{text-decoration:underline;}.errorBreak{cursor:pointer;display:none;margin:0 6px 0 0;width:13px;height:14px;vertical-align:bottom;opacity:0.1;}.hasBreakSwitch .errorBreak{display:inline;}.breakForError .errorBreak{opacity:1;}.assertDescription{margin:0;}.logRow-profile > .logRow > .objectBox-text{font-family:Lucida Grande,Tahoma,sans-serif;color:#000000;}.logRow-profile > .logRow > .objectBox-text:last-child{color:#555555;font-style:italic;}.logRow-profile.opened > .logRow{padding-bottom:4px;}.profilerRunning > .logRow{padding-left:22px !important;}.profileSizer{width:100%;overflow-x:auto;overflow-y:scroll;}.profileTable{border-bottom:1px solid #D7D7D7;padding:0 0 4px 0;}.profileTable tr[odd="1"]{background-color:#F5F5F5;vertical-align:middle;}.profileTable a{vertical-align:middle;}.profileTable td{padding:1px 4px 0 4px;}.headerCell{cursor:pointer;-moz-user-select:none;border-bottom:1px solid #9C9C9C;padding:0 !important;font-weight:bold;}.headerCellBox{padding:2px 4px;border-left:1px solid #D9D9D9;border-right:1px solid #9C9C9C;}.headerCell:hover:active{}.headerSorted{}.headerSorted > .headerCellBox{border-right-color:#6B7C93;}.headerSorted.sortedAscending > .headerCellBox{}.headerSorted:hover:active{}.linkCell{text-align:right;}.linkCell > .objectLink-sourceLink{position:static;}.logRow-stackTrace{padding-top:0;background:#f8f8f8;}.logRow-stackTrace > .objectBox-stackFrame{position:relative;padding-top:2px;}.objectLink-object{font-family:Lucida Grande,sans-serif;font-weight:bold;color:DarkGreen;white-space:pre-wrap;}.objectProp-object{color:DarkGreen;}.objectProps{color:#000;font-weight:normal;}.objectPropName{color:#777;}.objectProps .objectProp-string{color:#f55;}.objectProps .objectProp-number{color:#55a;}.objectProps .objectProp-object{color:#585;}.selectorTag,.selectorId,.selectorClass{font-family:Monaco,monospace;font-weight:normal;}.selectorTag{color:#0000FF;}.selectorId{color:DarkBlue;}.selectorClass{color:red;}.selectorHidden > .selectorTag{color:#5F82D9;}.selectorHidden > .selectorId{color:#888888;}.selectorHidden > .selectorClass{color:#D86060;}.selectorValue{font-family:Lucida Grande,sans-serif;font-style:italic;color:#555555;}.panelNode.searching .logRow{display:none;}.logRow.matched{display:block !important;}.logRow.matching{position:absolute;left:-1000px;top:-1000px;max-width:0;max-height:0;overflow:hidden;}.objectLeftBrace,.objectRightBrace,.objectEqual,.objectComma,.arrayLeftBracket,.arrayRightBracket,.arrayComma{font-family:Monaco,monospace;}.objectLeftBrace,.objectRightBrace,.arrayLeftBracket,.arrayRightBracket{font-weight:bold;}.objectLeftBrace,.arrayLeftBracket{margin-right:4px;}.objectRightBrace,.arrayRightBracket{margin-left:4px;}.logRow-dir{padding:0;}.logRow-errorMessage .hasTwisty .errorTitle,.logRow-spy .spyHead .spyTitle,.logGroup .logRow{cursor:pointer;padding-left:18px;background-repeat:no-repeat;background-position:3px 3px;}.logRow-errorMessage > .hasTwisty > .errorTitle{background-position:2px 3px;}.logRow-errorMessage > .hasTwisty > .errorTitle:hover,.logRow-spy .spyHead .spyTitle:hover,.logGroup > .logRow:hover{text-decoration:underline;}.logRow-spy{padding:0 !important;}.logRow-spy,.logRow-spy .objectLink-sourceLink{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/group.gif) repeat-x #FFFFFF;padding-right:4px;right:0;}.logRow-spy.opened{padding-bottom:4px;border-bottom:none;}.spyTitle{color:#000000;font-weight:bold;-moz-box-sizing:padding-box;overflow:hidden;z-index:100;padding-left:18px;}.spyCol{padding:0;white-space:nowrap;height:16px;}.spyTitleCol:hover > .objectLink-sourceLink,.spyTitleCol:hover > .spyTime,.spyTitleCol:hover > .spyStatus,.spyTitleCol:hover > .spyTitle{display:none;}.spyFullTitle{display:none;-moz-user-select:none;max-width:100%;background-color:Transparent;}.spyTitleCol:hover > .spyFullTitle{display:block;}.spyStatus{padding-left:10px;color:rgb(128,128,128);}.spyTime{margin-left:4px;margin-right:4px;color:rgb(128,128,128);}.spyIcon{margin-right:4px;margin-left:4px;width:16px;height:16px;vertical-align:middle;background:transparent no-repeat 0 0;display:none;}.loading .spyHead .spyRow .spyIcon{background-image:url(https://getfirebug.com/releases/lite/latest/skin/xp/loading_16.gif);display:block;}.logRow-spy.loaded:not(.error) .spyHead .spyRow .spyIcon{width:0;margin:0;}.logRow-spy.error .spyHead .spyRow .spyIcon{background-image:url(https://getfirebug.com/releases/lite/latest/skin/xp/errorIcon-sm.png);display:block;background-position:2px 2px;}.logRow-spy .spyHead .netInfoBody{display:none;}.logRow-spy.opened .spyHead .netInfoBody{margin-top:10px;display:block;}.logRow-spy.error .spyTitle,.logRow-spy.error .spyStatus,.logRow-spy.error .spyTime{color:red;}.logRow-spy.loading .spyResponseText{font-style:italic;color:#888888;}.caption{font-family:Lucida Grande,Tahoma,sans-serif;font-weight:bold;color:#444444;}.warning{padding:10px;font-family:Lucida Grande,Tahoma,sans-serif;font-weight:bold;color:#888888;}.panelNode-dom{overflow-x:hidden !important;}.domTable{font-size:1em;width:100%;table-layout:fixed;background:#fff;}.domTableIE{width:auto;}.memberLabelCell{padding:2px 0 2px 0;vertical-align:top;}.memberValueCell{padding:1px 0 1px 5px;display:block;overflow:hidden;}.memberLabel{display:block;cursor:default;-moz-user-select:none;overflow:hidden;padding-left:18px;background-color:#FFFFFF;text-decoration:none;}.memberRow.hasChildren .memberLabelCell .memberLabel:hover{cursor:pointer;color:blue;text-decoration:underline;}.userLabel{color:#000000;font-weight:bold;}.userClassLabel{color:#E90000;font-weight:bold;}.userFunctionLabel{color:#025E2A;font-weight:bold;}.domLabel{color:#000000;}.domFunctionLabel{color:#025E2A;}.ordinalLabel{color:SlateBlue;font-weight:bold;}.scopesRow{padding:2px 18px;background-color:LightYellow;border-bottom:5px solid #BEBEBE;color:#666666;}.scopesLabel{background-color:LightYellow;}.watchEditCell{padding:2px 18px;background-color:LightYellow;border-bottom:1px solid #BEBEBE;color:#666666;}.editor-watchNewRow,.editor-memberRow{font-family:Monaco,monospace !important;}.editor-memberRow{padding:1px 0 !important;}.editor-watchRow{padding-bottom:0 !important;}.watchRow > .memberLabelCell{font-family:Monaco,monospace;padding-top:1px;padding-bottom:1px;}.watchRow > .memberLabelCell > .memberLabel{background-color:transparent;}.watchRow > .memberValueCell{padding-top:2px;padding-bottom:2px;}.watchRow > .memberLabelCell,.watchRow > .memberValueCell{background-color:#F5F5F5;border-bottom:1px solid #BEBEBE;}.watchToolbox{z-index:2147483647;position:absolute;right:0;padding:1px 2px;}#fbConsole{overflow-x:hidden !important;}#fbCSS{font:1em Monaco,monospace;padding:0 7px;}#fbstylesheetButtons select,#fbScriptButtons select{font:11px Lucida Grande,Tahoma,sans-serif;margin-top:1px;padding-left:3px;background:#fafafa;border:1px inset #fff;width:220px;outline:none;}.Selector{margin-top:10px}.CSSItem{margin-left:4%}.CSSText{padding-left:20px;}.CSSProperty{color:#005500;}.CSSValue{padding-left:5px; color:#000088;}#fbHTMLStatusBar{display:inline;}.fbToolbarButtons{display:none;}.fbStatusSeparator{display:block;float:left;padding-top:4px;}#fbStatusBarBox{display:none;}#fbToolbarContent{display:block;position:absolute;_position:absolute;top:0;padding-top:4px;height:23px;clip:rect(0,2048px,27px,0);}.fbTabMenuTarget{display:none !important;float:left;width:10px;height:10px;margin-top:6px;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/tabMenuTarget.png);}.fbTabMenuTarget:hover{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/tabMenuTargetHover.png);}.fbShadow{float:left;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/shadowAlpha.png) no-repeat bottom right !important;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/shadow2.gif) no-repeat bottom right;margin:10px 0 0 10px !important;margin:10px 0 0 5px;}.fbShadowContent{display:block;position:relative;background-color:#fff;border:1px solid #a9a9a9;top:-6px;left:-6px;}.fbMenu{display:none;position:absolute;font-size:11px;line-height:13px;z-index:2147483647;}.fbMenuContent{padding:2px;}.fbMenuSeparator{display:block;position:relative;padding:1px 18px 0;text-decoration:none;color:#000;cursor:default;background:#ACA899;margin:4px 0;}.fbMenuOption{display:block;position:relative;padding:2px 18px;text-decoration:none;color:#000;cursor:default;}.fbMenuOption:hover{color:#fff;background:#316AC5;}.fbMenuGroup{background:transparent url(https://getfirebug.com/releases/lite/latest/skin/xp/tabMenuPin.png) no-repeat right 0;}.fbMenuGroup:hover{background:#316AC5 url(https://getfirebug.com/releases/lite/latest/skin/xp/tabMenuPin.png) no-repeat right -17px;}.fbMenuGroupSelected{color:#fff;background:#316AC5 url(https://getfirebug.com/releases/lite/latest/skin/xp/tabMenuPin.png) no-repeat right -17px;}.fbMenuChecked{background:transparent url(https://getfirebug.com/releases/lite/latest/skin/xp/tabMenuCheckbox.png) no-repeat 4px 0;}.fbMenuChecked:hover{background:#316AC5 url(https://getfirebug.com/releases/lite/latest/skin/xp/tabMenuCheckbox.png) no-repeat 4px -17px;}.fbMenuRadioSelected{background:transparent url(https://getfirebug.com/releases/lite/latest/skin/xp/tabMenuRadio.png) no-repeat 4px 0;}.fbMenuRadioSelected:hover{background:#316AC5 url(https://getfirebug.com/releases/lite/latest/skin/xp/tabMenuRadio.png) no-repeat 4px -17px;}.fbMenuShortcut{padding-right:85px;}.fbMenuShortcutKey{position:absolute;right:0;top:2px;width:77px;}#fbFirebugMenu{top:22px;left:0;}.fbMenuDisabled{color:#ACA899 !important;}#fbFirebugSettingsMenu{left:245px;top:99px;}#fbConsoleMenu{top:42px;left:48px;}.fbIconButton{display:block;}.fbIconButton{display:block;}.fbIconButton{display:block;float:left;height:20px;width:20px;color:#000;margin-right:2px;text-decoration:none;cursor:default;}.fbIconButton:hover{position:relative;top:-1px;left:-1px;margin-right:0;_margin-right:1px;color:#333;border:1px solid #fff;border-bottom:1px solid #bbb;border-right:1px solid #bbb;}.fbIconPressed{position:relative;margin-right:0;_margin-right:1px;top:0 !important;left:0 !important;height:19px;color:#333 !important;border:1px solid #bbb !important;border-bottom:1px solid #cfcfcf !important;border-right:1px solid #ddd !important;}#fbErrorPopup{position:absolute;right:0;bottom:0;height:19px;width:75px;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/sprite.png) #f1f2ee 0 0;z-index:999;}#fbErrorPopupContent{position:absolute;right:0;top:1px;height:18px;width:75px;_width:74px;border-left:1px solid #aca899;}#fbErrorIndicator{position:absolute;top:2px;right:5px;}.fbBtnInspectActive{background:#aaa;color:#fff !important;}.fbBody{margin:0;padding:0;overflow:hidden;font-family:Lucida Grande,Tahoma,sans-serif;font-size:11px;background:#fff;}.clear{clear:both;}#fbMiniChrome{display:none;right:0;height:27px;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/sprite.png) #f1f2ee 0 0;margin-left:1px;}#fbMiniContent{display:block;position:relative;left:-1px;right:0;top:1px;height:25px;border-left:1px solid #aca899;}#fbToolbarSearch{float:right;border:1px solid #ccc;margin:0 5px 0 0;background:#fff url(https://getfirebug.com/releases/lite/latest/skin/xp/search.png) no-repeat 4px 2px !important;background:#fff url(https://getfirebug.com/releases/lite/latest/skin/xp/search.gif) no-repeat 4px 2px;padding-left:20px;font-size:11px;}#fbToolbarErrors{float:right;margin:1px 4px 0 0;font-size:11px;}#fbLeftToolbarErrors{float:left;margin:7px 0px 0 5px;font-size:11px;}.fbErrors{padding-left:20px;height:14px;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/errorIcon.png) no-repeat !important;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/errorIcon.gif) no-repeat;color:#f00;font-weight:bold;}#fbMiniErrors{display:inline;display:none;float:right;margin:5px 2px 0 5px;}#fbMiniIcon{float:right;margin:3px 4px 0;height:20px;width:20px;float:right;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/sprite.png) 0 -135px;cursor:pointer;}#fbChrome{font-family:Lucida Grande,Tahoma,sans-serif;font-size:11px;position:absolute;_position:static;top:0;left:0;height:100%;width:100%;border-collapse:collapse;border-spacing:0;background:#fff;overflow:hidden;}#fbChrome > tbody > tr > td{padding:0;}#fbTop{height:49px;}#fbToolbar{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/sprite.png) #f1f2ee 0 0;height:27px;font-size:11px;line-height:13px;}#fbPanelBarBox{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/sprite.png) #dbd9c9 0 -27px;height:22px;}#fbContent{height:100%;vertical-align:top;}#fbBottom{height:18px;background:#fff;}#fbToolbarIcon{float:left;padding:0 5px 0;}#fbToolbarIcon a{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/sprite.png) 0 -135px;}#fbToolbarButtons{padding:0 2px 0 5px;}#fbToolbarButtons{padding:0 2px 0 5px;}.fbButton{text-decoration:none;display:block;float:left;color:#000;padding:4px 6px 4px 7px;cursor:default;}.fbButton:hover{color:#333;background:#f5f5ef url(https://getfirebug.com/releases/lite/latest/skin/xp/buttonBg.png);padding:3px 5px 3px 6px;border:1px solid #fff;border-bottom:1px solid #bbb;border-right:1px solid #bbb;}.fbBtnPressed{background:#e3e3db url(https://getfirebug.com/releases/lite/latest/skin/xp/buttonBgHover.png) !important;padding:3px 4px 2px 6px !important;margin:1px 0 0 1px !important;border:1px solid #ACA899 !important;border-color:#ACA899 #ECEBE3 #ECEBE3 #ACA899 !important;}#fbStatusBarBox{top:4px;cursor:default;}.fbToolbarSeparator{overflow:hidden;border:1px solid;border-color:transparent #fff transparent #777;_border-color:#eee #fff #eee #777;height:7px;margin:6px 3px;float:left;}.fbBtnSelected{font-weight:bold;}.fbStatusBar{color:#aca899;}.fbStatusBar a{text-decoration:none;color:black;}.fbStatusBar a:hover{color:blue;cursor:pointer;}#fbWindowButtons{position:absolute;white-space:nowrap;right:0;top:0;height:17px;width:48px;padding:5px;z-index:6;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/sprite.png) #f1f2ee 0 0;}#fbPanelBar1{width:1024px; z-index:8;left:0;white-space:nowrap;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/sprite.png) #dbd9c9 0 -27px;position:absolute;left:4px;}#fbPanelBar2Box{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/sprite.png) #dbd9c9 0 -27px;position:absolute;height:22px;width:300px; z-index:9;right:0;}#fbPanelBar2{position:absolute;width:290px; height:22px;padding-left:4px;}.fbPanel{display:none;}#fbPanelBox1,#fbPanelBox2{max-height:inherit;height:100%;font-size:1em;}#fbPanelBox2{background:#fff;}#fbPanelBox2{width:300px;background:#fff;}#fbPanel2{margin-left:6px;background:#fff;}#fbLargeCommandLine{display:none;position:absolute;z-index:9;top:27px;right:0;width:294px;height:201px;border-width:0;margin:0;padding:2px 0 0 2px;resize:none;outline:none;font-size:11px;overflow:auto;border-top:1px solid #B9B7AF;_right:-1px;_border-left:1px solid #fff;}#fbLargeCommandButtons{display:none;background:#ECE9D8;bottom:0;right:0;width:294px;height:21px;padding-top:1px;position:fixed;border-top:1px solid #ACA899;z-index:9;}#fbSmallCommandLineIcon{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/down.png) no-repeat;position:absolute;right:2px;bottom:3px;z-index:99;}#fbSmallCommandLineIcon:hover{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/downHover.png) no-repeat;}.hide{overflow:hidden !important;position:fixed !important;display:none !important;visibility:hidden !important;}#fbCommand{height:18px;}#fbCommandBox{position:fixed;_position:absolute;width:100%;height:18px;bottom:0;overflow:hidden;z-index:9;background:#fff;border:0;border-top:1px solid #ccc;}#fbCommandIcon{position:absolute;color:#00f;top:2px;left:6px;display:inline;font:11px Monaco,monospace;z-index:10;}#fbCommandLine{position:absolute;width:100%;top:0;left:0;border:0;margin:0;padding:2px 0 2px 32px;font:11px Monaco,monospace;z-index:9;outline:none;}#fbLargeCommandLineIcon{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/up.png) no-repeat;position:absolute;right:1px;bottom:1px;z-index:10;}#fbLargeCommandLineIcon:hover{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/upHover.png) no-repeat;}div.fbFitHeight{overflow:auto;position:relative;}.fbSmallButton{overflow:hidden;width:16px;height:16px;display:block;text-decoration:none;cursor:default;}#fbWindowButtons .fbSmallButton{float:right;}#fbWindow_btClose{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/min.png);}#fbWindow_btClose:hover{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/minHover.png);}#fbWindow_btDetach{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/detach.png);}#fbWindow_btDetach:hover{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/detachHover.png);}#fbWindow_btDeactivate{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/off.png);}#fbWindow_btDeactivate:hover{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/offHover.png);}.fbTab{text-decoration:none;display:none;float:left;width:auto;float:left;cursor:default;font-family:Lucida Grande,Tahoma,sans-serif;font-size:11px;line-height:13px;font-weight:bold;height:22px;color:#565656;}.fbPanelBar span{float:left;}.fbPanelBar .fbTabL,.fbPanelBar .fbTabR{height:22px;width:8px;}.fbPanelBar .fbTabText{padding:4px 1px 0;}a.fbTab:hover{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/sprite.png) 0 -73px;}a.fbTab:hover .fbTabL{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/sprite.png) -16px -96px;}a.fbTab:hover .fbTabR{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/sprite.png) -24px -96px;}.fbSelectedTab{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/sprite.png) #f1f2ee 0 -50px !important;color:#000;}.fbSelectedTab .fbTabL{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/sprite.png) 0 -96px !important;}.fbSelectedTab .fbTabR{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/sprite.png) -8px -96px !important;}#fbHSplitter{position:fixed;_position:absolute;left:0;top:0;width:100%;height:5px;overflow:hidden;cursor:n-resize !important;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/pixel_transparent.gif);z-index:9;}#fbHSplitter.fbOnMovingHSplitter{height:100%;z-index:100;}.fbVSplitter{background:#ece9d8;color:#000;border:1px solid #716f64;border-width:0 1px;border-left-color:#aca899;width:4px;cursor:e-resize;overflow:hidden;right:294px;text-decoration:none;z-index:10;position:absolute;height:100%;top:27px;}div.lineNo{font:1em/1.4545em Monaco,monospace;position:relative;float:left;top:0;left:0;margin:0 5px 0 0;padding:0 5px 0 10px;background:#eee;color:#888;border-right:1px solid #ccc;text-align:right;}.sourceBox{position:absolute;}.sourceCode{font:1em Monaco,monospace;overflow:hidden;white-space:pre;display:inline;}.nodeControl{margin-top:3px;margin-left:-14px;float:left;width:9px;height:9px;overflow:hidden;cursor:default;background:url(https://getfirebug.com/releases/lite/latest/skin/xp/tree_open.gif);_float:none;_display:inline;_position:absolute;}div.nodeMaximized{background:url(https://getfirebug.com/releases/lite/latest/skin/xp/tree_close.gif);}div.objectBox-element{padding:1px 3px;}.objectBox-selector{cursor:default;}.selectedElement{background:highlight;color:#fff !important;}.selectedElement span{color:#fff !important;}* html .selectedElement{position:relative;}@media screen and (-webkit-min-device-pixel-ratio:0){.selectedElement{background:#316AC5;color:#fff !important;}}.logRow *{font-size:1em;}.logRow{position:relative;border-bottom:1px solid #D7D7D7;padding:2px 4px 1px 6px;zbackground-color:#FFFFFF;}.logRow-command{font-family:Monaco,monospace;color:blue;}.objectBox-string,.objectBox-text,.objectBox-number,.objectBox-function,.objectLink-element,.objectLink-textNode,.objectLink-function,.objectBox-stackTrace,.objectLink-profile{font-family:Monaco,monospace;}.objectBox-null{padding:0 2px;border:1px solid #666666;background-color:#888888;color:#FFFFFF;}.objectBox-string{color:red;}.objectBox-number{color:#000088;}.objectBox-function{color:DarkGreen;}.objectBox-object{color:DarkGreen;font-weight:bold;font-family:Lucida Grande,sans-serif;}.objectBox-array{color:#000;}.logRow-info,.logRow-error,.logRow-warn{background:#fff no-repeat 2px 2px;padding-left:20px;padding-bottom:3px;}.logRow-info{background-image:url(https://getfirebug.com/releases/lite/latest/skin/xp/infoIcon.png) !important;background-image:url(https://getfirebug.com/releases/lite/latest/skin/xp/infoIcon.gif);}.logRow-warn{background-color:cyan;background-image:url(https://getfirebug.com/releases/lite/latest/skin/xp/warningIcon.png) !important;background-image:url(https://getfirebug.com/releases/lite/latest/skin/xp/warningIcon.gif);}.logRow-error{background-color:LightYellow;background-image:url(https://getfirebug.com/releases/lite/latest/skin/xp/errorIcon.png) !important;background-image:url(https://getfirebug.com/releases/lite/latest/skin/xp/errorIcon.gif);color:#f00;}.errorMessage{vertical-align:top;color:#f00;}.objectBox-sourceLink{position:absolute;right:4px;top:2px;padding-left:8px;font-family:Lucida Grande,sans-serif;font-weight:bold;color:#0000FF;}.selectorTag,.selectorId,.selectorClass{font-family:Monaco,monospace;font-weight:normal;}.selectorTag{color:#0000FF;}.selectorId{color:DarkBlue;}.selectorClass{color:red;}.objectBox-element{font-family:Monaco,monospace;color:#000088;}.nodeChildren{padding-left:26px;}.nodeTag{color:blue;cursor:pointer;}.nodeValue{color:#FF0000;font-weight:normal;}.nodeText,.nodeComment{margin:0 2px;vertical-align:top;}.nodeText{color:#333333;font-family:Monaco,monospace;}.nodeComment{color:DarkGreen;}.nodeHidden,.nodeHidden *{color:#888888;}.nodeHidden .nodeTag{color:#5F82D9;}.nodeHidden .nodeValue{color:#D86060;}.selectedElement .nodeHidden,.selectedElement .nodeHidden *{color:SkyBlue !important;}.log-object{}.property{position:relative;clear:both;height:15px;}.propertyNameCell{vertical-align:top;float:left;width:28%;position:absolute;left:0;z-index:0;}.propertyValueCell{float:right;width:68%;background:#fff;position:absolute;padding-left:5px;display:table-cell;right:0;z-index:1;}.propertyName{font-weight:bold;}.FirebugPopup{height:100% !important;}.FirebugPopup #fbWindowButtons{display:none !important;}.FirebugPopup #fbHSplitter{display:none !important;}',
31166     HTML: '<table id="fbChrome" cellpadding="0" cellspacing="0" border="0"><tbody><tr><td id="fbTop" colspan="2"><div id="fbWindowButtons"><a id="fbWindow_btDeactivate" class="fbSmallButton fbHover" title="Deactivate Firebug for this web page">&nbsp;</a><a id="fbWindow_btDetach" class="fbSmallButton fbHover" title="Open Firebug in popup window">&nbsp;</a><a id="fbWindow_btClose" class="fbSmallButton fbHover" title="Minimize Firebug">&nbsp;</a></div><div id="fbToolbar"><div id="fbToolbarContent"><span id="fbToolbarIcon"><a id="fbFirebugButton" class="fbIconButton" class="fbHover" target="_blank">&nbsp;</a></span><span id="fbToolbarButtons"><span id="fbFixedButtons"><a id="fbChrome_btInspect" class="fbButton fbHover" title="Click an element in the page to inspect">Inspect</a></span><span id="fbConsoleButtons" class="fbToolbarButtons"><a id="fbConsole_btClear" class="fbButton fbHover" title="Clear the console">Clear</a></span></span><span id="fbStatusBarBox"><span class="fbToolbarSeparator"></span></span></div></div><div id="fbPanelBarBox"><div id="fbPanelBar1" class="fbPanelBar"><a id="fbConsoleTab" class="fbTab fbHover"><span class="fbTabL"></span><span class="fbTabText">Console</span><span class="fbTabMenuTarget"></span><span class="fbTabR"></span></a><a id="fbHTMLTab" class="fbTab fbHover"><span class="fbTabL"></span><span class="fbTabText">HTML</span><span class="fbTabR"></span></a><a class="fbTab fbHover"><span class="fbTabL"></span><span class="fbTabText">CSS</span><span class="fbTabR"></span></a><a class="fbTab fbHover"><span class="fbTabL"></span><span class="fbTabText">Script</span><span class="fbTabR"></span></a><a class="fbTab fbHover"><span class="fbTabL"></span><span class="fbTabText">DOM</span><span class="fbTabR"></span></a></div><div id="fbPanelBar2Box" class="hide"><div id="fbPanelBar2" class="fbPanelBar"></div></div></div><div id="fbHSplitter">&nbsp;</div></td></tr><tr id="fbContent"><td id="fbPanelBox1"><div id="fbPanel1" class="fbFitHeight"><div id="fbConsole" class="fbPanel"></div><div id="fbHTML" class="fbPanel"></div></div></td><td id="fbPanelBox2" class="hide"><div id="fbVSplitter" class="fbVSplitter">&nbsp;</div><div id="fbPanel2" class="fbFitHeight"><div id="fbHTML_Style" class="fbPanel"></div><div id="fbHTML_Layout" class="fbPanel"></div><div id="fbHTML_DOM" class="fbPanel"></div></div><textarea id="fbLargeCommandLine" class="fbFitHeight"></textarea><div id="fbLargeCommandButtons"><a id="fbCommand_btRun" class="fbButton fbHover">Run</a><a id="fbCommand_btClear" class="fbButton fbHover">Clear</a><a id="fbSmallCommandLineIcon" class="fbSmallButton fbHover"></a></div></td></tr><tr id="fbBottom" class="hide"><td id="fbCommand" colspan="2"><div id="fbCommandBox"><div id="fbCommandIcon">&gt;&gt;&gt;</div><input id="fbCommandLine" name="fbCommandLine" type="text"/><a id="fbLargeCommandLineIcon" class="fbSmallButton fbHover"></a></div></td></tr></tbody></table><span id="fbMiniChrome"><span id="fbMiniContent"><span id="fbMiniIcon" title="Open Firebug Lite"></span><span id="fbMiniErrors" class="fbErrors"></span></span></span>'
31167 };
31168
31169 // ************************************************************************************************
31170 }});
31171
31172 // ************************************************************************************************
31173 FBL.initialize();
31174 // ************************************************************************************************
31175
31176 })();