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
25var FBL = {};
26
27( /** @scope s_lib @this FBL */ function() {
28// ************************************************************************************************
29
30// ************************************************************************************************
31// Constants
32
33var productionDir = "http://getfirebug.com/releases/lite/";
34var bookmarkletVersion = 4;
35
36// ************************************************************************************************
37
38var reNotWhitespace = /[^\s]/;
39var reSplitFile = /:\/{1,3}(.*?)\/([^\/]*?)\/?($|\?.*)/;
40
41// Globals
42this.reJavascript = /\s*javascript:\s*(.*)/;
43this.reChrome = /chrome:\/\/([^\/]*)\//;
44this.reFile = /file:\/\/([^\/]*)\//;
45
46
47// ************************************************************************************************
48// properties
49
50var userAgent = navigator.userAgent.toLowerCase();
51this.isFirefox = /firefox/.test(userAgent);
52this.isOpera   = /opera/.test(userAgent);
53this.isSafari  = /webkit/.test(userAgent);
54this.isIE      = /msie/.test(userAgent) && !/opera/.test(userAgent);
55this.isIE6     = /msie 6/i.test(navigator.appVersion);
56this.browserVersion = (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1];
57this.isIElt8   = this.isIE && (this.browserVersion-0 < 8);
58
59// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
60
61this.NS = null;
62this.pixelsPerInch = null;
63
64
65// ************************************************************************************************
66// Namespaces
67
68var namespaces = [];
69
70// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
71
72this.ns = function(fn)
73{
74    var ns = {};
75    namespaces.push(fn, ns);
76    return ns;
77};
78
79var FBTrace = null;
80
81this.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
198var 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
213var 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
245var sharedEnv;
246
247this.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
302var destroyEnvironment = function destroyEnvironment()
303{
304    setTimeout(function()
305    {
306        FBL = null;
307    }, 100);
308};
309
310// ************************************************************************************************
311// Library location
312
313var 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
569this.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
575this.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
581this.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
591this.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
601this.append = function(l, r)
602{
603    for (var n in r)
604        l[n] = r[n];
605
606    return l;
607};
608
609this.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
625this.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
655this.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
667this.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
676function 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
690function extendArray(array, array2)
691{
692   var newArray = [];
693   newArray.push.apply(newArray, array);
694   newArray.push.apply(newArray, array2);
695   return newArray;
696}
697
698this.extendArray = extendArray;
699this.cloneArray = cloneArray;
700
701function 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
711this.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
727this.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
736this.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
747this.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
770this.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
786var 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
807var normal = entityConversionLists.normal,
808    reverse = entityConversionLists.reverse;
809
810function 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
825var e = addEntityMapToList,
826    white = 'whitespace',
827    text = 'text',
828    attr = 'attributes',
829    css = 'css',
830    editor = 'editor';
831
832e(0x0022, 'quot', attr, css);
833e(0x0026, 'amp', attr, text, css);
834e(0x0027, 'apos', css);
835e(0x003c, 'lt', attr, text, css);
836e(0x003e, 'gt', attr, text, css);
837e(0xa9, 'copy', text, editor);
838e(0xae, 'reg', text, editor);
839e(0x2122, 'trade', text, editor);
840
841// See http://en.wikipedia.org/wiki/Dash
842e(0x2012, '#8210', attr, text, editor); // figure dash
843e(0x2013, 'ndash', attr, text, editor); // en dash
844e(0x2014, 'mdash', attr, text, editor); // em dash
845e(0x2015, '#8213', attr, text, editor); // horizontal bar
846
847e(0x00a0, 'nbsp', attr, text, white, editor);
848e(0x2002, 'ensp', attr, text, white, editor);
849e(0x2003, 'emsp', attr, text, white, editor);
850e(0x2009, 'thinsp', attr, text, white, editor);
851e(0x200c, 'zwnj', attr, text, white, editor);
852e(0x200d, 'zwj', attr, text, white, editor);
853e(0x200e, 'lrm', attr, text, white, editor);
854e(0x200f, 'rlm', attr, text, white, editor);
855e(0x200b, '#8203', attr, text, white, editor); // zero-width space (ZWSP)
856
857//************************************************************************************************
858// Entity escaping
859
860var entityConversionRegexes = {
861        normal : {},
862        reverse : {}
863    };
864
865var 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
886function 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
917function 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
935function 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
983this.escapeGroupsForEntities = escapeGroupsForEntities;
984
985
986function 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
1022var escapeForTextNode = this.escapeForTextNode = createSimpleEscape('text', 'normal');
1023var escapeForHtmlEditor = this.escapeForHtmlEditor = createSimpleEscape('editor', 'normal');
1024var escapeForElementAttribute = this.escapeForElementAttribute = createSimpleEscape('attributes', 'normal');
1025var 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
1033var escapeForSourceLine = this.escapeForSourceLine = createSimpleEscape('text', 'normal');
1034
1035var unescapeWhitespace = createSimpleEscape('whitespace', 'reverse');
1036
1037this.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
1046this.escapeNewLines = function(value)
1047{
1048    return value.replace(/\r/g, "\\r").replace(/\n/g, "\\n");
1049};
1050
1051this.stripNewLines = function(value)
1052{
1053    return typeof(value) == "string" ? value.replace(/[\r\n]/g, " ") : value;
1054};
1055
1056this.escapeJS = function(value)
1057{
1058    return value.replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace('"', '\\"', "g");
1059};
1060
1061function 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
1088function 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
1110this.escapeHTML = escapeHTML;
1111
1112this.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
1127this.isWhitespace = function(text)
1128{
1129    return !reNotWhitespace.exec(text);
1130};
1131
1132this.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
1152this.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
1183this.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
1196var reTrim = /^\s+|\s+$/g;
1197this.trim = function(s)
1198{
1199    return s.replace(reTrim, "");
1200};
1201
1202
1203// ************************************************************************************************
1204// Empty
1205
1206this.emptyFn = function(){};
1207
1208
1209
1210// ************************************************************************************************
1211// Visibility
1212
1213this.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
1230this.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
1247this.obscure = function(elt, obscured)
1248{
1249    if (obscured)
1250        this.setClass(elt, "obscured");
1251    else
1252        this.removeClass(elt, "obscured");
1253};
1254
1255this.hide = function(elt, hidden)
1256{
1257    elt.style.visibility = hidden ? "hidden" : "visible";
1258};
1259
1260this.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
1272this.eraseNode = function(node)
1273{
1274    while (node.lastChild)
1275        node.removeChild(node.lastChild);
1276};
1277
1278// ************************************************************************************************
1279// Window iteration
1280
1281this.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
1298this.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
1311this.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
1354this.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
1432this.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
1459this.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
1489this.getOffsetSize = function(elt)
1490{
1491    return {width: elt.offsetWidth, height: elt.offsetHeight};
1492};
1493
1494this.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
1503this.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
1511this.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
1525this.move = function(element, x, y)
1526{
1527    element.style.left = x + "px";
1528    element.style.top = y + "px";
1529};
1530
1531this.resize = function(element, w, h)
1532{
1533    element.style.width = w + "px";
1534    element.style.height = h + "px";
1535};
1536
1537this.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
1564this.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
1610var cssKeywordMap = null;
1611var cssPropNames = null;
1612var cssColorNames = null;
1613var imageRules = null;
1614
1615this.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
1640this.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
1653this.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
1676this.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
1696this.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
1721this.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
1745this.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
1765this.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
1775this.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
1786this.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
1792this.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 */
1806this.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
1835var 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
1849var 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
1859var isElementHTML = this.isElementHTML = function(node)
1860{
1861    return node.nodeName == node.nodeName.toUpperCase();
1862};
1863
1864var isElementXHTML = this.isElementXHTML = function(node)
1865{
1866    return node.nodeName == node.nodeName.toLowerCase();
1867};
1868
1869var isElementMathML = this.isElementMathML = function(node)
1870{
1871    return node.namespaceURI == 'http://www.w3.org/1998/Math/MathML';
1872};
1873
1874var isElementSVG = this.isElementSVG = function(node)
1875{
1876    return node.namespaceURI == 'http://www.w3.org/2000/svg';
1877};
1878
1879var isElementXUL = this.isElementXUL = function(node)
1880{
1881    return node instanceof XULElement;
1882};
1883
1884this.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
1892this.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
1965this.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
2023this.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
2047this.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
2065this.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
2072this.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
2079this.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
2092this.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
2101this.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
2119this.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
2133this.$ = 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
2143this.$$ = 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
2153this.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
2173this.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
2185this.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
2198this.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
2217this.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
2228this.getNextElement = function(node)
2229{
2230    while (node && node.nodeType != 1)
2231        node = node.nextSibling;
2232
2233    return node;
2234};
2235
2236this.getPreviousElement = function(node)
2237{
2238    while (node && node.nodeType != 1)
2239        node = node.previousSibling;
2240
2241    return node;
2242};
2243
2244this.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
2256this.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
2272this.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
2288this.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
2314this.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
2345this.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
2351this.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
2357this.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
2372var appendFragment = null;
2373
2374this.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
2416this.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
2434this.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
2459this.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
2487this.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
2495this.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
2503this.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
2511this.noKeyModifiers = function(event)
2512{
2513    return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
2514};
2515
2516this.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
2524this.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
2532this.isControl = function(event)
2533{
2534    return (event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey;
2535};
2536
2537this.isAlt = function(event)
2538{
2539    return event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey;
2540};
2541
2542this.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
2550this.isControlShift = function(event)
2551{
2552    return (event.metaKey || event.ctrlKey) && event.shiftKey && !event.altKey;
2553};
2554
2555this.isShift = function(event)
2556{
2557    return event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey;
2558};
2559
2560this.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
2568this.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
2584this.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
2604this.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
2627this.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
2652this.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
2694var disableTextSelectionHandler = function(event)
2695{
2696    FBL.cancelEvent(event, true);
2697
2698    return false;
2699};
2700
2701this.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
2719this.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
2738var 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
2812this.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
2833this.getFileName = function(url)
2834{
2835    var split = this.splitURLBase(url);
2836    return split.name;
2837};
2838
2839this.splitURLBase = function(url)
2840{
2841    if (this.isDataURL(url))
2842        return this.splitDataURL(url);
2843    return this.splitURLTrue(url);
2844};
2845
2846this.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
2897this.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
2908this.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
2923this.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
2942this.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
2968this.isSystemStyleSheet = function(sheet)
2969{
2970    var href = sheet && sheet.href;
2971    return href && FBL.isSystemURL(href);
2972};
2973
2974this.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
2989this.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
2999this.isDataURL = function(url)
3000{
3001    return (url && url.substr(0,5) == "data:");
3002};
3003
3004this.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
3014this.getURLFromLocalFile = function(file)
3015{
3016    var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
3017    var URL = fileHandler.getURLSpecFromFile(file);
3018    return URL;
3019};
3020
3021this.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
3030this.getDomain = function(url)
3031{
3032    var m = /[^:]+:\/{1,3}([^\/]+)/.exec(url);
3033    return m ? m[1] : "";
3034};
3035
3036this.getURLPath = function(url)
3037{
3038    var m = /[^:]+:\/{1,3}[^\/]+(\/.*?)$/.exec(url);
3039    return m ? m[1] : "";
3040};
3041
3042this.getPrettyDomain = function(url)
3043{
3044    var m = /[^:]+:\/{1,3}(www\.)?([^\/]+)/.exec(url);
3045    return m ? m[2] : "";
3046};
3047
3048this.absoluteURL = function(url, baseURL)
3049{
3050    return this.absoluteURLWithDots(url, baseURL).replace("/./", "/", "g");
3051};
3052
3053this.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
3084this.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
3110this.denormalizeURL = function(url)
3111{
3112    return url.replace(/file:\/\/\//g, "file:/");
3113};
3114
3115this.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
3132this.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?
3174this.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
3191this.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
3231this.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
3246this.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
3261this.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
3327this.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
3342this.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
3361this.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
3392function onOperaTabBlur(e)
3393{
3394    if (this.lastKey == 9)
3395      this.focus();
3396};
3397
3398function onOperaTabKeyDown(e)
3399{
3400    this.lastKey = e.keyCode;
3401};
3402
3403function onOperaTabFocus(e)
3404{
3405    this.lastKey = null;
3406};
3407
3408this.fixOperaTabKey = function(el)
3409{
3410    el.onfocus = onOperaTabFocus;
3411    el.onblur = onOperaTabBlur;
3412    el.onkeydown = onOperaTabKeyDown;
3413};
3414
3415// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3416
3417this.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
3428this.ErrorCopy = function(message)
3429{
3430    this.message = message;
3431};
3432
3433function 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
3445this.EventCopy = EventCopy;
3446
3447
3448// ************************************************************************************************
3449// Type Checking
3450
3451var toString = Object.prototype.toString;
3452var reFunction = /^\s*function(\s+[\w_$][\w\d_$]*)?\s*\(/;
3453
3454this.isArray = function(object) {
3455    return toString.call(object) === '[object Array]';
3456};
3457
3458this.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
3478this.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
3541var 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
3648Problems:
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
3656var domMemberMap2 = {};
3657
3658var domMemberMap2Sandbox = null;
3659
3660var 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
3716this.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/*
3800this.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
3872this.isDOMMember = function(object, propName)
3873{
3874    var members = this.getDOMMembers(object);
3875    return members && propName in members;
3876};
3877
3878var domMemberCache = null;
3879var domMemberMap = {};
3880
3881domMemberMap.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
3986domMemberMap.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
4002domMemberMap.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
4053domMemberMap.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
4175domMemberMap.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
4236domMemberMap.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
4260domMemberMap.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
4322domMemberMap.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
4343domMemberMap.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
4366domMemberMap.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
4381domMemberMap.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
4407domMemberMap.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
4421domMemberMap.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
4440domMemberMap.HTMLScriptElement = extendArray(domMemberMap.Element,
4441[
4442    "src"
4443]);
4444
4445domMemberMap.HTMLButtonElement = extendArray(domMemberMap.Element,
4446[
4447    "accessKey",
4448    "disabled",
4449    "form",
4450    "name",
4451    "type",
4452    "value",
4453
4454    "click"
4455]);
4456
4457domMemberMap.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
4485domMemberMap.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
4506domMemberMap.HTMLBodyElement = extendArray(domMemberMap.Element,
4507[
4508    "aLink",
4509    "background",
4510    "bgColor",
4511    "link",
4512    "text",
4513    "vLink"
4514]);
4515
4516domMemberMap.HTMLHtmlElement = extendArray(domMemberMap.Element,
4517[
4518    "version"
4519]);
4520
4521domMemberMap.Text = extendArray(domMemberMap.Node,
4522[
4523    "data",
4524    "length",
4525
4526    "appendData",
4527    "deleteData",
4528    "insertData",
4529    "replaceData",
4530    "splitText",
4531    "substringData"
4532]);
4533
4534domMemberMap.Attr = extendArray(domMemberMap.Node,
4535[
4536    "name",
4537    "value",
4538    "specified",
4539    "ownerElement"
4540]);
4541
4542domMemberMap.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
4595this.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
4790this.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
4931this.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
4964this.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
5598this.nonEditableTags =
5599{
5600    "HTML": 1,
5601    "HEAD": 1,
5602    "html": 1,
5603    "head": 1
5604};
5605
5606this.innerEditableTags =
5607{
5608    "BODY": 1,
5609    "body": 1
5610};
5611
5612this.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
5627var 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
5665if (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 */
5792this.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
5984this.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
6001this.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
6019this.removeCookie = function(name)
6020{
6021    this.createCookie(name, "", -1);
6022};
6023
6024
6025// ************************************************************************************************
6026// http://www.mister-pixel.com/#Content__state=is_that_simple
6027var 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
6043var resetStyle = "margin:0; padding:0; border:0; position:absolute; overflow:hidden; display:block;";
6044
6045var 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
6062this.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
6071this.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
6089this.SourceText = function(lines, owner)
6090{
6091    this.lines = lines;
6092    this.owner = owner;
6093};
6094
6095this.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
6106FBL.ns( /** @scope s_i18n */ function() { with (FBL) {
6107// ************************************************************************************************
6108
6109// TODO: xxxpedro localization
6110var 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
6133FBL.$STR = function(name)
6134{
6135    return oSTR.hasOwnProperty(name) ? oSTR[name] : name;
6136};
6137
6138FBL.$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
6182var 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
6233FBL.ns( /** @scope s_firebug */ function() { with (FBL) {
6234// ************************************************************************************************
6235
6236// ************************************************************************************************
6237// Globals
6238
6239// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6240// Internals
6241
6242var modules = [];
6243var panelTypes = [];
6244var panelTypeMap = {};
6245var reps = [];
6246
6247var parentPanelMap = {};
6248
6249
6250// ************************************************************************************************
6251// Firebug
6252
6253/**
6254 * @namespace describe Firebug
6255 * @exports FBL.Firebug as Firebug
6256 */
6257FBL.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
6549Firebug.restorePrefs();
6550
6551// xxxpedro should we remove this?
6552window.Firebug = FBL.Firebug;
6553
6554if (!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
6563FBL.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 */
6585Firebug.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
6593Firebug.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 */
6621Firebug.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 */
6703Firebug.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 */
7438Firebug.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// ************************************************************************************************
7487if (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
7594FBL.ns( /** @scope s_gui */ function() { with (FBL) {
7595// ************************************************************************************************
7596
7597// ************************************************************************************************
7598// Controller
7599
7600/**@namespace*/
7601FBL.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*/
7669FBL.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
7838FBL.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
7871Button.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 */
8017FBL.IconButton = function()
8018{
8019    Button.apply(this, arguments);
8020};
8021
8022IconButton.prototype = extend(Button.prototype,
8023/**@extend FBL.IconButton.prototype*/
8024{
8025    baseClassName: "fbIconButton",
8026    pressedClassName: "fbIconPressed"
8027});
8028
8029
8030//************************************************************************************************
8031// Menu
8032
8033var menuItemProps = {"class": "$item.className", type: "$item.type", value: "$item.value",
8034        _command: "$item.command"};
8035
8036if (isIE6)
8037    menuItemProps.href = "javascript:void(0)";
8038
8039// Allow GUI to be loaded even when Domplate module is not installed.
8040if (FBL.domplate)
8041var 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 */
8178FBL.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
8220var menuMap = {};
8221
8222// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8223
8224Menu.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
8521append(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*/
8557function StatusBar(){};
8558
8559StatusBar.prototype = extend(Controller, {
8560
8561});
8562
8563// ************************************************************************************************
8564
8565
8566// ************************************************************************************************
8567}});
8568
8569/* See license.txt for terms of usage */
8570
8571FBL.ns( /**@scope s_context*/ function() { with (FBL) {
8572// ************************************************************************************************
8573
8574// ************************************************************************************************
8575// Globals
8576
8577var 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
8583var shouldFixElementFromPoint = isOpera || isSafari && browserVersion < "532";
8584
8585var evalError = "___firebug_evaluation_error___";
8586var pixelsPerInch;
8587
8588var resetStyle = "margin:0; padding:0; border:0; position:absolute; overflow:hidden; display:block;";
8589var offscreenStyle = resetStyle + "top:-1234px; left:-1234px;";
8590
8591
8592// ************************************************************************************************
8593// Context
8594
8595/** @class */
8596FBL.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
8621FBL.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
9219FBL.ns( /**@scope ns-chrome*/ function() { with (FBL) {
9220// ************************************************************************************************
9221
9222// ************************************************************************************************
9223// Globals
9224
9225// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9226// Window Options
9227
9228var 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
9310FBL.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*/
9325FBL.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
9392var 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
9684var 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
9735var getChromeDivTemplate = function()
9736{
9737    return FirebugChrome.Skin.HTML;
9738};
9739
9740var 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*/
9772var 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 **/
9801var ChromeBase = {};
9802append(ChromeBase, Controller);
9803append(ChromeBase, PanelBar);
9804append(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 */
10835var 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 */
11067var 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 */
11170var 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
11328var 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
11353var 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
11370var 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
11400var 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
11410var 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
11423var 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
11479var 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
11515var 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
11536var onVSplitterMouseDown = function onVSplitterMouseDown(event)
11537{
11538    addGlobalEvent("mousemove", onVSplitterMouseMove);
11539    addGlobalEvent("mouseup", onVSplitterMouseUp);
11540
11541    return false;
11542};
11543
11544var 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
11573var 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
11587FBL.ns(function() { with (FBL) {
11588// ************************************************************************************************
11589
11590Firebug.Lite =
11591{
11592};
11593
11594// ************************************************************************************************
11595}});
11596
11597
11598/* See license.txt for terms of usage */
11599
11600FBL.ns(function() { with (FBL) {
11601// ************************************************************************************************
11602
11603Firebug.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 */
11619var cacheUID = 0;
11620var 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
11769Firebug.Lite.Cache.StyleSheet = createCache();
11770Firebug.Lite.Cache.Element = createCache();
11771
11772// TODO: xxxpedro
11773Firebug.Lite.Cache.Event = createCache();
11774
11775
11776// ************************************************************************************************
11777}});
11778
11779
11780/* See license.txt for terms of usage */
11781
11782FBL.ns(function() { with (FBL) {
11783// ************************************************************************************************
11784
11785// ************************************************************************************************
11786var sourceMap = {};
11787
11788// ************************************************************************************************
11789Firebug.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
11865Firebug.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
11870var 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
11890var 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
11918FBL.ns(function() { with (FBL) {
11919// ************************************************************************************************
11920
11921Firebug.Lite.Style =
11922{
11923};
11924
11925// ************************************************************************************************
11926}});
11927
11928
11929/* See license.txt for terms of usage */
11930
11931FBL.ns(function() { with (FBL) {
11932// ************************************************************************************************
11933
11934Firebug.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
11946Firebug.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
11964FBL.ns(function() { with (FBL) {
11965// ************************************************************************************************
11966
11967
11968Firebug.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
11978Firebug.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
12156var 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]*$/.
12453test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
12454replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
12455replace(/(?:^|:|,)(?:\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
12480FBL.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
12511var 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
12617if (typeof module != 'undefined') { module.exports = store }
12618
12619
12620// ************************************************************************************************
12621// registration
12622
12623FBL.Store = store;
12624
12625// ************************************************************************************************
12626})();
12627
12628/* See license.txt for terms of usage */
12629
12630FBL.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
12640var 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 */
12663var 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
12784Sizzle.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
12801Sizzle.matches = function(expr, set){
12802    return Sizzle(expr, null, null, set);
12803};
12804
12805Sizzle.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
12837Sizzle.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 */
12913var 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
13312var origPOS = Expr.match.POS;
13313
13314for ( 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
13319var 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.
13332try {
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
13358var sortOrder;
13359
13360if ( 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
13486if ( 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
13517if ( 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
13541function 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
13577function 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
13620var 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
13626var isXML = function(elem){
13627    return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
13628        !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
13629};
13630
13631var 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
13653Firebug.Selector = Sizzle;
13654
13655/**#@-*/
13656
13657// ************************************************************************************************
13658}});
13659
13660/* See license.txt for terms of usage */
13661
13662FBL.ns(function() { with (FBL) {
13663// ************************************************************************************************
13664
13665// ************************************************************************************************
13666// Inspector Module
13667
13668var ElementCache = Firebug.Lite.Cache.Element;
13669
13670var inspectorTS, inspectorTimer, isInspecting;
13671
13672Firebug.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
14029var offlineFragment = null;
14030
14031var boxModelVisible = false;
14032
14033var boxModel, boxModelStyle,
14034    boxMargin, boxMarginStyle,
14035    boxBorder, boxBorderStyle,
14036    boxPadding, boxPaddingStyle,
14037    boxContent, boxContentStyle;
14038
14039// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14040
14041var resetStyle = "margin:0; padding:0; border:0; position:absolute; overflow:hidden; display:block;";
14042var offscreenStyle = resetStyle + "top:-1234px; left:-1234px;";
14043
14044var inspectStyle = resetStyle + "z-index: 2147483500;";
14045var 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
14050var inspectModelOpacity = isIE ? "filter:alpha(opacity=80);" : "opacity:0.8;";
14051var inspectModelStyle = inspectStyle + inspectModelOpacity;
14052var inspectMarginStyle = inspectStyle + "background: #EDFF64; height:100%; width:100%;";
14053var inspectBorderStyle = inspectStyle + "background: #666;";
14054var inspectPaddingStyle = inspectStyle + "background: SlateBlue;";
14055var inspectContentStyle = inspectStyle + "background: SkyBlue;";
14056
14057
14058var outlineStyle = {
14059    fbHorizontalLine: "background: #3875D7;height: 2px;",
14060    fbVerticalLine: "background: #3875D7;width: 2px;"
14061};
14062
14063// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14064
14065var lastInspecting = 0;
14066var fbInspectFrame = null;
14067
14068
14069// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14070
14071var outlineVisible = false;
14072var outlineElements = {};
14073var outline = {
14074  "fbOutlineT": "fbHorizontalLine",
14075  "fbOutlineL": "fbVerticalLine",
14076  "fbOutlineB": "fbHorizontalLine",
14077  "fbOutlineR": "fbVerticalLine"
14078};
14079
14080
14081var getInspectingTarget = function()
14082{
14083
14084};
14085
14086// ************************************************************************************************
14087// Section
14088
14089var 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
14098var destroyInspectorFrame = function destroyInspectorFrame()
14099{
14100    if (fbInspectFrame)
14101    {
14102        Firebug.browser.document.getElementsByTagName("body")[0].removeChild(fbInspectFrame);
14103        fbInspectFrame = null;
14104    }
14105};
14106
14107var 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
14119var destroyOutlineInspector = function destroyOutlineInspector()
14120{
14121    for (var name in outline)
14122    {
14123        var el = outlineElements[name];
14124        el.parentNode.removeChild(el);
14125    }
14126};
14127
14128var 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
14163var 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 */
14193FBL.DomplateTag = function DomplateTag(tagName)
14194{
14195    this.tagName = tagName;
14196};
14197
14198/**
14199 * @class
14200 * @extends FBL.DomplateTag
14201 */
14202FBL.DomplateEmbed = function DomplateEmbed()
14203{
14204};
14205
14206/**
14207 * @class
14208 * @extends FBL.DomplateTag
14209 */
14210FBL.DomplateLoop = function DomplateLoop()
14211{
14212};
14213
14214// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14215
14216var DomplateTag = FBL.DomplateTag;
14217var DomplateEmbed = FBL.DomplateEmbed;
14218var DomplateLoop = FBL.DomplateLoop;
14219
14220// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14221
14222var womb = null;
14223
14224FBL.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
14240var domplate = FBL.domplate;
14241
14242FBL.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
14250FBL.TAG = function()
14251{
14252    var embed = new DomplateEmbed();
14253    return embed.merge(arguments);
14254};
14255
14256FBL.FOR = function()
14257{
14258    var loop = new DomplateLoop();
14259    return loop.merge(arguments);
14260};
14261
14262FBL.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
14711FBL.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
14780FBL.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 */
14876function Variable(name, format)
14877{
14878    this.name = name;
14879    this.format = format;
14880}
14881
14882/** @class */
14883function Parts(parts)
14884{
14885    this.parts = parts;
14886}
14887
14888// ************************************************************************************************
14889
14890function 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
14918function parseValue(val)
14919{
14920    return typeof(val) == 'string' ? parseParts(val) : val;
14921}
14922
14923function 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
14933function 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
14946function 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
14978function 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
15017function isTag(obj)
15018{
15019    return (typeof(obj) == "function" || obj instanceof Function) && !!obj.tag;
15020}
15021
15022function 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
15039function 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
15048function copyObject(l, r)
15049{
15050    var m = {};
15051    extend(m, l);
15052    extend(m, r);
15053    return m;
15054}
15055
15056function extend(l, r)
15057{
15058    for (var n in r)
15059        l[n] = r[n];
15060}
15061
15062function 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 */
15073function 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 */
15087function StopIteration() {}
15088
15089FBL.$break = function()
15090{
15091    throw StopIteration;
15092};
15093
15094// ************************************************************************************************
15095
15096/** @namespace */
15097var 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
15304function 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
15317defineTags(
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
15328var FirebugReps = FBL.ns(function() { with (FBL) {
15329
15330
15331// ************************************************************************************************
15332// Common Tags
15333
15334var OBJECTBOX = this.OBJECTBOX =
15335    SPAN({"class": "objectBox objectBox-$className"});
15336
15337var OBJECTBLOCK = this.OBJECTBLOCK =
15338    DIV({"class": "objectBox objectBox-$className"});
15339
15340var 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
15363this.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
15379this.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
15395this.Nada = domplate(Firebug.Rep,
15396{
15397    tag: SPAN(""),
15398
15399    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15400
15401    className: "nada"
15402});
15403
15404// ************************************************************************************************
15405
15406this.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
15422this.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
15440this.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
15453this.Caption = domplate(Firebug.Rep,
15454{
15455    tag: SPAN({"class": "caption"}, "$object")
15456});
15457
15458// ************************************************************************************************
15459
15460this.Warning = domplate(Firebug.Rep,
15461{
15462    tag: DIV({"class": "warning focusRow", role : 'listitem'}, "$object|STR")
15463});
15464
15465// ************************************************************************************************
15466
15467this.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/*
15553this.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
15621this.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
15882this.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
16006this.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
16026this.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
16047this.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
16065this.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
16323this.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
16345this.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
16394this.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
16459this.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
16516this.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
16559this.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
16673this.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
16709this.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
16790this.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
16809this.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
16843this.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
17020this.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
17055this.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//************************************************************************************************
17092this.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// ************************************************************************************************
17134this.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
17169this.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// ************************************************************************************************
17193Firebug.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
17219Firebug.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 *
17228Software License Agreement (BSD License)
17229
17230Copyright (c) 2006, Yahoo! Inc.
17231All rights reserved.
17232
17233Redistribution and use of this software in source and binary forms, with or without modification, are
17234permitted 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
17250THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
17251WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
17252PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17253ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
17254LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
17255INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
17256TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
17257ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17258 * /
17259 */
17260
17261
17262/* See license.txt for terms of usage */
17263
17264FBL.ns(function() { with (FBL) {
17265
17266// ************************************************************************************************
17267// Constants
17268
17269var saveTimeout = 400;
17270var pageAmount = 10;
17271
17272// ************************************************************************************************
17273// Globals
17274
17275var currentTarget = null;
17276var currentGroup = null;
17277var currentPanel = null;
17278var currentEditor = null;
17279
17280var defaultEditor = null;
17281
17282var originalClassName = null;
17283
17284var originalValue = null;
17285var defaultValue = null;
17286var previousValue = null;
17287
17288var invalidEditor = false;
17289var ignoreNextInput = false;
17290
17291// ************************************************************************************************
17292
17293Firebug.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
17682Firebug.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
17743var 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
17760if (isIE)
17761{
17762    inlineEditorAttributes.onpropertychange = "$onInput";
17763    inlineEditorAttributes.onkeydown = "$onKeyDown";
17764}
17765// for other browsers we use the oninput event
17766else
17767{
17768    inlineEditorAttributes.oninput = "$onInput";
17769}
17770
17771Firebug.InlineEditor = function(doc)
17772{
17773    this.initializeInline(doc);
17774};
17775
17776Firebug.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
18310Firebug.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
18580var 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 */
18598var 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
18611var isGroupInsert = function isGroupInsert(next, group)
18612{
18613    return (!group || isAncestor(next, group))
18614        && (hasClass(next, "insertBefore") || hasClass(next, "insertAfter"));
18615}
18616
18617var getNextOutsider = function getNextOutsider(element, group)
18618{
18619    return getOutsider(element, group, bind(getNextByClass, FBL, "editable"));
18620}
18621
18622var getPreviousOutsider = function getPreviousOutsider(element, group)
18623{
18624    return getOutsider(element, group, bind(getPreviousByClass, FBL, "editable"));
18625}
18626
18627var 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
18645var insertTab = function insertTab()
18646{
18647    insertTextIntoElement(currentEditor.input, Firebug.Editor.tabCharacter);
18648}
18649
18650// ************************************************************************************************
18651
18652Firebug.registerModule(Firebug.Editor);
18653
18654// ************************************************************************************************
18655
18656}});
18657
18658
18659/* See license.txt for terms of usage */
18660
18661FBL.ns(function() { with (FBL) {
18662// ************************************************************************************************
18663
18664if (Env.Options.disableXHRListener)
18665    return;
18666
18667// ************************************************************************************************
18668// XHRSpy
18669
18670var XHRSpy = function()
18671{
18672    this.requestHeaders = [];
18673    this.responseHeaders = [];
18674};
18675
18676XHRSpy.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
18706var 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
19118var _ActiveXObject;
19119var isIE6 =  /msie 6/i.test(navigator.appVersion);
19120
19121if (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
19157if (!isIE6)
19158{
19159    var _XMLHttpRequest = XMLHttpRequest;
19160    window.XMLHttpRequest = function()
19161    {
19162        return new XMLHttpRequestWrapper();
19163    };
19164}
19165
19166//************************************************************************************************
19167
19168FBL.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
19206FBL.ns(function() { with (FBL) {
19207// ************************************************************************************************
19208
19209// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19210
19211var reIgnore = /about:|javascript:|resource:|chrome:|jar:/;
19212var layoutInterval = 300;
19213var indentWidth = 18;
19214
19215var cacheSession = null;
19216var contexts = new Array();
19217var panelName = "net";
19218var maxQueueRequests = 500;
19219//var panelBar1 = $("fbPanelBar1"); // chrome not available at startup
19220var activeRequests = [];
19221
19222// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19223
19224var 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
19243var 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
19256var textFileCategories =
19257{
19258    "txt": 1,
19259    "html": 1,
19260    "xhr": 1,
19261    "css": 1,
19262    "js": 1
19263};
19264
19265var binaryFileCategories =
19266{
19267    "bin": 1,
19268    "flash": 1
19269};
19270
19271var 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
19290var 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 */
19304Firebug.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 */
19358Firebug.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
19776var 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 */
19784Firebug.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
19907var 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 */
19915Firebug.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
20176var NetInfoPostData = Firebug.NetMonitor.NetInfoPostData;
20177
20178// ************************************************************************************************
20179
20180
20181// TODO: xxxpedro net i18n
20182var $STRP = function(a){return a;};
20183
20184Firebug.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
20271var NetLimit = Firebug.NetMonitor.NetLimit;
20272
20273// ************************************************************************************************
20274
20275Firebug.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
20302Firebug.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
20502var Utils = Firebug.NetMonitor.Utils;
20503
20504// ************************************************************************************************
20505
20506//Firebug.registerRep(Firebug.NetMonitor.NetRequestTable);
20507//Firebug.registerActivableModule(Firebug.NetMonitor);
20508//Firebug.registerPanel(NetPanel);
20509
20510Firebug.registerModule(Firebug.NetMonitor);
20511//Firebug.registerRep(Firebug.NetMonitor.BreakpointRep);
20512
20513// ************************************************************************************************
20514}});
20515
20516
20517/* See license.txt for terms of usage */
20518
20519FBL.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.
20528var 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 */
20542Firebug.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/*
20731function 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/*
20775Firebug.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/*
20880function 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
20942function 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
20956function 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
20971function 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
21003Firebug.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
21162function updateTime(spy)
21163{
21164    var timeBox = spy.logRow.getElementsByClassName("spyTime").item(0);
21165    if (spy.responseTime)
21166        timeBox.textContent = " " + formatTime(spy.responseTime);
21167}
21168
21169function 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
21190var 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
21227function 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
21245function 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
21275Firebug.registerModule(Firebug.Spy);
21276//Firebug.registerRep(Firebug.Spy.XHR);
21277
21278// ************************************************************************************************
21279}});
21280
21281
21282/* See license.txt for terms of usage */
21283
21284FBL.ns(function() { with (FBL) {
21285
21286// ************************************************************************************************
21287
21288// List of JSON content types.
21289var 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
21308Firebug.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
21405Firebug.registerModule(Firebug.JSONViewerModel);
21406
21407// ************************************************************************************************
21408}});
21409
21410
21411/* See license.txt for terms of usage */
21412
21413FBL.ns(function() { with (FBL) {
21414
21415// ************************************************************************************************
21416// Constants
21417
21418// List of XML related content types.
21419var 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 */
21439Firebug.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 */
21582Firebug.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
21613Firebug.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)
21621FBL.ns(function() { with (FBL) {
21622// ************************************************************************************************
21623
21624// ************************************************************************************************
21625// Constants
21626
21627/*
21628const Cc = Components.classes;
21629const Ci = Components.interfaces;
21630const nsIPrefBranch2 = Ci.nsIPrefBranch2;
21631const PrefService = Cc["@mozilla.org/preferences-service;1"];
21632const prefs = PrefService.getService(nsIPrefBranch2);
21633/**/
21634/*
21635
21636// new offline message handler
21637o = {x:1,y:2};
21638
21639r = Firebug.getRep(o);
21640
21641r.tag.tag.compile();
21642
21643outputs = [];
21644html = r.tag.renderHTML({object:o}, outputs);
21645
21646
21647// finish rendering the template (the DOM part)
21648target = $("build");
21649target.innerHTML = html;
21650root = target.firstChild;
21651
21652domArgs = [root, r.tag.context, 0];
21653domArgs.push.apply(domArgs, r.tag.domArgs);
21654domArgs.push.apply(domArgs, outputs);
21655r.tag.tag.renderDOM.apply(self ? self : r.tag.subject, domArgs);
21656
21657
21658 */
21659var consoleQueue = [];
21660var lastHighlightedObject;
21661var FirebugContext = Env.browser;
21662
21663// ************************************************************************************************
21664
21665var maxQueueRequests = 500;
21666
21667// ************************************************************************************************
21668
21669Firebug.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);
21788var ActivableConsole = extend(Firebug.ConsoleBase,
21789{
21790    isAlwaysEnabled: function()
21791    {
21792        return true;
21793    }
21794});
21795
21796Firebug.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
22031Firebug.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
22044Firebug.ConsolePanel = function () {} // XXjjb attach Firebug so this panel can be extended.
22045
22046//TODO: xxxpedro
22047//Firebug.ConsolePanel.prototype = extend(Firebug.ActivablePanel,
22048Firebug.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
22750function 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
22798var appendObject = Firebug.ConsolePanel.prototype.appendObject;
22799var appendFormatted = Firebug.ConsolePanel.prototype.appendFormatted;
22800var appendOpenGroup = Firebug.ConsolePanel.prototype.appendOpenGroup;
22801var appendCloseGroup = Firebug.ConsolePanel.prototype.appendCloseGroup;
22802
22803// ************************************************************************************************
22804
22805//Firebug.registerActivableModule(Firebug.Console);
22806Firebug.registerModule(Firebug.Console);
22807Firebug.registerPanel(Firebug.ConsolePanel);
22808
22809// ************************************************************************************************
22810}});
22811
22812
22813/* See license.txt for terms of usage */
22814
22815FBL.ns(function() { with (FBL) {
22816
22817// ************************************************************************************************
22818// Constants
22819
22820//const Cc = Components.classes;
22821//const Ci = Components.interfaces;
22822
22823var frameCounters = {};
22824var traceRecursion = 0;
22825
22826Firebug.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
23053var total_handlers = 0;
23054var 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
23645FBL.registerConsole = function()
23646{
23647    var win = Env.browser.window;
23648    Firebug.Console.injector.install(win);
23649};
23650
23651registerConsole();
23652
23653}});
23654
23655
23656/* See license.txt for terms of usage */
23657
23658FBL.ns(function() { with (FBL) {
23659// ************************************************************************************************
23660
23661
23662// ************************************************************************************************
23663// Globals
23664
23665var commandPrefix = ">>>";
23666var reOpenBracket = /[\[\(\{]/;
23667var reCloseBracket = /[\]\)\}]/;
23668
23669// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23670
23671var commandHistory = [];
23672var commandPointer = -1;
23673
23674// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23675
23676var isAutoCompleting = null;
23677var autoCompletePrefix = null;
23678var autoCompleteExpr = null;
23679var autoCompleteBuffer = null;
23680var autoCompletePosition = null;
23681
23682// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23683
23684var fbCommandLine = null;
23685var fbLargeCommandLine = null;
23686var fbLargeCommandButtons = null;
23687
23688// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23689
23690var _completion =
23691{
23692    window:
23693    [
23694        "console"
23695    ],
23696
23697    document:
23698    [
23699        "getElementById",
23700        "getElementsByTagName"
23701    ]
23702};
23703
23704// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23705
23706var _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
23718Firebug.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
24117Firebug.registerModule(Firebug.CommandLine);
24118
24119
24120// ************************************************************************************************
24121//
24122
24123function 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
24154var 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
24193var 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
24213FBL.ns(function() { with (FBL) {
24214// ************************************************************************************************
24215
24216// ************************************************************************************************
24217// Globals
24218
24219var ElementCache = Firebug.Lite.Cache.Element;
24220var cacheID = Firebug.Lite.Cache.ID;
24221
24222var 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
24230if (Firebug.ignoreFirebugElements)
24231    // ignores also the cache property injected by firebug
24232    ignoreHTMLProps[cacheID] = 1;
24233
24234
24235// ************************************************************************************************
24236// HTML Module
24237
24238Firebug.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
24508Firebug.registerModule(Firebug.HTML);
24509
24510// ************************************************************************************************
24511// HTML Panel
24512
24513function HTMLPanel(){};
24514
24515HTMLPanel.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
24618Firebug.registerPanel(HTMLPanel);
24619
24620// ************************************************************************************************
24621
24622var 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
24633var selectedElement = null;
24634var fbPanel1 = null;
24635
24636// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
24637var selectedSidePanelTS, selectedSidePanelTimer;
24638
24639var 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// ************************************************************************************************
24696Firebug.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
24752function 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
24768var hoverElement = null;
24769var hoverElementTS = 0;
24770
24771Firebug.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
24839Firebug.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
25017Hack:
25018Firebug.chrome.currentPanel = Firebug.chrome.selectedPanel;
25019Firebug.showInfoTips = true;
25020Firebug.InfoTip.initializeBrowser(Firebug.chrome);
25021
25022/**/
25023
25024FBL.ns(function() { with (FBL) {
25025
25026// ************************************************************************************************
25027// Constants
25028
25029var maxWidth = 100, maxHeight = 80;
25030var infoTipMargin = 10;
25031var infoTipWindowPadding = 25;
25032
25033// ************************************************************************************************
25034
25035Firebug.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
25377Firebug.registerModule(Firebug.InfoTip);
25378
25379// ************************************************************************************************
25380
25381}});
25382
25383
25384/* See license.txt for terms of usage */
25385
25386FBL.ns(function() { with (FBL) {
25387// ************************************************************************************************
25388
25389var 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 */
25401CssParser = (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
25692FBL.CssParser = CssParser;
25693
25694// ************************************************************************************************
25695}});
25696
25697/* See license.txt for terms of usage */
25698
25699FBL.ns(function() { with (FBL) {
25700
25701// ************************************************************************************************
25702// StyleSheet Parser
25703
25704var CssAnalyzer = {};
25705
25706// ************************************************************************************************
25707// Locals
25708
25709var CSSRuleMap = {};
25710var ElementCSSRulesMap = {};
25711
25712var internalStyleSheetIndex = -1;
25713
25714var reSelectorTag = /(^|\s)(?:\w+)/g;
25715var reSelectorClass = /\.[\w\d_-]+/g;
25716var reSelectorId = /#[\w\d_-]+/g;
25717
25718var globalCSSRuleIndex;
25719
25720var processAllStyleSheetsTimeout = null;
25721
25722var externalStyleSheetURLs = [];
25723
25724var ElementCache = Firebug.Lite.Cache.Element;
25725var StyleSheetCache = Firebug.Lite.Cache.StyleSheet;
25726
25727//************************************************************************************************
25728// CSS Analyzer templates
25729
25730CssAnalyzer.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
25742CssAnalyzer.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 */
25760CssAnalyzer.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
25773CssAnalyzer.getRuleData = function(ruleId)
25774{
25775    return CSSRuleMap[ruleId];
25776};
25777
25778// TODO: do we need this?
25779CssAnalyzer.getRuleLine = function()
25780{
25781};
25782
25783CssAnalyzer.hasExternalStyleSheet = function()
25784{
25785    return externalStyleSheetURLs.length > 0;
25786};
25787
25788CssAnalyzer.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
25817var 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
25924var 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
26056var 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
26152var 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
26211var 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
26229var 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
26240var 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
26257var 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
26316FBL.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 */
26333this.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
26350this.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
26375this.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
26393this.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
26406FBL.ns(function() { with (FBL) {
26407
26408// ************************************************************************************************
26409// ************************************************************************************************
26410// ************************************************************************************************
26411// ************************************************************************************************
26412// ************************************************************************************************
26413
26414var toCamelCase = function toCamelCase(s)
26415{
26416    return s.replace(reSelectorCase, toCamelCaseReplaceFn);
26417};
26418
26419var toSelectorCase = function toSelectorCase(s)
26420{
26421  return s.replace(reCamelCase, "-$1").toLowerCase();
26422
26423};
26424
26425var reCamelCase = /([A-Z])/g;
26426var reSelectorCase = /\-(.)/g;
26427var toCamelCaseReplaceFn = function toCamelCaseReplaceFn(m,g)
26428{
26429    return g.toUpperCase();
26430};
26431
26432// ************************************************************************************************
26433
26434var ElementCache = Firebug.Lite.Cache.Element;
26435var 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// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26464Firebug.SourceBoxPanel = Firebug.Panel;
26465
26466var reSelectorTag = /(^|\s)(?:\w+)/g;
26467
26468var domUtils = null;
26469
26470var textContent = isIE ? "innerText" : "textContent";
26471// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26472// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26473// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26474// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26475
26476var 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
26487var 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
26499var CSSRuleTag =
26500    TAG("$rule.tag", {rule: "$rule"});
26501
26502var 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
26510var 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
26530var reSplitCSS =  /(url\("?[^"\)]+?"?\))|(rgb\(.*?\))|(#[\dA-Fa-f]+)|(-?\d+(\.\d+)?(%|[a-z]{1,2})?)|([^,\s]+)|"(.*?)"/;
26531
26532var reURL = /url\("?([^"\)]+)?"?\)/;
26533
26534var reRepeat = /no-repeat|repeat-x|repeat-y|repeat/;
26535
26536//const sothinkInstalled = !!$("swfcatcherKey_sidebar");
26537var sothinkInstalled = false;
26538var 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
26641var styleGroupTitles =
26642{
26643    text: "Text",
26644    background: "Background",
26645    box: "Box Model",
26646    layout: "Layout",
26647    other: "Other"
26648};
26649
26650Firebug.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
26835Firebug.CSSStyleSheetPanel = function() {};
26836
26837Firebug.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
27822function CSSElementPanel() {}
27823
27824CSSElementPanel.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
28252function 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
28267function CSSComputedElementPanel() {}
28268
28269CSSComputedElementPanel.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
28360function CSSEditor(doc)
28361{
28362    this.initializeInline(doc);
28363}
28364
28365CSSEditor.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
28475function CSSRuleEditor(doc)
28476{
28477    this.initializeInline(doc);
28478    this.completeAsYouType = false;
28479}
28480CSSRuleEditor.uniquifier = 0;
28481CSSRuleEditor.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
28586function StyleSheetEditor(doc)
28587{
28588    this.box = this.tag.replace({}, doc, this);
28589    this.input = this.box.firstChild;
28590}
28591
28592StyleSheetEditor.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
28668var 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
28673var rgbToHexReplacer = function(_, r, g, b) {
28674    return '#' + ((1 << 24) + (r << 16) + (g << 8) + (b << 0)).toString(16).substr(-6).toUpperCase();
28675};
28676
28677var 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
28683var stripUnitsReplacer = function(_, skip, remove, whitespace) {
28684    return skip || ('0' + whitespace);
28685};
28686
28687function 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
28696function parseURLValue(value)
28697{
28698    var m = reURL.exec(value);
28699    return m ? m[1] : "";
28700}
28701
28702function parseRepeatValue(value)
28703{
28704    var m = reRepeat.exec(value);
28705    return m ? m[0] : "";
28706}
28707
28708function 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
28739function 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
28748function sortProperties(props)
28749{
28750    props.sort(function(a, b)
28751    {
28752        return a.name > b.name ? 1 : -1;
28753    });
28754}
28755
28756function 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
28773function 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
28781function getStyleSheetOwnerNode(sheet) {
28782    for (; sheet && !sheet.ownerNode; sheet = sheet.parentStyleSheet);
28783
28784    return sheet.ownerNode;
28785}
28786
28787function scrollSelectionIntoView(panel)
28788{
28789    var selCon = getSelectionController(panel);
28790    selCon.scrollSelectionIntoView(
28791            nsISelectionController.SELECTION_NORMAL,
28792            nsISelectionController.SELECTION_FOCUS_REGION, true);
28793}
28794
28795function 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
28805Firebug.registerModule(Firebug.CSSModule);
28806Firebug.registerPanel(Firebug.CSSStyleSheetPanel);
28807Firebug.registerPanel(CSSElementPanel);
28808Firebug.registerPanel(CSSComputedElementPanel);
28809
28810// ************************************************************************************************
28811
28812}});
28813
28814
28815/* See license.txt for terms of usage */
28816
28817FBL.ns(function() { with (FBL) {
28818// ************************************************************************************************
28819
28820// ************************************************************************************************
28821// Script Module
28822
28823Firebug.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
28836Firebug.registerModule(Firebug.Script);
28837
28838
28839// ************************************************************************************************
28840// Script Panel
28841
28842function ScriptPanel(){};
28843
28844ScriptPanel.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
29028Firebug.registerPanel(ScriptPanel);
29029
29030
29031// ************************************************************************************************
29032
29033
29034var 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
29104var 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
29119FBL.ns(function() { with (FBL) {
29120// ************************************************************************************************
29121
29122// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
29123
29124var ElementCache = Firebug.Lite.Cache.Element;
29125
29126var insertSliceSize = 18;
29127var insertInterval = 40;
29128
29129var 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
29149if (Firebug.ignoreFirebugElements)
29150    ignoreVars[Firebug.Lite.Cache.ID] = 1;
29151
29152// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
29153
29154var memberPanelRep =
29155    isIE6 ?
29156    {"class": "memberLabel $member.type\\Label", href: "javacript:void(0)"}
29157    :
29158    {"class": "memberLabel $member.type\\Label"};
29159
29160var 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
29173var 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
29183var SizerRow =
29184    TR({role : 'presentation'},
29185        TD({width: "30%"}),
29186        TD({width: "70%"})
29187    );
29188
29189var domTableClass = isIElt8 ? "domTable domTableIE" : "domTable";
29190var 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
29363Firebug.DOMBasePanel = function() {};
29364
29365Firebug.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
30174var 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
30201var DOMMainPanel = Firebug.DOMPanel = function () {};
30202
30203Firebug.DOMPanel.DirTable = DirTablePlate;
30204
30205DOMMainPanel.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
30355Firebug.registerPanel(DOMMainPanel);
30356
30357
30358// ************************************************************************************************
30359
30360
30361
30362// ************************************************************************************************
30363// Local Helpers
30364
30365var 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
30498function 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
30538function 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
30567FBL.ErrorCopy = function(message)
30568{
30569    this.message = message;
30570};
30571
30572var 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
30598var 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
30606var getRowName = function getRowName(row)
30607{
30608    var node = row.firstChild;
30609    return node.textContent ? node.textContent : node.innerText;
30610};
30611
30612var getRowValue = function getRowValue(row)
30613{
30614    return row.lastChild.firstChild.repObject;
30615};
30616
30617var getRowOwnerObject = function getRowOwnerObject(row)
30618{
30619    var parentRow = getParentRow(row);
30620    if (parentRow)
30621        return getRowValue(parentRow);
30622};
30623
30624var 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
30634var 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
30665Firebug.DOM = extend(Firebug.Module,
30666{
30667    getPanel: function()
30668    {
30669        return Firebug.chrome ? Firebug.chrome.getPanel("DOM") : null;
30670    }
30671});
30672
30673Firebug.registerModule(Firebug.DOM);
30674
30675
30676// ************************************************************************************************
30677// DOM Panel
30678
30679var lastHighlightedObject;
30680
30681function DOMSidePanel(){};
30682
30683DOMSidePanel.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
30804Firebug.registerPanel(DOMSidePanel);
30805
30806
30807// ************************************************************************************************
30808}});
30809
30810/* See license.txt for terms of usage */
30811
30812FBL.FBTrace = {};
30813
30814(function() {
30815// ************************************************************************************************
30816
30817var 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
30826this.module = null;
30827
30828this.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
30840this.sysout = function()
30841{
30842    return this.logFormatted(arguments, "");
30843};
30844
30845this.dumpProperties = function(title, object)
30846{
30847    return this.logFormatted("dumpProperties() not supported.", "warning");
30848};
30849
30850this.dumpStack = function()
30851{
30852    return this.logFormatted("dumpStack() not supported.", "warning");
30853};
30854
30855this.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
30866this.getPanel = function()
30867{
30868    return this.module ? this.module.getPanel() : null;
30869};
30870
30871//*************************************************************************************************
30872
30873this.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
30897this.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
30911this.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
30923this.appendRow = function(row)
30924{
30925    var container = this.getPanel().panelNode;
30926    container.appendChild(row);
30927};
30928
30929this.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
30939function appendText(object, html)
30940{
30941    html.push(escapeHTML(objectToString(object)));
30942};
30943
30944function 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
30955var HTMLtoEntity =
30956{
30957    "<": "&lt;",
30958    ">": "&gt;",
30959    "&": "&amp;",
30960    "'": "&#39;",
30961    '"': "&quot;"
30962};
30963
30964function replaceChars(ch)
30965{
30966    return HTMLtoEntity[ch];
30967};
30968
30969function escapeHTML(value)
30970{
30971    return (value+"").replace(/[<>&"']/g, replaceChars);
30972};
30973
30974//*************************************************************************************************
30975
30976function 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
30993FBL.ns(function() { with (FBL) {
30994// ************************************************************************************************
30995
30996// If application isn't in trace mode, the FBTrace panel won't be loaded
30997if (!Env.Options.enableTrace) return;
30998
30999// ************************************************************************************************
31000// FBTrace Module
31001
31002Firebug.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
31015Firebug.registerModule(Firebug.Trace);
31016
31017
31018// ************************************************************************************************
31019// FBTrace Panel
31020
31021function TracePanel(){};
31022
31023TracePanel.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
31059Firebug.registerPanel(TracePanel);
31060
31061// ************************************************************************************************
31062}});
31063
31064/* See license.txt for terms of usage */
31065
31066FBL.ns(function() { with (FBL) {
31067// ************************************************************************************************
31068
31069// ************************************************************************************************
31070// Globals
31071
31072var modules = [];
31073var panelTypes = [];
31074var panelTypeMap = {};
31075
31076var parentPanelMap = {};
31077
31078
31079var registerModule = Firebug.registerModule;
31080var registerPanel = Firebug.registerPanel;
31081
31082// ************************************************************************************************
31083append(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
31160FBL.ns(function() { with (FBL) {
31161// ************************************************************************************************
31162
31163FirebugChrome.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// ************************************************************************************************
31173FBL.initialize();
31174// ************************************************************************************************
31175
31176})();